Это один из подходов к приложению WinForms, если оно имеет форму «MainForm», которая всегда существует после инициализации приложения. Я кеширую ссылку на это в статической переменной, а затем у меня есть статический вспомогательный метод, который я использую во всех методах, которым необходим доступ к пользовательскому интерфейсу, и которые могут вызываться из потоков, не связанных с пользовательским интерфейсом.
Что мне нравится в этом подходе, так это то, что после начальной настройки вы можете написать код, касающийся интерфейса пользователя, в ЛЮБОМ классе, а не только в классах, которые являются элементами управления. А кодирование - это просто вопрос оборачивания действия в вызове MyApp.RunOnUIThread
. См. Определения для SomeUIWork1
, SomeUIWork2
и SomeUIWork3
для вариантов этого.
Ограничения и предостережения:
Если ваше приложение не имеет формы, которая всегда существует, или по какой-то причине у вас есть несколько потоков пользовательского интерфейса, то это решение нужно будет адаптировать, или оно может быть бесполезным для вас.
Этот подход, как и все подходы, включающие RequireInvoke или аналогичные тесты, может быть ПЕРЕОБРАЗОВАН, что приводит к сложной для обслуживания / понимания системе. Рекомендуется только в крайнем случае. (Я использовал его при расширении устаревшего кода, когда было слишком много времени на разработку, чтобы безопасно реорганизовать существующий код.)
Если это целесообразно, вместо того, чтобы делать то, что я делаю здесь, отделите ваш код пользовательского интерфейса от кода не-пользовательского интерфейса Например, используйте 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