我有小 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 线程上进行了更改,则任何
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/