WPF和WinForms Dispatcher应该统一吗?

时间:2016-02-02 15:05:36

标签: c# .net wpf multithreading winforms

我有一个应用程序混合了 WPF WinForm 控件,因为项目在WPF可用之前就开始了。 由于控制量的大小以及为构建更多应用程序的其他控件向后兼容的需要,无法将所有控件更改为基于WPF。

现在我偶尔会面临重入问题导致"收集被修改" 例外,因为当我们调用 WPF 调度程序消息时,它将导致当我们发送 WPF 消息时,其他待处理的WinForms消息将在 WinForms 侧输出意外消息。

典型的callstack以 WPF 调用开始,但最终调用BeginInvoke WinForm 消息,这些消息会改变UI的状态。我可以通过事件处理程序发生,我们更改了 WPF 集合,这些集合正好在此刻枚举。

...

   at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.Form.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.DispatcherOperation.Wait(TimeSpan timeout)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherOperation operation, CancellationToken cancellationToken, TimeSpan timeout)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)

实际的 WPF 代码导致这看起来无辜:

UIDispatcher.Current  (this is WPF Dispatcher) .Invoke(new Action<string, …>(delegate(string msg, …)
        {
            this.statusModel.AddToStatus(msg, …);
        }), new object[]
        {
            message,
            icon
        });

   at System.EventHandler`1.Invoke(Object sender, TEventArgs e)
….

此行为是否记录在案? 由于我有 WinForm WPF 代码,因此基于 WinForm 的代码使用WinForm机制来触发UI线程中的执行(System的ISynchronizeInvoke)。 Windows.Forms.Control)。

这基本上混合了 WPF WinForms 调度的消息。摆脱这个烂摊子的建议解决方案是什么?我应该通过调用 WPF Dispatcher调用来更改所有 WinForms Invoke / BeginInvoke调用,这样我们就有了一个公共的消息处理队列而不是两个。或者有更好的方法吗?

更新1

在这里可以更详细地找到混合WPF和WinForms的问题:

1 个答案:

答案 0 :(得分:0)

如何嵌套调用调用,以便从您选择的消息泵确定性地触发所有调用?

这样的事情:

yourWinFormsControl.Invoke((Action)(() =>
    {
        UIDispatcher.Current.Invoke(
            (Action)(() =>
            {
                this.statusModel.AddToStatus(xyz);
            }));
    }));

这将创建一个Action,它在WPF调度程序上执行调用,但将其推送到WinForms调度程序。