Search code examples
c#file-watcher

C# FileWatch write event on Listbox


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));  
    }
}

Solution

  • 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!