Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/293.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 根据FileSystemWatcher更改通知更改ObservableCollection_C#_Wpf_Observablecollection_Filesystemwatcher_Multithreading - Fatal编程技术网

C# 根据FileSystemWatcher更改通知更改ObservableCollection

C# 根据FileSystemWatcher更改通知更改ObservableCollection,c#,wpf,observablecollection,filesystemwatcher,multithreading,C#,Wpf,Observablecollection,Filesystemwatcher,Multithreading,我正试图在FileSystemWatcher通知更改时更新ObservableCollection。我知道这是不可能的,因为跨线程操作 因此,我希望在触发事件时获取创建/删除/重命名的文件的名称,并在事件完成后在UI线程中更新它,就像我们在BackgroundWorker中所做的那样。谁能告诉我怎么做 还要告诉我应该在哪里定义和启动这个FileSystemWatcher。目前我已经在MainViewModel中定义了它 旁白:我在SO中也看到过类似的问题,但没有弄清楚 提前感谢, Veer我认为

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

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

旁白:我在SO中也看到过类似的问题,但没有弄清楚

提前感谢,

Veer

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


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

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

我的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;
var switchThreadForFsEvent=(Func)(
(FileSystemEventHandler处理程序)=>
(对象对象对象,文件系统目标)=>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send,新操作(()=>
handler(obj,e));
var switchThreadForFsRenameEvent=(Func)(
(重命名的EventHandler)=>
(对象对象对象,重命名为eventargs e)=>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send,新操作(()=>
handler(obj,e));
_fileSystemWatcher=新的fileSystemWatcher(documentCollectionPath);
_fileSystemWatcher.Created+=switchThreadForFsEvent(OnFileCreated);
_fileSystemWatcher.Deleted+=switchThreadForFsEvent(OnFileDeleted);
_fileSystemWatcher.rename+=switchThreadForFsRenameEvent(onFileName);
_fileSystemWatcher.NotifyFilter=NotifyFilters.DirectoryName | NotifyFilters.FileName;
_fileSystemWatcher.IncludeSubdirectories=true;
_fileSystemWatcher.EnableRaisingEvents=true;

对我的第二个问题有什么建议吗?这个代码是反模式的。与更简单的:
someControl.Dispatcher.Invoke(DispatcherPriority.Send,new Action(()=>{…修改控件的代码在这里…}))相比,它的效率和可读性要低得多。
。它效率低的原因是调用了三次CheckAccess:一次在调用中,一次在Dispatcher.Invoke中,一次在递归调用中。调用Dispatcher.Invoke直接绕过所有这些。
CheckAccess
唯一有意义的是,如果99%以上的调用来自同一个线程,那么当调用来自同一个线程时,性能是绝对关键的(在这种情况下,性能差异在1ns以下,因此您最好有一个很好的理由用一些奇怪的递归将代码弄乱!).太酷了。我想监视我的整个计算机,我已经为每个驱动器创建了单独的FileSystemWatcher对象。可以吗?或者有没有一种方法可以使用单个监视程序对象监视所有驱动器?我不认为有任何方法可以使用单个
FileSystemWatcher
监视所有驱动器,但是可以为每个驱动器创建一个
FileSystemWatcher
。您可以使用
FileSystemWatcher.IncludeSubdirectories
属性监视整个驱动器。我还没有测试过它的性能。另外请注意,如果您的FileSystemWatcher的内部缓冲区溢出,您会收到一个重置事件,此时您必须重新扫描树以查找更改的内容。目前,我已为每个驱动器创建了一个watcher。但正如您所说,“我必须检查缓冲区溢出……因为Dispatcher是一个非静态类Dispatcher。BeginInvoke()似乎不起作用。我是否应该创建主Dispatcher的对象并使用它?否。上面的代码假定位于现有对象内,例如应用程序、窗口或用户控件,所有这些对象都具有
Dispatcher
属性。对我来说,这听起来像是把它放在一个静态的方法中。在这种情况下,您需要访问适当的Dispatcher,它可以通过其Dispatcher属性从UI中的任何对象获得。这可能应该传递到静态方法中。
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;