如何在WPF中使用ISynchronizeInvoke接口?附不可修改组件代码
在WPF中使用
UFScannerManager的ISynchronizeInvoke接口指南 首先得明确:ISynchronizeInvoke的核心作用是让UFScannerManager触发的ScannerEvent回调能在WPF UI线程上执行——因为WPF的控件只能由创建它们的线程更新,而扫描仪的事件大概率是从后台线程发出来的,直接在事件里操作UI会抛出跨线程异常。
WPF本身没有直接提供ISynchronizeInvoke的实现,但我们可以用Dispatcher(WPF的线程调度核心)来包装一个适配类,完美对接这个接口。
步骤1:实现DispatcherSynchronizeInvoke适配类
这个类会把ISynchronizeInvoke的方法委托给WPF的Dispatcher处理,确保所有调用都回到UI线程:
using System.ComponentModel; using System.Windows.Threading; public class DispatcherSynchronizeInvoke : ISynchronizeInvoke { private readonly Dispatcher _dispatcher; public DispatcherSynchronizeInvoke(Dispatcher dispatcher) { _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); } // 判断当前线程是否是UI线程,不是则需要调度 public bool InvokeRequired => !_dispatcher.CheckAccess(); // 异步调度委托到UI线程 public IAsyncResult BeginInvoke(Delegate method, object[] args) { return _dispatcher.BeginInvoke(method, DispatcherPriority.Normal, args); } // 等待异步调度完成并获取结果 public object EndInvoke(IAsyncResult result) { if (result is DispatcherOperation operation) { operation.Wait(); return operation.Result; } return null; } // 同步调度委托到UI线程 public object Invoke(Delegate method, object[] args) { return _dispatcher.Invoke(method, DispatcherPriority.Normal, args); } }
步骤2:在WPF窗口中初始化UFScannerManager
在你的WPF窗口(比如MainWindow)里,用上面的适配类实例来初始化UFScannerManager,并订阅事件:
public partial class MainWindow : Window { private UFScannerManager _scannerManager; public MainWindow() { InitializeComponent(); // 传入当前窗口的Dispatcher,确保事件回调在UI线程执行 var syncInvoke = new DispatcherSynchronizeInvoke(this.Dispatcher); _scannerManager = new UFScannerManager(syncInvoke); // 订阅扫描仪事件,现在可以安全更新UI了 _scannerManager.ScannerEvent += OnScannerEvent; // 初始化管理器 var initStatus = _scannerManager.Init(); if (initStatus != UFS_STATUS.UFS_SUCCESS) { MessageBox.Show("扫描仪管理器初始化失败,请检查设备连接"); } } private void OnScannerEvent(UFS_SCANNER_EVENT_TYPE eventType, UFScanner scanner, IntPtr data) { // 这里直接操作UI控件完全安全,因为已经在UI线程了 ScannerStatusLabel.Content = $"事件类型:{eventType} | 扫描仪ID:{scanner.ScannerID}"; } // 窗口关闭时释放资源 protected override void OnClosed(EventArgs e) { base.OnClosed(e); _scannerManager?.Uninit(); } }
额外注意事项
- MVVM场景适配:如果用MVVM模式,ViewModel可以通过
Application.Current.Dispatcher获取全局Dispatcher,或者在ViewModel构造时从View注入Dispatcher,避免直接依赖UI控件。 - 资源释放:一定要在窗口关闭或应用退出时调用
Uninit(),避免资源泄漏。 - 手动线程判断:如果需要在其他地方手动判断是否需要跨线程调用,可以使用
syncInvoke.InvokeRequired属性,比如:if (syncInvoke.InvokeRequired) { syncInvoke.Invoke(new Action(() => { /* UI操作 */ }), null); } else { /* 直接执行UI操作 */ }
这样处理后,UFScannerManager的所有事件回调都会自动在WPF的UI线程执行,再也不会出现跨线程更新UI的异常了。
内容的提问来源于stack exchange,提问作者Bivatan
相关产品推荐
相关产品推荐

