根据 FileSystemWatcher 更改通知更改 ObservableCollection

发布于 2024-08-24 04:32:34 字数 305 浏览 11 评论 0原文

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

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

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

提前致谢,
维尔

I'm trying to update my ObservableCollection as the FileSystemWatcher notifies changes. I know this is not possible because of cross thread operations.
So i would like to get the name of the file created/deleted/renamed when the event is fired and update it in the UI thread once the event is completed, as we do in BackgroundWorker. Can anyone tell me how to do this?

Also tell me where i should define and start this FileSystemWatcher. Currently i've defined it in the MainViewModel.

P.S.: I've seen similar questions in SO, but didn't get a clear picture

Thanks In Advance,
Veer

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

萝莉病 2024-08-31 04:32:34

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

其中 OnCreated, OnChanged, OnDeleted和 OnRenamed 是具有正常签名的正常事件处理程序方法,例如:

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

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

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

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

I would think the main view model is the right place to define the FileSystemWatcher. As for the threading issues, this is the easy way:

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

Each of the "Code to handle" will execute within the UI thread so it can update the ObservableCollection. Note that the FileSystemEventArgs "e" is available within this code.

If you prefer to use separate event handler methods you can call them from the above code or use this convenient shortcut:

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;

where OnCreated, OnChanged, OnDeleted, and OnRenamed are normal event handler methods with the normal signature, for example:

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

Personally I prefer the first way of doing it because I don't like creating four extra 1-line methods.

Note that your view model will need to know which Dispatcher to call back on. The easiest way to do this is to derive your view model from DispatcherObject, as assumed above. Another way is for the view model's constructor or the method that registers the FileSystemWatcher events to store a copy of Dispatcher.Current in a local field or local variable, then use that for the .BeginInvoke calls.

Also note that you can use exactly the same code in your view code-behind instead of in your view model if you prefer.

梦年海沫深 2024-08-31 04:32:34
public void SomeActionToBeInvokedOnTheMainThread()
{
    if (someControl.Dispatcher.CheckAccess())
    {
        // you can modify the control
    }
    else
    {
        someControl.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Normal,
            new Action(SomeActionToBeInvokedOnTheMainThread)
        );
    }
}
public void SomeActionToBeInvokedOnTheMainThread()
{
    if (someControl.Dispatcher.CheckAccess())
    {
        // you can modify the control
    }
    else
    {
        someControl.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Normal,
            new Action(SomeActionToBeInvokedOnTheMainThread)
        );
    }
}
镜花水月 2024-08-31 04:32:34

我使用了 Ray B. 的方法,但必须稍微修改一下,并认为我应该在这里发布更新,也许可以节省其他人的时间。

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

Cannot assign lambda expression to an implicitly-typed local variable

经过一些调整后,我想出了以下结果。请注意定义用于处理 Renamed 事件的附加 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;

I used Ray B.'s approach but had to modify things slightly and thought I'd post an update here to maybe save others some time.

My VS2010/.NET 4.0 WPF project was throwing the error:

Cannot assign lambda expression to an implicitly-typed local variable

After some tweaking I came up with the following. Note the additional var defined to handle the Renamed event:

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文