我当前正在尝试编写一个组件,其中的某些组件应该在UI线程上运行(解释将会很长)。 所以最简单的方法是将控件传递给它,并对其使用InvokeRequired/Invoke。 但我不认为这是一个好的设计将控制引用传递给“数据/背景”组件,所以我正在寻找一种方法来在UI线程上运行代码,而不需要控制可用。 喜欢的东西Application.Dispatcher.Invoke在WPF ...在不存在控制对象的UI线程上运行代码
任何想法, THX 马丁
我当前正在尝试编写一个组件,其中的某些组件应该在UI线程上运行(解释将会很长)。 所以最简单的方法是将控件传递给它,并对其使用InvokeRequired/Invoke。 但我不认为这是一个好的设计将控制引用传递给“数据/背景”组件,所以我正在寻找一种方法来在UI线程上运行代码,而不需要控制可用。 喜欢的东西Application.Dispatcher.Invoke在WPF ...在不存在控制对象的UI线程上运行代码
任何想法, THX 马丁
你是正确的,这是不好控制传递给线程。 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)
}
}
}
我更喜欢这种方法,因为它干净地将UI代码与后台工作分开。它还允许多个“监听者”响应后台工作状态更新。 – 2013-11-25 21:28:45
把UI操作的方法中的形式被操纵,并通过一个委托给在后台线程运行的代码,点菜APM。您不必使用params object p
,您可以强制键入以适应您自己的目的。这只是一个简单的通用示例。
delegate UiSafeCall(delegate d, params object p);
void SomeUiSafeCall(delegate d, params object p)
{
if (InvokeRequired)
BeginInvoke(d,p);
else
{
//do stuff to UI
}
}
这种方法是基于这样一个事实,即代表引用特定实例上的方法;通过使实现成为表单的一种方法,您将表单带入范围为this
。以下语义相同。
delegate UiSafeCall(delegate d, params object p);
void SomeUiSafeCall(delegate d, params object p)
{
if (this.InvokeRequired)
this.BeginInvoke(d,p);
else
{
//do stuff to UI
}
}
怎么样传递一个System.ComponentModel.ISynchronizeInvoke?这样你可以避免传递一个Control。
有做这更好的,更抽象的方式,在两个WinForms和WPF的工作原理:
System.Threading.SynchronizationContext.Current.Post(theMethod, state);
这工作,因为WindowsForms安装了一个WindowsFormsSynchronizationContext
对象作为当前同步上下文。 WPF做类似的事情,安装它自己的专用同步上下文(DispatcherSynchronizationContext
)。
.Post
对应于control.BeginInvoke
和.Send
对应于control.Invoke
。
当我尝试调用它时,SynchronizationContext.Current为null ... D: – 2013-03-15 19:55:48
首先,在您的表单构造函数中,保留SynchronizationContext.Current
对象(实际上是WindowsFormsSynchronizationContext
)的类作用域引用。
public partial class MyForm : Form {
private SynchronizationContext syncContext;
public MyForm() {
this.syncContext = SynchronizationContext.Current;
}
}
然后,你的类中的任何位置,使用此背景下将消息发送到用户界面:
public partial class MyForm : Form {
public void DoStuff() {
ThreadPool.QueueUserWorkItem(_ => {
// worker thread starts
// invoke UI from here
this.syncContext.Send(() =>
this.myButton.Text = "Updated from worker thread");
// continue background work
this.syncContext.Send(() => {
this.myText1.Text = "Updated from worker thread";
this.myText2.Text = "Updated from worker thread";
});
// continue background work
});
}
}
您将需要以下扩展方法与lambda表达式的工作:http://codepaste.net/zje4k6
请注明如果你解决了你的问题,你会接受一个答案。 – SandRock 2012-02-24 15:05:39