【问题标题】:Cross-thread operation not valid control跨线程操作无效控件
【发布时间】:2014-04-28 11:16:40
【问题描述】:

我知道有很多关于我的问题的话题,但我找不到适合我的情况的解决方案...

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

跨线程操作无效控件“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);

【问题讨论】:

  • 据我所知,这应该是正确的。 panel.Invoke(...) 被调用了吗?
  • 你为什么要为此使用递归调用?为什么不直接调用 panel.Invoke((MethodInvoker)delegate { panel.Controls.Add(ctrl); });如果 panel.InvokeRequired 为真?
  • 您的控件是在与 panel1 相同的线程上创建的吗?如果不是,那是你的问题。
  • 您的表单是否在与 UI 线程不同的线程上创建?
  • @aEkBKF winforms 控件具有线程关联性,因此创建它们的线程很重要,即调用 new 时正在运行的线程。如果 panel1 是在主线程中创建的,而 ctrl 是在后台线程中创建的,则它们不能一起使用,因为它们由不同的线程“拥有”。

标签: c# thread-safety invoke


【解决方案1】:

您可以像这样在项目的命名空间中创建控件扩展:

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);
});

【讨论】:

  • 但是这不与他已经在执行的操作完全相同,因此不能解决他的问题吗?但仍然是一个非常好的扩展!
  • 没有区别,你只是导出到了一个不错的扩展方法:)
  • 我在相当多的需要跨线程操作的项目中使用这个 ext,它可以工作。不知道为什么这会引发同样的错误。
  • 它会抛出相同的错误,因为它执行相同的逻辑。他的问题一定出在其他地方……
【解决方案2】:

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

来自MSDN: ...如果不需要 Invoke(调用发生在同一个线程上),或者如果控件是在不同的线程上创建但尚未创建控件的句柄,则 InvokeRequired 可以返回 false。

...

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

【讨论】:

    【解决方案3】:

    最后我解决了我的问题,我的代码是正确的,只是隐藏旧控件以显示新控件,使用这个不错的功能:

    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);
                        }
                    }
                }
            }
    

    像这样:

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

    然后,我可以像这样重新创建我的控件:

    AddControlToPanel(panel1, ctrl);
    

    感谢大家的帮助:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多