为什么在WinForms中你可以跨线程添加控件,但在WPF中却不行?

4
在一个虚拟的WinForms应用程序中,我能够在设计时创建一个ListBox,在运行时创建一个后台线程,然后从后台线程向ListBox添加控件。但如果我在WPF中做同样的事情,就会出现错误。
为什么我能在WinForms中做到这一点,但在WPF中却不能?我的WinForm示例和WPF示例不同吗?或者确实有一个原因,它在WinForms中可以正常工作而在WPF中不行?
WinForms:
private List<Label> _labels;

public Form1()
{
    InitializeComponent();

    Thread test = new Thread(DoStuff);
    test.SetApartmentState(ApartmentState.STA);
    test.Start();
}

private void DoStuff()
{
    _labels = new List<Label>();

    _labels.Add(new Label() { Text = "Label1" });
    _labels.Add(new Label() { Text = "Label2" });
    _labels.Add(new Label() { Text = "Label3" });

    if (listBox1.InvokeRequired)
    {
        listBox1.Invoke((MethodInvoker)delegate { listBox1.DataSource = _labels; });
    }
    else
    {
        listBox1.DataSource = _labels;
    }
}

WPF:

public partial class MainWindow : Window
{
    private ObservableCollection<Label> _labels;
    public MainWindow()
    {
        InitializeComponent();

        Thread test = new Thread(DoStuff);
        test.SetApartmentState(ApartmentState.STA);
        test.Start();
    }

    private void DoStuff()
    {
        _labels = new ObservableCollection<Label>();
        _labels.Add(new Label() { Content = "Label1" });
        _labels.Add(new Label() { Content = "Label2" });
        _labels.Add(new Label() { Content = "Label3" });

        this.Dispatcher.BeginInvoke((Action)(() =>{ icMain.ItemsSource = _labels; }));
    }
}

我收到的错误信息是标准和预期的,如下图所示:

enter image description here


3
在Winforms中,你实际上不应该这样做。这是WPF的特定设计,你必须从拥有它的线程修改UI元素。要从另一个线程修改它,你需要使用Dispatcher。 - cost
WinForms示例没有从后台线程执行任何相关操作。listBox1.Invoke(...)确保传递的委托在UI线程上调用。 - user743382
@hvd 我认为他的问题是,在这两个示例中,_labels 都是在后台线程上创建的,因此当它在 UI 线程上使用时应该会导致相同的错误? - Allan Elder
@AllanElder _labels 不是用户界面元素,List<T>ObservableCollection<T> 也没有这样的跨线程检查(除了可能存在并发访问),所以如果它引起任何混淆,那会让我感到惊讶,但你可能是正确的。 - user743382
2个回答

2

WinForms对于跨线程问题的检查不如严格。这可能是因为WinForms实际上没有像标签这样的控件。相反,它们只是封装在操作系统级别实现的真正控件的包装器。

由于这是一项实现细节,所以不能保证您的WinForms代码将在未来继续工作。(也就是说,它没有处于活跃开发阶段,所以它很可能会继续工作。)


我不能代表WPF示例发言,但对于问题中的WinForms示例,没有任何跨线程问题。该控件仅从UI线程访问。 - user743382
被创建的三个标签是控件。 - Jonathan Allen
啊!在我看来,问题中的“Label”不是一个控件,但如果它是,那就可以解释一切了。在这种情况下,OP所做的事情就没有任何意义。列表框的数据源或项源不应包含任何控件,这是没有道理的。 - user743382

-2

我认为在WinForms中添加子控件或修改控件属性也是不好的做法。WPF只是有更先进的方法来控制从其他线程访问UI,并在早期阶段警告您。

相关问题


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接