FileSystemWatcher事件触发时WinForm通知窗体无法正常显示求助
解决WinForms通知窗体在FileSystemWatcher事件中仅显示任务栏的问题
这个问题我之前做类似下载管理器时也碰到过,核心原因是线程上下文不匹配!
问题根源
FileSystemWatcher的事件处理程序是在后台工作线程触发的,而WinForms的所有控件(包括窗体)都要求在UI主线程上进行创建、显示等操作。你在_fileWatcher_Created里直接实例化并显示Notification_form,相当于在非UI线程操作窗体,系统只能把窗体挂在任务栏,无法正常渲染界面;而按钮点击事件本身就在UI线程执行,所以能正常显示通知。
两种可行的解决方案
方案1:给MyFileWatcher传入UI控件引用,手动切换线程
修改MyFileWatcher的构造函数,让它接收一个UI控件(比如你的主窗体)的引用,用来获取UI线程的同步上下文:
public class MyFileWatcher { private readonly FileSystemWatcher _fileSystemWatcher; private readonly Control _uiAnchor; // 用来锚定UI线程的控件 // 构造函数接收UI控件,比如主窗体实例 public MyFileWatcher(Control uiAnchor) { _uiAnchor = uiAnchor; _fileSystemWatcher = new FileSystemWatcher(resources.sourcePath); _fileSystemWatcher.Created += (sender, e) => _fileWatcher_Created(sender, e); _fileSystemWatcher.EnableRaisingEvents = true; } private void _fileWatcher_Created(object sender, FileSystemEventArgs e) { // 先执行文件移动逻辑 CustomServiceContainer.GetService<IFileManager>().MoveFiles(resources.sourcePath, resources.targetPath); // 检查是否需要切换到UI线程 if (_uiAnchor.InvokeRequired) { // 使用Invoke将通知操作投递到UI线程 _uiAnchor.Invoke(new Action(() => ShowNotificationAlert(eventType))); } else { ShowNotificationAlert(eventType); } } // 抽离出通知显示的逻辑,让代码更清晰 private void ShowNotificationAlert(string eventType) { Notification_form ntf = new Notification_form(); ntf.showAlert(eventType); } }
然后在你的主窗体初始化MyFileWatcher时,把自身传进去:
// 假设在主窗体的构造函数或者Load事件里初始化 var fileWatcher = new MyFileWatcher(this);
方案2:在Notification_form内部处理线程切换
如果你不想修改MyFileWatcher的结构,可以直接在通知窗体的showAlert方法里做线程检查,自动切换到UI线程:
public void showAlert(string msg) { // 先判断当前线程是否是UI线程,不是的话就切换 if (this.InvokeRequired) { this.Invoke(new Action<string>(showAlert), msg); return; } // 下面是你原来的showAlert逻辑,不需要修改 this.Opacity = 0.0; this.StartPosition = FormStartPosition.Manual; string fName; for (int i = 1; i < 10; i++) { fName = "alert" + i.ToString(); Notification_form frm = (Notification_form)Application.OpenForms[fName]; if (frm == null) { this.Name = fName; this.x = Screen.PrimaryScreen.WorkingArea.Width - this.Width + 15; this.y = Screen.PrimaryScreen.WorkingArea.Height - this.Height * i; this.Location = new Point(this.x, this.y); break; } } this.x = Screen.PrimaryScreen.WorkingArea.Width - base.Width - 5; this.lblTitle.Text = msg; this.Show(); this.action = enmAction.start; this.timer1.Interval = 1; this.timer1.Start(); }
这样不管在哪个线程调用showAlert,都会自动切换到UI线程执行,完美解决问题。
额外的优化建议
- 设置FileSystemWatcher的SynchronizingObject:你也可以直接把
_fileSystemWatcher.SynchronizingObject设置为UI控件,这样整个Created事件都会在UI线程触发,不需要手动Invoke:
这种方式和方案1原理类似,看你个人代码习惯选择。_fileSystemWatcher.SynchronizingObject = _uiAnchor; - 处理文件未下载完成的情况:FileSystemWatcher的
Created事件可能在文件还没完全写入磁盘时就触发,建议在MoveFiles方法里添加文件可访问性检查,比如用try-catch包裹文件操作,或者等待文件释放锁后再移动。
内容的提问来源于stack exchange,提问作者Working Pickle
相关产品推荐
相关产品推荐

