4

当 FileSystemWatcher 通知更改时,我正在尝试更新我的 ObservableCollection。我知道这是不可能的,因为跨线程操作。
所以我想在事件触发时获取创建/删除/重命名的文件的名称,并在事件完成后在 UI 线程中更新它,就像我们在 BackgroundWorker 中所做的那样。谁能告诉我该怎么做?

还告诉我应该在哪里定义和启动这个 FileSystemWatcher。目前我已经在 MainViewModel 中定义了它。

PS:我在SO中看到过类似的问题,但没有得到清晰的图片

提前致谢,
维尔

4

3 回答 3

2
public void SomeActionToBeInvokedOnTheMainThread()
{
    if (someControl.Dispatcher.CheckAccess())
    {
        // you can modify the control
    }
    else
    {
        someControl.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Normal,
            new Action(SomeActionToBeInvokedOnTheMainThread)
        );
    }
}
于 2010-03-03T07:29:38.543 回答
2

我认为主视图模型是定义 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;

每个“要处理的代码”都将在 UI 线程中执行,因此它可以更新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;

其中OnCreatedOnChangedOnDeletedOnRenamed是具有正常签名的正常事件处理程序方法,例如:

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

我个人更喜欢第一种方法,因为我不喜欢创建四个额外的单行方法。

请注意,您的视图模型将需要知道要回调哪个 Dispatcher。最简单的方法是从 DispatcherObject 派生您的视图模型,如上所述。另一种方法是视图模型的构造函数或注册 FileSystemWatcher 事件的方法将 Dispatcher.Current 的副本存储在本地字段或本地变量中,然后将其用于 .BeginInvoke 调用。

另请注意,如果您愿意,可以在视图代码隐藏而不是视图模型中使用完全相同的代码。

于 2010-03-03T18:44:43.523 回答
1

我使用了 Ray B. 的方法,但不得不稍作修改,并认为我会在此处发布更新以节省其他人一些时间。

我的 VS2010/.NET 4.0 WPF 项目抛出错误:

Cannot assign lambda expression to an implicitly-typed local variable

经过一些调整,我想出了以下内容。请注意为处理重命名事件而定义的附加 var:

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;
于 2011-03-27T02:12:06.450 回答