【问题标题】:Cross-thread set control跨线程集控制
【发布时间】:2016-05-24 08:06:47
【问题描述】:

我想设置你正在使用的线程在另一个线程上创建的控件。

在不同线程上创建的控件:PictureBox PBoxSplitContainer _sc

enter code here
class Class1
{
    public Class1(SplitContainer _sc,PictureBox PBox)
    {
        SetPanel(_sc,PBox);
    }


    delegate void SetPanelDele(SplitContainer _sc, PictureBox pb);
    void SetPanel(SplitContainer _sc, PictureBox pb)
    {
        if (pb.InvokeRequired && _sc.InvokeRequired)
        {
            pb.Invoke(new SetPanelDele(SetPanel), new object[] { _sc, pb });
        }
        else
        {
            pb.Parent = _sc.Panel2; // here
        }
    }
 }   

【问题讨论】:

  • 那么问题是什么?
  • 你应该检查this.InvokeRequired,而不是pb.InvokeRequired

标签: c# multithreading


【解决方案1】:

请注意,如果pb.InvokeRequired != _sc.InvokeRequired 存在严重问题,则两个控件都必须在 UI 线程上有效

回答您的问题,那么它是不,您不能使用在另一个线程中创建的控件,除非您确保尚未在该线程上创建句柄。

简而言之(让我稍微简化一下):必须从 UI 线程创建和访问所有控件。没有例外。

稍微长一点:可以从任何线程创建实例,但 handle 必须在 UI 线程中创建。这意味着 sc 和 pb 可以在两个不同的线程中创建,然后在 UI 线程中合并到 UI 中,但您必须确保不要调用任何方法或设置任何会导致创建句柄的属性。

可以完成,但它非常脆弱。您可以做的是提供一个工厂方法,您将在适当的线程上调用:

static void AddControl(Control control, Func<Control> factory)
{
    if (control.InvokeRequired)
    {
        control.BeginInvoke(
            new Action<Control, Func<Control>>(AddToControls), control, factory);
    }
    else
    {
        control.Controls.Add(factory());
    }
}

然后会这样使用:

// I'm in another non UI thread...
AddControl(splitContainer, () =>
{
    // Just an example...
    return new PictureBox
    {
        Size = new Size(40, 40),
        Image = LoadImageFromFile()
    };
});

请注意,工厂方法将捕获变量,如果您使用调用线程访问共享资源,您可能需要一些同步。我使用BeginInvoke() 而不是Invoke() 来不阻塞调用线程,事情更容易,因为在WinForms 中我们不需要调用EndInvoke()


与您的问题无关的旁注,您应该坚持命名约定,现在您有_sc(函数参数的下划线前缀)、PBox(函数参数的标题大小写)、pb(全部小写- 或驼峰式 - 用于函数参数)。这有点令人困惑,通常参数和局部变量是无前缀的驼峰式标识符。

【讨论】:

  • 谢谢你,但我不能用它来询问例子。
  • AddToControls(_sc, )
猜你喜欢
  • 2017-11-02
  • 1970-01-01
  • 2010-12-04
  • 1970-01-01
  • 1970-01-01
  • 2013-07-06
  • 1970-01-01
  • 2011-10-14
  • 2011-02-03
相关资源
最近更新 更多