Изменение ObservableCollection в соответствии с уведомлением об изменении FileSystemWatcher - PullRequest
4 голосов
/ 03 марта 2010

Я пытаюсь обновить свою коллекцию ObservableCollection, когда FileSystemWatcher уведомляет об изменениях. Я знаю, что это невозможно из-за операций с несколькими потоками.
Поэтому я хотел бы получить имя файла, созданного / удаленного / переименованного при запуске события, и обновить его в потоке пользовательского интерфейса после завершения события, как мы это делаем в BackgroundWorker. Может кто-нибудь сказать мне, как это сделать?

Также скажите мне, где я должен определить и запустить этот FileSystemWatcher. В настоящее время я определил его в MainViewModel.

P.S .: Я видел подобные вопросы в SO, но не получил четкую картину

Заранее спасибо,
Veer

Ответы [ 3 ]

2 голосов
/ 03 марта 2010

Я думаю, что модель основного вида - это то место, где нужно определить FileSystemWatcher. Что касается вопросов с потоками, это простой способ:

_watcher = new FileSystemWatcher(path);
_watcher.Created += (obj, e) =>
  Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
  {
    // Code to handle Created event
  };
_watcher.Changed += (obj, e) =>
  Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
  {
    // Code to handle Changed event
  };
_watcher.Renamed += (obj, e) =>
  Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
  {
    // Code to handle Renamed event
  };
_watcher.Deleted += (obj, e) =>
  Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
  {
    // Code to handle Deleted event
  };
// ...
_watcher.EnableRaisingEvents = true;

Каждый из «Код для обработки» будет выполняться в потоке пользовательского интерфейса, чтобы он мог обновить ObservableCollection. Обратите внимание, что FileSystemEventArgs "e" доступен в этом коде.

Если вы предпочитаете использовать отдельные методы-обработчики событий, вы можете вызвать их из приведенного выше кода или воспользоваться удобным сочетанием клавиш:

var switchThread =
  (FileSystemEventHandler handler) =>
    (object obj, FileSystemEventArgs e) =>
      Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
       handler(obj, e))

_watcher = new FileSystemWatcher(path);
_watcher.Created += switchThread(OnCreated);
_watcher.Changed += switchThread(OnChanged);
_watcher.Deleted += switchThread(OnDeleted);
_watcher.Renamed += switchThread(OnRenamed);
_watcher.EnableRaisingEvents = true;

, где OnCreated, OnChanged, OnDeleted и OnRenamed - это обычные методы-обработчики событий с нормальной сигнатурой, например:

void OnChanged(object sender, FileSystemEventArgs e)
{
  // Code to handle Changed event
}

Лично я предпочитаю первый способ сделать это, потому что мне не нравится создавать четыре дополнительных однострочных метода.

Обратите внимание, что вашей модели представления нужно будет знать, какой Dispatcher необходимо перезвонить. Самый простой способ сделать это - извлечь модель представления из DispatcherObject, как предполагалось выше. Другой способ - конструктор модели представления или метод, который регистрирует события FileSystemWatcher, для сохранения копии Dispatcher.Current в локальном поле или локальной переменной, а затем использовать ее для вызовов .BeginInvoke.

Также обратите внимание, что вы можете использовать точно такой же код в коде представления, а не в модели представления, если хотите.

2 голосов
/ 03 марта 2010
public void SomeActionToBeInvokedOnTheMainThread()
{
    if (someControl.Dispatcher.CheckAccess())
    {
        // you can modify the control
    }
    else
    {
        someControl.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Normal,
            new Action(SomeActionToBeInvokedOnTheMainThread)
        );
    }
}
1 голос
/ 27 марта 2011

Я использовал подход Рэя Б., но мне пришлось немного изменить положение вещей, и я решил опубликовать здесь обновление, чтобы сэкономить время другим.

Мой проект WPF VS2010 / .NET 4.0 выдавал ошибку:

Cannot assign lambda expression to an implicitly-typed local variable

После некоторой настройки я придумал следующее. Обратите внимание на дополнительную переменную, определенную для обработки переименованного события:

var switchThreadForFsEvent = (Func<FileSystemEventHandler, FileSystemEventHandler>)(
        (FileSystemEventHandler handler) =>
                (object obj, FileSystemEventArgs e) =>
                    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
                        handler(obj, e))));

var switchThreadForFsRenameEvent = (Func<RenamedEventHandler, RenamedEventHandler>)(
            (RenamedEventHandler handler) =>
                (object obj, RenamedEventArgs e) =>
                    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
                        handler(obj, e))));

_fileSystemWatcher = new FileSystemWatcher(documentCollectionPath);
_fileSystemWatcher.Created += switchThreadForFsEvent(OnFileCreated);
_fileSystemWatcher.Deleted += switchThreadForFsEvent(OnFileDeleted);
_fileSystemWatcher.Renamed += switchThreadForFsRenameEvent(OnFileRenamed);
_fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName;
_fileSystemWatcher.IncludeSubdirectories = true;
_fileSystemWatcher.EnableRaisingEvents = true;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...