C# 跨线程操作无效控件

C# 跨线程操作无效控件,c#,thread-safety,invoke,C#,Thread Safety,Invoke,我知道关于我的问题有很多话题,但我找不到解决我的问题的办法 我有一个在运行时添加控件的面板,我尝试了这段代码,但它对我没有帮助,错误显示: 跨线程操作无效从线程访问的控件“panel1” 而不是创建它的线程 这是我的代码: public void AddControlToPanel(Panel panel, Control ctrl) { if (panel.InvokeRequired) { panel.Invoke((MethodInvoker)delegat

我知道关于我的问题有很多话题,但我找不到解决我的问题的办法

我有一个在运行时添加控件的面板,我尝试了这段代码,但它对我没有帮助,错误显示:

跨线程操作无效从线程访问的控件“panel1” 而不是创建它的线程

这是我的代码:

public void AddControlToPanel(Panel panel, Control ctrl)
{
    if (panel.InvokeRequired)
    {
        panel.Invoke((MethodInvoker)delegate { AddControlToPanel(panel, ctrl); });
        return;
    }
    else
        panel.Controls.Add(ctrl);
}
我这样称呼它:

AddControlToPanel(panel1, ctrl);
foreach (Control item in panel1.Controls.OfType<Type>())
                {
                    SetControlPropertyValue(item, "Visible", false);                 
                }

您可以在项目的命名空间中创建控件扩展,如下所示:

public static class ControlExtensions
{
    public static void UIThread(this Control @this, Action code)
    {
        if (null != @this && (!@this.Disposing || !@this.IsDisposed))
        {
            if (@this.InvokeRequired)
            {
                @this.BeginInvoke(code);
            }
            else
            {
                code.Invoke();
            }
        }
    }
}
this.UIThread(() =>
{
    panel1.Controls.Add(ctrl);
});
AddControlToPanel(panel1, ctrl);
并在代码中使用它,如下所示:

public static class ControlExtensions
{
    public static void UIThread(this Control @this, Action code)
    {
        if (null != @this && (!@this.Disposing || !@this.IsDisposed))
        {
            if (@this.InvokeRequired)
            {
                @this.BeginInvoke(code);
            }
            else
            {
                code.Invoke();
            }
        }
    }
}
this.UIThread(() =>
{
    panel1.Controls.Add(ctrl);
});
AddControlToPanel(panel1, ctrl);

在这种情况下,您还应该检查“IsHandleCreated”

发件人: 如果不需要调用(调用发生在同一个线程上),或者如果控件是在不同的线程上创建的,但控件的句柄尚未创建,则


通过在后台线程上检查InvokeRequired返回false时IsHandleCreated的值,可以防止这种情况。如果尚未创建控件句柄,则必须等到它已创建后再调用Invoke或BeginInvoke。通常,只有在应用程序的主窗体的构造函数中创建后台线程时才会发生这种情况(如application.Run(new MainForm()),在显示窗体或调用Application.Run之前。

最后我解决了问题,代码正确,只需隐藏旧控件以显示新控件,使用此功能:

private void SetControlPropertyValue(Control oControl, string propName, object propValue)
        {
            if (oControl.InvokeRequired)
            {
                SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue);
                oControl.Invoke(d, new object[] { oControl, propName, propValue });
            }
            else
            {
                Type t = oControl.GetType();
                PropertyInfo[] props = t.GetProperties();
                foreach (PropertyInfo p in props)
                {
                    if (p.Name.ToUpper() == propName.ToUpper())
                    {
                        p.SetValue(oControl, propValue, null);
                    }
                }
            }
        }
像这样:

AddControlToPanel(panel1, ctrl);
foreach (Control item in panel1.Controls.OfType<Type>())
                {
                    SetControlPropertyValue(item, "Visible", false);                 
                }

谢谢大家的帮助:)

在我看来,这应该是正确的。是否调用了
面板.Invoke(…)
?为什么对此使用递归调用?为什么不直接调用panel.Invoke((MethodInvoker)委托{panel.Controls.Add(ctrl);});如果panel.InvokeRequired为true?控件是否与panel1在同一线程上创建?如果不是,那就是你的问题。你的表单是在与UI线程不同的线程上创建的吗?@aEkBKF winforms控件具有线程关联性,因此它们是在哪个线程上创建的,也就是说调用new时哪个线程正在运行很重要。如果panel1是在主线程上创建的,而ctrl是在后台线程中创建的,那么它们不能一起使用,因为它们“属于”不同的线程。但是,这不是执行与他已经执行的完全相同的操作,因此不能解决他的问题吗?但仍然是一个非常好的扩展!没有区别,您只是将它导出到了一个很好的扩展方法:)我在需要跨线程操作的项目中使用了这个ext,它可以正常工作。不确定为什么会抛出相同的错误。它会在执行相同逻辑时抛出相同的错误。他的问题一定在别处。。。