如何在 WinForm 组件的 UI 线程上调用?

Posted

技术标签:

【中文标题】如何在 WinForm 组件的 UI 线程上调用?【英文标题】:How to invoke on the UI thread of a WinForm Component? 【发布时间】:2011-04-30 09:59:09 【问题描述】:

我正在编写一个 WinForm 组件,在该组件中我启动一个任务来进行实际处理并在继续时捕获异常。从那里我想在 UI 元素上显示异常消息。

Task myTask = Task.Factory.StartNew (() => SomeMethod(someArgs));
myTask.ContinueWith (antecedant => uiTextBox.Text = antecedant.Exception.Message,
                     TaskContinuationOptions.OnlyOnFaulted);

现在我得到一个跨线程异常,因为该任务正在尝试从一个显然是非 UI 线程的 UI 元素。

但是,Component 类中没有定义 Invoke 或 BeginInvoke。

如何从这里开始?


更新

另外,请注意 Invoke/BeginInvoke/InvokeRequired 在我的 Component 派生类中不可用,因为 Component 不提供它们。

【问题讨论】:

【参考方案1】:

你可以给你的组件添加一个属性,允许客户端设置一个表单引用,你可以使用它来调用它的 BeginInvoke() 方法。

这也可以自动完成,最好不要忘记。它需要一些相当难以理解的设计时魔法。这个不是我自己想出来的,是从 ErrorProvider 组件中得到的。值得信赖的来源等等。将此粘贴到您的组件源代码中:

using System.Windows.Forms;
using System.ComponentModel.Design;
...
    [Browsable(false)]
    public Form ParentForm  get; set; 

    public override ISite Site 
        set 
            // Runs at design time, ensures designer initializes ParentForm
            base.Site = value;
            if (value != null) 
                IDesignerHost service = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
                if (service != null) this.ParentForm = service.RootComponent as Form;
            
        
    

当用户将您的组件放在表单上时,设计器会自动设置 ParentForm 属性。使用 ParentForm.BeginInvoke()。

【讨论】:

非常聪明的解决方案。 我观察到这段代码是在运行时调用的,并且在设计时拖放时会执行预期的操作。运行时:service.RootComponent as Form;返回空值。【参考方案2】:

您可以使用委托来执行此操作。

    delegate void UpdateStatusDelegate (string value);


    void UpdateStatus(string value)
    
        if (InvokeRequired)
        
            // We're not in the UI thread, so we need to call BeginInvoke
            BeginInvoke(new UpdateStatusDelegate(UpdateStatus), new object[]value);
            return;
        
        // Must be on the UI thread if we've got this far
        statusIndicator.Text = value;
    

【讨论】:

那么,创建一个委托并调用它会使用 UI 线程吗? 应该做,没有测试过这段代码,但这是我在标准winforms中的做法 很遗憾,这不起作用,因为 InvokeRequired 方法在组件中不可用。 您通常不需要创建委托;请改用Action<T> 你能在组件上创建一个事件,然后从更远的地方订阅它吗?

以上是关于如何在 WinForm 组件的 UI 线程上调用?的主要内容,如果未能解决你的问题,请参考以下文章

WPF 之 调用线程必须为 STA,因为许多 UI 组件都需要

Winform业务层如何调用UI层的代码

C# WINFORM 线程中更新UI

c# 多线程 ui winform界面

C#WinForm在新线程中动态创建控件时,gif图动不动

winform的窗体控件可以用线程直接调用吗