c# - 从非 UI 线程访问 UI 元素

标签 c# wpf mvvm

我有小 WPF/MVVM 具有两个视觉元素(一个 ComboBox 和一个简单的 TextBlock )的示例项目。这两个元素都绑定(bind)到我的 ViewModel 的一个属性:

属性 MainViewModel.cs

public const string WelcomeTitlePropertyName = "WelcomeTitle";
private string _welcomeTitle = string.Empty;

public string WelcomeTitle
{
    get{ return _welcomeTitle;}
    set
    {
        _welcomeTitle = value;
        RaisePropertyChanged(WelcomeTitlePropertyName);
    }
}

public const string PositionsPropertyName = "Positions";
private ObservableCollection<int> _positions = new ObservableCollection<int>();

public ObservableCollection<int> Positions
{
    get{ return _positions; }

    set
    {
        _positions = value;
        RaisePropertyChanged(PositionsPropertyName);
    }
}

绑定(bind) MainWindow.xaml
<StackPanel>
    <TextBlock Text="{Binding WelcomeTitle}"/>
    <ComboBox ItemsSource="{Binding Positions}" />
</StackPanel>

现在我从这样的非 UI 线程更改这两个属性(据我所知,这是不允许的):
    System.Threading.ThreadPool.QueueUserWorkItem(delegate
    {
        int i = 0;
        while(true)
        {
            Positions.Add(i); // Solution 1: this throws NotSupportedException
            WelcomeTitle = i.ToString(); // Solution 2: this works

            i++;
        }
    }, null);

问题:

为什么解决方案 1 会抛出 NotSupportedExpection (不允许从非调度程序线程更改集合)而解决方案 2 按需要工作?

最佳答案

Now I change both properties from a non UI thread like this (which is not allowed, as far as I know it)


通常,无论您在哪个线程上,更改属性值都非常好。当更改属性具有“有趣”的副作用时,可能会出现问题和限制。
在这种情况下,两个被更改的属性都会产生有趣的副作用,观察到的行为的差异是由于这些副作用被以不同的方式处理(来自框架代码,您无法直接看到)。

Why does solution 1 throw a NotSupportedExpection (not allowed to change collection from non dispatcher thread) while solution 2 works as desired?


当绑定(bind)源的属性发生更改时,WPF 绑定(bind)系统会通过对 UI 进行相应的更新来做出响应;但是,当从后台线程进行更改时,绑定(bind)系统的事件处理程序也将在后台线程中运行,并且无法直接更新 UI。对于这里讨论的两种情况都是如此。
不同之处在于,对于“简单”的属性值更改,绑定(bind)系统会自动检测到它没有响应 UI 线程上的更改,并使用 Dispatcher.Invoke 将 UI 更改分派(dispatch)到正确的线程。 ,但是当可观察的集合被修改时,这个调度不会自动发生。结果是更新 UI 的代码在后台线程上运行并引发异常。
解决方案
有两件事可以解决这个问题:
  • 直接在 UI 线程中进行属性更改
    如果在 UI 线程上进行了更改,则任何 PropertyChanged处理程序也将在 UI 线程上运行,因此他们可以自由地进行任何他们想要的 UI 更改。此解决方案可以在您自己的代码中强制执行,并且永远不会导致问题,但如果不需要更改 UI,则分派(dispatch)到 UI 线程的额外工作将无益地完成。
  • 确保 PropertyChanged处理程序将任何与 UI 相关的更改分派(dispatch)到 UI 线程
    这个解决方案的好处是它只按需分派(dispatch)工作,但也有一个缺点是必须显式地对事件处理程序(可能不是您自己的代码)进行编程以进行分派(dispatch)。 .NET 已经为普通属性做到了这一点,但对于 ObservableCollection 则没有。 .见 How do I update an ObservableCollection via a worker thread?了解更多信息。
  • 关于c# - 从非 UI 线程访问 UI 元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21114831/

    相关文章:

    c# - 将一个文档插入另一个 Microsoft.Office.Interop.Word

    c# - WPF效果差异

    c# - 在 WPF 中执行静态 ComboBox 的有效方法

    .net - 简单但显示有关 WPF 和 MVVM 知识的面试任务

    c# - 使用 LINQ 从另一个 LINQ 集合中获取结果

    c# - 如何打印 XMLNodeList 中的节点

    c# - 更改绑定(bind)的一部分

    MvvmCross DataBinding 修改 Android ListView 中的单个项目

    c# - 自定义控件可绑定(bind)属性中的值未更新 - Xamarin forms/Prism

    c# - 我们可以让 DataTable 在列上自动排序吗?