Mar*_*ser 11 invoke ui-thread invokerequired winforms
我目前正在尝试编写一个组件,其中某些部分应该在UI线程上运行(解释会很长).所以最简单的方法是将控件传递给它,并在其上使用InvokeRequired/Invoke.但我不认为将控件引用传递给"数据/背景"组件是一个好设计,所以我正在寻找一种在UI线程上运行代码的方法,而无需提供控件.像WPF中的Application.Dispatcher.Invoke ...
任何想法,马丁
Jud*_*ngo 19
有一种更好,更抽象的方法可以在WinForms和WPF上运行:
System.Threading.SynchronizationContext.Current.Post(theMethod, state);
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为WindowsForms将WindowsFormsSynchronizationContext对象安装为当前同步上下文.WPF执行类似的操作,安装它自己的专用同步上下文(DispatcherSynchronizationContext).
.Post对应于control.BeginInvoke,.Send对应于control.Invoke.
你是对的,将控制权传递给线程是不好的。Winforms 控件是单线程的,将它们传递给多个线程可能会导致竞争条件或破坏您的 UI。相反,您应该使线程的功能可供 UI 使用,并在 UI 良好且准备就绪时让它调用线程。如果您想让后台线程触发 UI 更改,请公开后台事件并从 UI 订阅它。线程可以在任何需要的时候触发事件,并且 UI 可以在有能力时响应它们。
在线程之间创建这种不阻塞 UI 线程的双向通信需要大量工作。下面是一个使用 BackgroundWorker 类的高度缩写的示例:
public class MyBackgroundThread : BackgroundWorker
{
public event EventHandler<ClassToPassToUI> IWantTheUIToDoSomething;
public MyStatus TheUIWantsToKnowThis { get { whatever... } }
public void TheUIWantsMeToDoSomething()
{
// Do something...
}
protected override void OnDoWork(DoWorkEventArgs e)
{
// This is called when the thread is started
while (!CancellationPending)
{
// The UI will set IWantTheUIToDoSomething when it is ready to do things.
if ((IWantTheUIToDoSomething != null) && IHaveUIData())
IWantTheUIToDoSomething( this, new ClassToPassToUI(uiData) );
}
}
}
public partial class MyUIClass : Form
{
MyBackgroundThread backgroundThread;
delegate void ChangeUICallback(object sender, ClassToPassToUI uiData);
...
public MyUIClass
{
backgroundThread = new MyBackgroundThread();
// Do this when you're ready for requests from background threads:
backgroundThread.IWantTheUIToDoSomething += new EventHandler<ClassToPassToUI>(SomeoneWantsToChangeTheUI);
// This will run MyBackgroundThread.OnDoWork in a background thread:
backgroundThread.RunWorkerAsync();
}
private void UserClickedAButtonOrSomething(object sender, EventArgs e)
{
// Really this should be done in the background thread,
// it is here as an example of calling a background task from the UI.
if (backgroundThread.TheUIWantsToKnowThis == MyStatus.ThreadIsInAStateToHandleUserRequests)
backgroundThread.TheUIWantsMeToDoSomething();
// The UI can change the UI as well, this will not need marshalling.
SomeoneWantsToChangeTheUI( this, new ClassToPassToUI(localData) );
}
void SomeoneWantsToChangeTheUI(object sender, ClassToPassToUI uiData)
{
if (InvokeRequired)
{
// A background thread wants to change the UI.
if (iAmInAStateWhereTheUICanBeChanged)
{
var callback = new ChangeUICallback(SomeoneWantsToChangeTheUI);
Invoke(callback, new object[] { sender, uiData });
}
}
else
{
// This is on the UI thread, either because it was called from the UI or was marshalled.
ChangeTheUI(uiData)
}
}
}
Run Code Online (Sandbox Code Playgroud)