C# 事件、GUI和线程

C# 事件、GUI和线程,c#,C#,我有父表和子表。父窗体包含dataGridView(帐户列表),子窗体允许用户注册帐户 子窗体在单独的线程上启动 注册帐户时,它将被添加到SQL数据库中,并对父窗体订阅的子窗体激发事件。然后,父窗体更新dataGridView以从数据库添加新值 问题是,当试图在触发事件时更新父窗体中的dataGridView时,我遇到了一个跨线程错误。这在上下文中是正常行为吗?是的,这是正常的,触发事件的线程是子线程窗体。此事件中在mainform中运行的代码在子窗体线程中运行。所以这很正常。 我鼓励您永远不要

我有父表和子表。父窗体包含dataGridView(帐户列表),子窗体允许用户注册帐户

子窗体在单独的线程上启动

注册帐户时,它将被添加到SQL数据库中,并对父窗体订阅的子窗体激发事件。然后,父窗体更新dataGridView以从数据库添加新值


问题是,当试图在触发事件时更新父窗体中的dataGridView时,我遇到了一个跨线程错误。这在上下文中是正常行为吗?

是的,这是正常的,触发事件的线程是子线程窗体。此事件中在mainform中运行的代码在子窗体线程中运行。所以这很正常。
我鼓励您永远不要在不同的线程中启动窗体和控件。留在主线程中。但是,您可以使用BackgroundWorker或其他任何工具在另一个线程中执行内部进程。

是的,您不允许从创建UI元素的其他线程访问UI元素。您需要将调用封送到父窗体所在的线程(如果您使用的是winforms,则是您的朋友)

这种行为是预期的-winforms应用程序模型在设计上不是线程安全的。 要与非UI线程中的控件交互,请使用Control.Invoke或Control.BeginInvoke()方法

例如:

void RefreshData()
{
    // Refresh database here
}

void MyOtherThreadCallback()
{
   this.BeginInvoke(new Action(RefreshData()))
}

父窗体尝试使用激发事件的线程更新网格视图。这会导致交叉线程错误。为了避免这种情况,您必须使用创建控件的线程更新该控件。这通常使用以下代码完成:

if(control.InvokeRequired)
{   
   control.Invoke(delegateToThisMethod)
}
中,将此方法委托给
更新网格视图


这个问题在处理程序中得到了很好的深入回答,您需要做一些工作才能将处理程序恢复到UI线程中。。。“规范”是使用
invokererequired
和以下模式:

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...
}
private void OnChildFormSaysNewItemsHandler(对象发送方,事件参数e)
{
//打开UI线程
if(this.invokererequired)
{
Action handler=OnChildFormSaysNewItemsHandler;
this.BeginInvoke(处理程序、发送器、e);
回来
}
//做正常的工作。。。
}

您没有列出您正在使用的版本,但请查看MSDN上的,它使基本线程非常容易

您将要查看以下事件:

OnDoWork               Raises the DoWork event.
OnProgressChanged      Raises the ProgressChanged event.
OnRunWorkerCompleted   Raises the RunWorkerCompleted event.

DoWork方法中发生的事情在单独的线程(以及它调用的任何线程)上,但是您创建的ProgressChanged和RunWorkerCompleted方法都在UI线程上运行,因此可以更新UI元素。

与UI控件的任何交互都必须从创建控件的线程中进行。假设您使用的是win forms,则需要使用Control.BeginInvoke/Invoke方法与在另一个线程中创建的控件进行交互。在事件处理程序中,这样做是否足够?调用((MethodInvoker)委托{fillData();});进入“完成正常工作”部分后,您将返回UI线程,因此您将能够调用
FillData()
,而无需担心调用(通常会同步调用UI线程上的某些内容)。