问题:在.NET 3.5 WinForms应用程序中,如何在不传递委托的情况下从单独的线程访问/调用UI线程中的方法?
示例:假设我有一些代码我希望同时运行(a)当用户单击按钮时手动运行,(b)由一个单独运行的进程定期调用mainUI线程但没有传递委托。 [简单地说我认为已经构造了具有此方法的类,并且主UI线程有一个句柄,因此如果在单独的线程中运行的进程可以从它获取它的句柄它可以调用它的主UI线程。希望这不是一个有缺陷的概念]
背景:我实际上已经采用了一种方法来执行上述操作,因为我的单独进程线程实际上是我使用quartz.net计划的工作。调度程序的工作方式我似乎无法实际传入委托。有一种方法可以传递JobDetails,但它似乎只能满足像string,int等一样的东西。因此,我所追求的是一种访问MainForm类的方法,例如,在其中调用方法,从quartz.net工作在一个单独的线程中运行。
谢谢
答案 0 :(得分:2)
假设您可以访问MainForm,您可以简单地调用它的任何方法,但这些方法将承担检查是否需要编组到UI线程并处理通过那里的委托的负担。
因此,在您的主表单上,您可以使用方法:
public void UpdateProgress()
{
if( this.InvokeRequired )
{
this.Invoke(UpdateProgress);
return;
}
// do actual update of progress here
}
答案 1 :(得分:1)
.Net不允许从非UI线程中摆弄UI;有很多方法可以解决它,例如Invoke
,但这是(安全地)执行它的唯一方法,而不需要轮询共享对象。
答案 2 :(得分:1)
您可以在工具箱中尝试BackgroundWorker控件,这适用于简单的事情。
答案 3 :(得分:0)
这是WinForms应用程序的一种方法,如果它有一个表单" MainForm"应用程序初始化后始终存在。我在静态变量中缓存对该引用的引用,然后在我需要访问UI的所有方法中使用静态帮助器方法,并且可以从非UI线程调用。
我喜欢这种方法,是在初始设置之后,你可以在任何类中编写触摸UI的代码,而不仅仅是作为控件的类。编码是一个简单的事情,即在MyApp.RunOnUIThread
的调用中包含一个动作。有关此变体,请参阅SomeUIWork1
,SomeUIWork2
和SomeUIWork3
的定义。
限制和注意事项:
如果您的应用没有始终存在的表单,或者由于某种原因您有多个UI线程,那么此解决方案将需要进行调整,或者可能对您没用。
< / LI>与所有涉及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