代码之家  ›  专栏  ›  技术社区  ›  Amsakanna

根据FileSystemWatcher更改通知更改ObservableCollection

  •  4
  • Amsakanna  · 技术社区  · 14 年前

    当文件系统监视程序通知更改时,我正在尝试更新我的ObservableCollection。我知道这是不可能的,因为跨线程操作。
    因此,我希望在激发事件时获取创建/删除/重命名的文件名,并在事件完成后在UI线程中更新它,就像在BackgroundWorker中那样。有人能告诉我怎么做吗?

    还要告诉我应该在哪里定义和启动这个文件系统监视程序。目前我已经在主视图模型中定义了它。

    附言:我在SO中看到过类似的问题,但没有得到一个清晰的画面。

    事先谢谢,
    转向器

    3 回复  |  直到 14 年前
        1
  •  2
  •   Ray Burns    14 年前

    我认为主视图模型是定义文件系统监视程序的正确位置。对于线程问题,这是一种简单的方法:

    _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;
    

    在哪里? OnCreated , OnChanged , OnDeleted ,和 OnRenamed 是具有常规签名的常规事件处理程序方法,例如:

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

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

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

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

        2
  •  2
  •   Darin Dimitrov    14 年前
    public void SomeActionToBeInvokedOnTheMainThread()
    {
        if (someControl.Dispatcher.CheckAccess())
        {
            // you can modify the control
        }
        else
        {
            someControl.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Normal,
                new Action(SomeActionToBeInvokedOnTheMainThread)
            );
        }
    }
    
        3
  •  1
  •   DCreeron    13 年前

    我使用了RayB的方法,但必须稍微修改一下,我想我会在这里发布一个更新,以节省其他人一些时间。

    我的vs2010/.NET 4.0 wpf项目正在引发错误:

    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;