如何在多个线程中访问 UI 元素?

Posted

技术标签:

【中文标题】如何在多个线程中访问 UI 元素?【英文标题】:How to access UI elements in multiple threads? 【发布时间】:2021-11-21 18:55:31 【问题描述】:

我有一个标准的foreach 循环,后来我变成了Parallel.Foreach()。但是在我的循环中,我有一些区域可以访问 UI 元素并获取和设置 UI 元素信息。

所以当我运行它时,我得到一个错误,我无法访问该元素,因为另一个线程可以访问它。我需要访问多个元素,x:Name 存储在列表中。

我该如何克服这个问题?

Parallel.ForEach(calculatedTestVariables, variable =>
        
            string idName = "id_" + variable.id;
            var textBox = this.FindName(idName) as TextBox; //need the text from this TextBox

            //some calculations
            int x = 1 + 2 + 3 + 4

            textBox.Text = x.toString();

        );

【问题讨论】:

你应该使用数据绑定和可能的数据模板来避免这种讨厌的代码。 控件具有线程亲和性:您只能从拥有的线程访问它们。出于这个原因,在后台线程上进行 UI 工作是没有意义的。只需将繁重的计算移至后台线程,然后返回 UI 线程以使用结果更新您的 UI。 【参考方案1】:

为此,您需要数据绑定。你可以按照这样的方式,

首先创建您的视图模型并更新您的属性:

public class MainViewModel : BindableBase

    private string m_TextProgress;
    public string TextProgress
    
        get  return m_TextProgress; 
        set  SetProperty(ref m_TextProgress, value); 
    

    public void Run()
    
        List<Variable> calculatedTestVariables = new List<Variable>();

        for (int i = 0; i < 5000; i++)
        
            Variable item = new Variable();

            item.id = i;
            calculatedTestVariables.Add(item);
        

        Parallel.ForEach(calculatedTestVariables, variable =>
        
            string idName = "id_" + variable.id;

            //some calculations
            int x = 1 + 2 + 3 + 4;

            TextProgress = "" + variable.id + x;
        );
    

设置你的数据上下文

<Window.DataContext>
    <local:MainViewModel />
</Window.DataContext>

<Grid>

    <StackPanel>
        <TextBlock
            Width="120"
            Height="30"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Text="Binding TextProgress, UpdateSourceTrigger=PropertyChanged, Mode=OneWay" />

        <Button
            Width="120"
            Height="30"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Click="Button_Click"
            Content="Calculate" />
    </StackPanel>
</Grid>

并从 GUI 端调用您的 Run 方法

【讨论】:

我认为您不需要对示例中的代码进行数据绑定。 @Andy,那会怎么样? 一个任务或方法可以接受一个参数并返回一个结果。您可以从 ui 传入任何您需要的内容,并在处理完成后返回设置 ui 属性所需的任何内容,返回 ui 线程。我可能会在使用视图模型使用 mvvm 时这样做,我也推荐使用 mvvm。但我不认为你绝对必须使用绑定。 是的,在这种情况下绑定不是必须的。他至少应该写一个异步方法。

以上是关于如何在多个线程中访问 UI 元素?的主要内容,如果未能解决你的问题,请参考以下文章

如何在多个任务之后继续而不阻塞 UI 线程?

在 XCTest 中:如何测试函数是不是强制执行到主线程

如何让工作线程在主 UI 线程上执行回调?

如何从 Android 中的 Fragment 访问 UI 元素?

[转]Android限制只能在主线程中进行UI访问的实现原理

如何向 Material-UI 的 AppBar 组件添加多个元素?