WPF中如何使用ISynchronizeInvoke接口?

How to use the ISynchronizeInvoke interface in WPF?

我有这样的组件,无法更改它:

public sealed class UFScannerManager
{
    public UFScannerManager(ISynchronizeInvoke synInvoke);
    public ScannerList Scanners { get; }
    public event UFS_SCANNER_PROC ScannerEvent;

    public UFS_STATUS Init();
    public UFS_STATUS Uninit();
    public UFS_STATUS Update();

    [DefaultMember("Item")]
    public sealed class ScannerList
    {
        public ScannerList(UFScannerManager Owner);

        public UFScanner this[int Index] { get; }
        public UFScanner this[string ScannerID] { get; }
        public UFScanner this[IntPtr ScannerHandle] { get; }

        public int Count { get; }
    }
}

我想像这样创建一个组件的实例:UFScannerManager(this),但在 WPF 中我不能调用 pass this 作为参数。这里this表示当前window表单对象,构造函数需要一个ISynchronizeInvoke sysInvoke参数。因此当通过 this 时,扫描器可以在 Windows 表单应用程序中正确初始化。无需担心 ISynchronizeInvoke 接口。

UFS_STATUS ufs_res;
UFScannerManager ScannerManager;
int nScannerNumber;
ScannerManager = new UFScannerManager(this);
ufs_res = ScannerManager.Init();
nScannerNumber = ScannerManager.Scanners.Count;

但是,此代码在 WPF 中不起作用。问题是这一行。它不喜欢的一点是 this.

ScannerManager = new UFScannerManager(this);

当我尝试构建时,出现错误:

Argument 1: cannot convert from 'win_myapp' to 'System.ComponentModel.ISynchronizeInvoke'

WPF 不像 System.Windows.Forms.Form class 那样提供 ISynchronizeInvoke 实现。所以你需要创建一个。

幸运的是,WPF 的 Dispatcher class 提供了该实现所需的所有方法。您只需为 DispatcherDispatcherOperation.

创建 wrappers/adapters

我可以告诉你如何做到这一点。请注意,此代码不应在生产环境中使用 'as is',因为它已简化且缺少异常处理。

class DispatcherSynchronizeInvoke : ISynchronizeInvoke
{
    private readonly Dispatcher dispatcher;

    public DispatcherSynchronizeInvoke(Dispatcher dispatcher)
    {
        this.dispatcher = dispatcher;
    }

    public IAsyncResult BeginInvoke(Delegate method, object[] args)
    {
        // Obtaining a DispatcherOperation instance
        // and wrapping it with our proxy class
        return new DispatcherAsyncResult(
            this.dispatcher.BeginInvoke(method, DispatcherPriority.Normal, args));
    }

    public object EndInvoke(IAsyncResult result)
    {
        DispatcherAsyncResult dispatcherResult = result as DispatcherAsyncResult;
        dispatcherResult.Operation.Wait();
        return dispatcherResult.Operation.Result;
    }

    public object Invoke(Delegate method, object[] args)
    {
        return dispatcher.Invoke(method, DispatcherPriority.Normal, args);
    }

    public bool InvokeRequired => !this.dispatcher.CheckAccess();

    // We also could use the DispatcherOperation.Task directly
    private class DispatcherAsyncResult : IAsyncResult
    {      
        private readonly IAsyncResult result;

        public DispatcherAsyncResult(DispatcherOperation operation)
        {
            this.Operation = operation;
            this.result = operation.Task;
        }

        public DispatcherOperation Operation { get; }

        public bool IsCompleted => this.result.IsCompleted;
        public WaitHandle AsyncWaitHandle => this.result.AsyncWaitHandle;
        public object AsyncState => this.result.AsyncState;
        public bool CompletedSynchronously => this.result.CompletedSynchronously;
    }
}

使用此自定义 ISynchronizeInvoke 实现,您可以实例化您的 classes:

// Assuming you're calling this inside of a DispatcherObject, e.g. a Window
new UFScannerManager(new DispatcherSynchronizeInvoke(this.Dispatcher));

以@dymanoid 的示例为基础。最好把它做成扩展方法。

public static class WPFExtensions
{
    public static System.ComponentModel.ISynchronizeInvoke ISynchronizeInvoke
        (this System.Windows.Threading.DispatcherObject dispatcher)
    {
        return new DispatcherSynchronizeInvoke(dispatcher.Dispatcher);
    }
}

class DispatcherSynchronizeInvoke : System.ComponentModel.ISynchronizeInvoke
{
    // see dymanoid's answer, you do need one additional line in the internal
    // private class DispatcherAsyncResult : IAsyncResult
    // this >>
    //   WaitHandle IAsyncResult.AsyncWaitHandle => this.result.AsyncWaitHandle;
}

这与计时器 class 配合使用效果很好,这样您就不必编组 elasped 事件。从 window 你可以做:

var timer = new Timer(TimeSpan.FromSeconds(10).TotalMilliseconds) 
{ 
    SynchronizingObject = this.ISynchronizeInvoke() 
};
timer.Elapsed += timer_Elapsed;

然后计时器的已用事件在 window 的线程上。