You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:51:48