I have a WPF program that Watchs a folder and I want to write on a listbox every event that occurs.
My problem is that i have a class FileWatcher and I can't pass the events (Created, Changed and deleted) to the listbox.
Can someone help please?
Thanks
MainWindow
public MainWindow()
{
InitializeComponent();
string Location = string.Empty; //variavel que vai definir a localização da monitorização
}
private void btMon_Click(object sender, RoutedEventArgs e)
{
FolderBrowserDialog browseFolder = new FolderBrowserDialog();
browseFolder.Description = "Selecione a pasta que deseja monitorizar. Todas as subpastas serão monitorizadas.";
if (browseFolder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Location = browseFolder.SelectedPath;
System.Windows.MessageBox.Show(Location);
}
} // Definição da localização que passa para o Filewatcher
private void btInMon_Click(object sender, RoutedEventArgs e)
{
monitoriza = new FileWatcher(Location);
} // Inicio da monitorização com a class FileWatcher
Class FileWatcher
public class FileWatcher
{
public MainWindow main;
private FileSystemWatcher _fileWatcher;
public FileWatcher(string Location)
{
_fileWatcher = new FileSystemWatcher(PathLocation(Location));
_fileWatcher.IncludeSubdirectories = true;
_fileWatcher.Created += new FileSystemEventHandler(_fileWatcher_Created);
_fileWatcher.EnableRaisingEvents = true;
}
private string PathLocation(string Location)
{
string value = String.Empty;
try
{
value = Location;
if (value != string.Empty)
{
return value;
}
}
catch (Exception ex)
{
//Implement logging on future version
}
return value;
}
void _fileWatcher_Created(object sender, FileSystemEventArgs e)
{
Logging.Log(String.Format("Evento criado por " + Environment.UserName + " Em " + DateTime.Now + " File Created: Patch:{0}, Name:{1}", e.FullPath, e.Name));
}
}
Since you're implementing a WPF application, make use of its data binding abilities. That said, you don't need reference to the MainWindow
class in FileWatcher
class.
What you need is a collection of string of all fired events in the FileWatcher
class, which is bound to your ListBox.
In your MainWindow.xaml, add the following line:
<ListBox Grid.Row="3" ItemsSource="{Binding FileWatcher.EventsFired}"/>
Change your MainWindow.xaml.cs to the following:
public partial class MainWindow : Window, INotifyPropertyChanged //Notify the UI when changes occur
{
private string _location;
private FileWatcher _fileWachter;
public FileWatcher FileWatcher
{
get
{
return _fileWachter;
}
set
{
// this makes your FileWatcher observable by the ui
_fileWachter = value;
OnPropertyChanged();
}
}
public string Location
{
get
{
return _location;
}
set
{
_location = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
Location = string.Empty; //variavel que vai definir a localização da monitorização
// DO NOT FORGET ABOUT THIS ONE
// REGISTER FOR NotifyPropertyChanged
DataContext = this;
}
private void btMon_Click(object sender, RoutedEventArgs e)
{
FolderBrowserDialog browseFolder = new FolderBrowserDialog();
browseFolder.Description = "Selecione a pasta que deseja monitorizar. Todas as subpastas serão monitorizadas.";
if (browseFolder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Location = browseFolder.SelectedPath;
System.Windows.MessageBox.Show(Location);
}
} // Definição da localização que passa para o Filewatcher
private void btInMon_Click(object sender, RoutedEventArgs e)
{
FileWatcher = new FileWatcher(Location);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// it is good practice to check the handler for null before calling it
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And last modify your FileWatcher
class:
public class FileWatcher : DispatcherObject, INotifyPropertyChanged //Dispatcher object is necessary to call the right dispatcher from the background thread
{
//collect all events from the filewatcher
public ObservableCollection<string> EventsFired
{
get
{
return _eventsFired;
}
set
{
_eventsFired = value;
OnPropertyChanged();
}
}
private FileSystemWatcher _fileWatcher;
private ObservableCollection<string> _eventsFired;
public FileWatcher(string Location)
{
//save all fired events
_eventsFired = new ObservableCollection<string>();
_fileWatcher = new FileSystemWatcher(PathLocation(Location));
_fileWatcher.IncludeSubdirectories = true;
_fileWatcher.Created += _fileWatcher_Created;
_fileWatcher.EnableRaisingEvents = true;
}
private string PathLocation(string Location)
{
string value = String.Empty;
try
{
value = Location;
if (value != string.Empty)
{
return value;
}
}
catch (Exception ex)
{
//Implement logging on future version
}
return value;
}
void _fileWatcher_Created(object sender, FileSystemEventArgs e)
{
//necessary to refresh collection from background thread
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
{
EventsFired.Add(String.Format("Evento criado por " + Environment.UserName + " Em " + DateTime.Now + " File Created: Patch:{0}, Name:{1}", e.FullPath, e.Name));
}));
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
In the MainWindow.xaml you're telling the ListBox to display all the events fired by the filewatcher (right now only your created files). These are saved in a ObservableCollection - a collection, that notifies the UI, when there are changed made to the collection, e.g. adding or removing items.
To invoke the Dispatcher on the OnCreated-event is necessary, because the event is being fired from a non-ui-thread. You can't modify a collection, which is bound the ui from a background thread. By using Dispatcher.Invoke, you're sending the newly added string to the main thread in order to refresh the collection.
Hope that helps!