1

问题:在 .NET 3.5 WinForms 应用程序中,如何在不传递委托的情况下从单独的线程访问/调用 UI 线程中的方法?

示例:假设我有一些代码我想(a)在用户单击按钮时手动运行,以及(b)由在单独的非 mainUI 线程中运行但不传递委托的进程定期调用。 [简单地说,我认为已经构造了具有此方法的类,并且主 UI 线程具有它的句柄,因此如果在单独线程中运行的进程可以从主 UI 中获取它的句柄线程它可以调用它。希望这不是一个有缺陷的概念]

背景:对于我的单独进程线程实际上是我使用quartz.net安排的工作的情况,我实际上正在寻找一种方法来完成上述工作。调度程序的工作方式我似乎无法真正传递委托。有一种方法可以传递 JobDetails,但它似乎只适合字符串、int 等。因此,我所追求的是一种访问 MainForm 类的方法,例如,从内部调用它的方法在单独的线程中运行的quartz.net 作业。

谢谢

4

4 回答 4

2

假设您可以访问 MainForm,您可以简单地调用它的任何方法,但是这些方法将承担检查它们是否需要编组到 UI 线程并处理传递到那里的委托的负担。

所以在你的主表单上你可以有一个方法:

public void UpdateProgress()
{
    if( this.InvokeRequired )
    {
        this.Invoke(UpdateProgress);
        return;
    }

    // do actual update of progress here
}
于 2010-03-26T21:50:33.350 回答
1

.Net 不允许在非 UI 线程中摆弄 UI;有一些方法可以解决它,例如Invoke,但这是(安全地)不求助于轮询共享对象的唯一方法。

于 2010-03-26T21:27:38.603 回答
1

您可以尝试工具箱中的 BackgroundWorker 控件,这适用于简单的事情。

于 2010-03-26T21:39:11.267 回答
0

这是 WinForms 应用程序的一种方法,如果它具有在应用程序初始化后始终存在的表单“MainForm”。我在静态变量中缓存对它的引用,然后有一个静态辅助方法,我在所有需要访问 UI 的方法中使用它,并且可能从非 UI 线程调用。

我喜欢这种方法的地方在于,在初始设置之后,您可以在任何类中编写 UI 触摸代码,而不仅仅是作为控件的类。并且编码是一个简单的事情,将一个动作包装在一个对MyApp.RunOnUIThread. 请参阅、 和的定义SomeUIWork1,了解有关此内容的变体。SomeUIWork2SomeUIWork3


限制和注意事项:

  • 如果您的应用程序没有始终存在的表单,或者由于某种原因您有多个 UI 线程,则需要调整此解决方案,或者可能对您没有用处。

  • 与所有涉及 RequireInvoke 或类似测试的方法一样,这种方法可能会被过度使用,从而导致系统难以维护/理解。建议仅作为最后的手段。(我在增强遗留代码时使用了它,因为需要太多的开发时间来安全地重构现有代码。)

如果可行,不要像我在这里做的那样,将你的 UI 代码与非 UI 代码分开。例如,将 BackgroundWorker 与 progressChanged https://stackoverflow.com/a/10646636/199364一起使用。


在 C# 中:

public static class MyApp
{
    public static MainForm mainForm;

    public static void RunOnUIThread(Action action)
    {
        if (mainForm.InvokeRequired)
            mainForm.Invoke(action);
        else
            action();
    }
}


// In the actual project, the Form inheritance is in the Visual Designer file for this form.
public class MainForm : System.Windows.Forms.Form
{

    public MainForm()
    {
        // Defined in the Visual Designer for this form.
        InitializeComponent();

        MyApp.mainForm = this;
    }
}


public class SomeClass
{
    public void SomeMethod()
    {
        // ... do some work ...

        SomeUIWork1(true);

        // ... do some work ...

        SomeUIWork2();

        // ... do some work ...

        SomeUIWork3(true);
    }

    // This accesses UI elements, yet is safe to call from non-UI thread.
    // Shows inline code.
    public void SomeUIWork1(bool param1)
    {
        MyApp.RunOnUIThread(() =>
        {
            // ... do the UI work ...
        });
    }

    // This accesses UI elements, yet is safe to call from non-UI thread.
    // Shows calling a separate method, when no parameters.
    public void SomeUIWork2()
    {
        MyApp.RunOnUIThread(SomeUIWork2_AlreadyOnUIThread);
    }

    // This accesses UI elements, yet is safe to call from non-UI thread.
    // Shows calling a separate method, when there are parameters.
    public void SomeUIWork3(bool param1)
    {
        MyApp.RunOnUIThread(() => SomeUIWork3_AlreadyOnUIThread(param1));
    }


#region "=== Only call from UI thread ==="
    // Only call if you are certain that you are running on UI thread.
    private void SomeUIWork2_AlreadyOnUIThread()
    {
        // ... do the UI work ...
    }

    // Only call if you are certain that you are running on UI thread.
    private void SomeUIWork3_AlreadyOnUIThread(bool param1)
    {
        // ... do the UI work ...
    }
#endregion
}

在 VB 中:

Imports Microsoft.VisualBasic

Public Shared Class MyApp
    Public MainForm As MainForm

    Public Sub RunOnUIThread(action As Action)
        If MainForm.InvokeRequired Then
            MainForm.Invoke(action)
        Else
            action()
        End If
    End Sub
End Class

' In the actual project, the "Inherits" line is in the Visual Designer file for this form.
Public Class MainForm
    Inherits System.Windows.Forms.Form   ' Or whatever type you are customizing

    Sub New()
        ' This call is required by the designer.
        InitializeComponent()

        MyApp.MainForm = Me
    End Sub
End Class

Public Class SomeClass
    Public Sub SomeSub()
        ' ... do some work ...

        SomeUIWork1(True)

        ' ... do some work ...

        SomeUIWork2()

        ' ... do some work ...

        SomeUIWork3(True)
    End Sub

    ' This accesses UI elements.
    ' Shows inline code.
    Public Sub SomeUIWork1(param1 As Boolean)
        MyApp.RunOnUIThread(
            Sub()
                ' ... do the UI work ...
            End Sub)
    End Sub

    ' This accesses UI elements.
    ' Shows calling a separate method, when no parameters.
    Public Sub SomeUIWork2()
        MyApp.RunOnUIThread(SomeUIWork_AlreadyOnUIThread)
    End Sub

    ' This accesses UI elements.
    ' Shows calling a separate method, when there are parameters..
    Public Sub SomeUIWork3(param1 As Boolean)
        MyApp.RunOnUIThread(Sub() SomeUIWork_AlreadyOnUIThread(param1))
    End Sub


#Region "=== Only call from UI thread ==="
    ' Only call if you are certain that you are running on UI thread.
    Private Sub SomeUIWork2_AlreadyOnUIThread()
        ' ... do the UI work ...
    End Sub

    ' Only call if you are certain that you are running on UI thread.
    Private Sub SomeUIWork3_AlreadyOnUIThread(param1 As Boolean)
        ' ... do the UI work ...
    End Sub
#End Region
End Class
于 2016-05-16T19:48:09.243 回答