-1

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));  
    }
}
George Alexandria
  • 2,841
  • 2
  • 16
  • 24

1 Answers1

1

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!

T.M.
  • 79
  • 6
  • he is giving me an error on [NotifyPropertyChangedInvocator], says that it could not be found. To use this property its not needed to have a variable that changes? – Jorge Rebelo Jul 30 '17 at 14:12
  • Can't add FileWatcher Class to windows.Resources on the XAML. :| its says that the constructor is not acessible. And with your properties it cant access. – Jorge Rebelo Jul 30 '17 at 15:23
  • Just remove the NotifyPropertyChangedInvocator (it is generated by Resharper), just forgot to remove it. Updated the answer. The thing with the FileWatcher class is, is that it only got a constructor with a parameter. However, you don't need to create from the resources - just create it from code behind. – T.M. Jul 30 '17 at 18:02
  • Changed my constructor to default constructor and now it runs just fine!!!! many thanks for your great help – Jorge Rebelo Jul 31 '17 at 12:06