以线程安全的方式迭代ComboBox.Items

时间:2014-04-07 09:52:59

标签: c# multithreading winforms

如何以线程安全的方式迭代C#WinForms ComboBox.Items集合(请参阅下面的代码示例)?使用集合我可以使用控制锁定,但是我不控制comboBox.Items集合,因为它是由其数据绑定源更新的(如果我想保持这种方法可重用,我可以与任何人交互此代码中的特定数据绑定源)。有一个comboBox.Items.CopyTo方法,但我需要先创建一个数组,并且计数可能会在创建数组和执行副本之间发生变化吗?

感谢您的任何建议。

private void SetComboBoxWidth(ComboBox comboBox, bool setDropDownWidth)
{
    int maxWidth = 0;
    using (Graphics graphics = comboBox.CreateGraphics())
    {
        foreach (object item in comboBox.Items)
        {
            int curWidth = TextRenderer.MeasureText(graphics, item.ToString(), comboBox.Font).Width;
            if (curWidth > maxWidth)
            {
                maxWidth = curWidth;
            }
        }
    }
    maxWidth += SystemInformation.VerticalScrollBarWidth;

    if (setDropDownWidth)
    {
        comboBox.DropDownWidth = maxWidth;
    }
    else
    {
        comboBox.Width = maxWidth;
    }
}

编辑: 上面的代码抛出:{System.InvalidOperationException} Collection被修改;枚举操作可能无法执行。

组合框设置为:

myComboBox.ComboBox.DataSource = new BindingList<IMyInterface>(); 

调用链是:MyForm.OnLoad-&gt; MyForm.Setup-&gt; MyForm.SetComboBoxWidth

编辑2: 更改示例代码以在循环期间根本不更改ComboBox(之前的版本可能会在每次迭代时调整宽度)。

2 个答案:

答案 0 :(得分:0)

不应该更新多个线程中的UI。如果您遵循此规则,则不必担心线程安全,因为它们仅由&#34;主线程&#34;更新。

如果你需要在另一个线程中更新UI,你可以使用Control.InvokeControl.BeginInvoke将控件传递给UI线程。

答案 1 :(得分:0)

回答我自己的问题以防其他人遇到这个问题,虽然我没有完全了解发生的事情。

存储在BindingList中的IMyInterface对象也实现了INotifyPropertyChanged。后台线程可以更改IMyInterface对象中的属性,从而导致发生INotifyPropertyChanged.PropertyChanged。当BindingList DataSource中对象的属性发生变化时,ComboBox会重新创建其列表(我想想无论如何都会发生这种情况,不确定)。如果在foreach迭代时发生这种情况,则抛出InvalidOperationException。

我通过创建BindingList的子类解决了这个问题,如果需要,它会将OnListChanged重写为Invoke。这似乎解决了这个问题。

感谢那些回答并评论并指出正确方向的人。