我有父窗体和子窗体。父表单包含一个dataGridView(帐户列表),子表单允许用户注册一个帐户。事件,GUI和线程
子窗体在单独的线程上启动。
当账户被注册时,它被添加到SQL数据库中,并且事件在父窗体订阅的子窗体上触发。父窗体然后更新dataGridView以从数据库添加新值。
问题是,当试图更新父窗体中的dataGridView时,事件被解雇我得到一个交叉线程错误。这是正常的行为吗?
我有父窗体和子窗体。父表单包含一个dataGridView(帐户列表),子表单允许用户注册一个帐户。事件,GUI和线程
子窗体在单独的线程上启动。
当账户被注册时,它被添加到SQL数据库中,并且事件在父窗体订阅的子窗体上触发。父窗体然后更新dataGridView以从数据库添加新值。
问题是,当试图更新父窗体中的dataGridView时,事件被解雇我得到一个交叉线程错误。这是正常的行为吗?
在你的处理器,你将不得不做一些工作,以使处理器背面的UI线程上......“规范”是使用InvokeRequired
和BeginInvoke
的以下模式:
private void OnChildFormSaysNewItemsHandler(object sender, EventArgs e)
{
// Bring on the UI thread
if (this.InvokeRequired)
{
Action<object, EventArgs> handler = OnChildFormSaysNewItemsHandler;
this.BeginInvoke(handler, sender, e);
return;
}
// Do the normal work...
}
在事件处理程序中会有这样的事情吗?this.Invoke((MethodInvoker)delegate fillData(); }); –
在进行“正常工作”部分之后,您将回到UI线程,因此您可以调用'FillData()'而不必担心invoke(它通常会同步调用UI线程中的某些内容) 。 – Reddog
是的,这是正常的,触发事件的线程是子线程表单。在此事件中运行在mainform中的代码在子窗体线程中运行。所以这很正常。 我鼓励你不要在不同的线程中启动窗体,控件。留在主线程中。但是您可以使用BackgroundWorker或其他任何工具在另一个线程中执行内部进程。
是的,您不允许从其他线程访问UI元素,而不是创建它们。您需要将呼叫整理到父窗体所在的线程(如果您使用winforms,则InvokeRequired是您的朋友)
此类行为是预期的 - WinForms应用程序模型不是线程安全设计。 要与来自非UI线程的控件进行交互,请使用Control.Invoke或Control.BeginInvoke()方法。
例子:
void RefreshData()
{
// Refresh database here
}
void MyOtherThreadCallback()
{
this.BeginInvoke(new Action(RefreshData()))
}
你父窗体尝试更新使用解雇事件的线程的网格视图。这会导致交叉线程错误。为了避免这种情况,您必须使用您创建控件的线程来更新控件。这通常使用代码:
if(control.InvokeRequired)
{
control.Invoke(delegateToThisMethod)
}
并在delegateToThisMethod
您更新网格视图。
对这一问题的好深入回答this question
你不列出您使用的是什么版本,但检查出MSDN上的BackgroundWorker Class,它使基本线程很容易。
你会想看看这些事件:
OnDoWork Raises the DoWork event.
OnProgressChanged Raises the ProgressChanged event.
OnRunWorkerCompleted Raises the RunWorkerCompleted event.
在DoWork的方法会发生什么是一个单独的线程(以及任何调用),但二者所创建的运行ProgressChanged和RunWorkerCompleted方法在UI线程上,因此可以更新UI元素。
与ui控件的任何交互操作都必须从创建控件的线程中完成。假设您使用win窗体,则需要使用Control.BeginInvoke/Invoke方法与另一个线程中创建的控件进行交互 – Loman