目录

WPf-Dispatcher常见使用场景

Application.Current.Dispatcher(或者控件自带的 this.Dispatcher)的核心作用是:"任何非UI线程相碰UI使,都需要它"。除了 Task.Run ,还有几种常见场景也必须使用它,否则程序直接崩溃。

1.定时器(System.Timers.Timer)

.NET有好几种定时器

  • DispatcherTimer:运行在UI线程(安全,不需要 Dispatcher)
  • System.Timers.Timer: 运行在后台线程池(高效,但不安全)

如果为了性能使用了 System.Timers.Timer,在它的回调里想更新界面,就必须用Dispatcher。

// 初始化一个后台定时器
System.Timers.Timer _timer = new System.Timers.Timer(1000); // 1秒一次

public MainWindow()
{
    InitializeCompoent();
    _timer.Elapsed += Timer_Elapsed; 
    timer.start();
}

// 这个事件是在后台线程触发的
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
   // 错误:直接改 UI 会崩
   // txtTime.Text = DataTime.Now.ToString();
  
   // 正确
   Application.Current.Dispatcher.Invoke(()=>
   {
      txtTime.Text = DataTime.Now.ToString();
   });
}

2.硬件或网络事件(串口,Socket,USB)

工控,物联网上位机开发中的常见场景。

当 SerialPort(串口)收到数据,或者 Socket 收到 TCP 包是,底层驱动会通过一个后台线程触发 DataReceived 事件。

// 串口对象
SerialPort _serialPort = new SerialPort("COM1");

public void Init()
{
    _serialPort.DataReceived += SerialPort_DataReceived;
}

// 典型的后台线程事件
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
   string data = _serialPort.ReadExisting();
  
   // 必须用 Dispatcher 把数据送到界面显示
   Application.Current.Dispatcher.Invoke(() => 
   {
      txtLog.AppendText(data + "\n");
   });
}

3.文件监控(FileSystemWatcher)

如果你写一个软件监听某个文件夹,当有新文件生成时自动显示到列表里。FileSystemWatcher 的事件也是在后台线程触发的。

FileSystemWatcher watcher = new FileSystemWatcher(@"C"\Temp");

watcher.Created += (s, e) =>
{
    // 这里是后台线程
    Application.Current.Dispatcher.Invoke(() => 
    {
        // 更新 UI 列表
        myListBox.Items.Add($"发现新文件:{e.name}");
    });
};

4.老式的Thread写法

虽然现在都用 Task,但是在维护老代码时可能会看到 new Thread()。这也属于后台线程。

Thread thread = new Thread(() =>
{
     // 后台线程做旧式操作
     Thread.Sleep(1000);
    
     Application.Current.Dispatcher.Invoke(() => 
     {
          txtStatus.Text = "老线程干完了";
     });
});

thread.Start();

补充:**Application.Current.Dispatcher **和 this.Dispatcher 有啥区别?

你会发现有时候用 this.Dispatcher,有时候用 Application.Current.Dispatcher

  1. this.Dispatcher:

    • 含义:获取当前窗口(或控件)所关联的 UI 线程管家。
    • 优点:多窗口程序时更准确(假如你开启了多个独立的 UI 线程窗口,极为罕见)。
    • 限制:代码必须写在 WindowUserControl 的后台代码里才能用 this
  2. Application.Current.Dispatcher:

    • 含义:获取整个应用程序的主 UI 线程管家。
    • 优点全局通用。哪怕你在一个普通的 Person 类、ViewModel 类或者静态辅助类里,只要想更新主界面,都可以用它。
    • 缺点:如果主窗口关闭了,有时候访问它可能会有问题(但在 WPF 生命周期内通常没事)。

总结

只要你的代码此时此刻不在 UI 线程(无论是 Task.Run 还是定时器,还是硬件事件),而你又想更新界面,就必须请出 Dispatcher