WPF 中什么时候需要调度程序

When is a dispatcher needed in WPF

建议的重复线程没有解决我的问题

所以我的理解是 WPF 应用程序处理所有 UI 相关的事情,按钮按下,通过调度程序线程更新可观察集合,可以用 Application.Current.Dispatcher.Invoke(Update UI element here), 而对模型和数据的更改可以由后台线程正常处理。

我不明白的是为什么你需要调用调度程序来更新一个绑定到组合框的可观察集合,但是当我想更新进度条或文本框中的文本或者如果按钮是启用我不需要呼叫调度员。我读到的所有内容都表明调度程序用于处理和更新 UI。文本框、进度条的状态、按钮是否启用都看不到 UI?

Observable 集合与不需要调用调度程序的 text/progress 条之间有什么区别?

几乎所有对 WPF UI 元素的调用都应该发生在应用程序的主线程上。 这通常是通过与该线程关联的 Dispatcher 的方法来完成的。 调度程序可以从任何 UI 元素中获取,但通常是从应用程序中获取:Applicatiion.Current.Dispatcher.

如果你不直接给UI元素的属性赋值,而是为此使用绑定,那么绑定机制内置了将赋值编组到[=的流程中40=] 元素。 因此,可以在任何线程上引发 INotifyPropertyChanged.PropertyChanged 事件。

当可观察集合发生变化时,会引发 INotifyCollectionChanged.CollectionChanged 事件。 不提供自动编组到 UI 线程。 但是集合可以用BindingOperations.EnableCollection(...)方法同步。 然后它可以在任何线程中更改(使用同步)。 如果没有执行过这样的同步,那么只能在UI线程中更改。

除了这些事件,还有ICommand.CanExecuteChanged。 没有为它提供额外的编组或同步方法。 因此,只能在UI线程中引发。 这通常内置于 ICommand 的 WPF 实现中,程序员无需担心。 但是在简单的(主要是教育性的)实现中,没有这样的编组。 因此,在使用它们时,程序员自己必须负责转换到 UI 线程以引发此事件。

So basically in MVVM practice you no matter what you would have to use a dispatcher to use BindingOperations.EnableCollectionSynchronization(fooCollection, _mylock)); correct?

是的。 对于集合的应用,你理解正确。

这里只出现了 View 和 ViewModel 功能分离的问题。 您只能在视图或主 UI 线程上调用“EnableCollectionSynchronization”。 但是您正在 ViewModel 中实现集合。 另外,为了不阻塞内存,在删除集合时(将其替换为另一个集合、清除使用它的绑定、替换 ViewModel 实例等),需要使用“DisableCollectionSynchronization(collection)”删除创建的同步" 方法。

在这方面,如果在整个应用程序会话中使用单个 ViewModel 实例,那么使用“EnableCollectionSynchronization()”是最简单的解决方案。

示例:

    public class MainViewModel
    {
        public ObservableCollection<int> Numbers { get; }
            = new ObservableCollection<int>();

        protected static readonly Dispatcher Dispatcher = Application.Current.Dispatcher;
        public MainViewModel()
        {
            if (Dispatcher.CheckAccess())
            {
                BindingOperations.EnableCollectionSynchronization(Numbers, ((ICollection)Numbers).SyncRoot);
            }
            else
            {
                Dispatcher.Invoke(()=>BindingOperations.EnableCollectionSynchronization(Numbers, ((ICollection)Numbers).SyncRoot));
            }
        }
    }

但是如果使用了很多个VM实例,相互嵌套,动态增删改查(比如实现一棵树,在TreeView中查看就可以),那么使用EnableCollectionSynchronization()就变成了不是微不足道的。 如果你像我在上面的例子中那样做,那么由于对同步对象的引用,对集合的引用将被保存,它们不会从内存中删除,因此,不需要的 ViewModel 实例也不会被删除。删除。 这将导致内存泄漏。 因此,在实践中,经常使用将集合更改编组到 UI 线程。

也可以嵌入 INotifyCollectionChanged 实现,以及编组到 UI 线程,并在订阅/取消订阅 CollectionChanged 事件时调用“EnableCollectionSynchronization () / DisableCollectionSynchronization ()”。 ObservableCollection 的实现没有这个,但是在实现类似的各种包中有可观察集合的自定义实现。 它们的使用使程序员无需创建例行的、特定的、重复的代码。

遗憾的是,我无法准确告诉您包中包含哪些所需的实现。 我更喜欢使用自己的实现。