【问题标题】:Update children View Model from Parent raising events从父培养事件更新子视图模型
【发布时间】:2013-12-03 10:12:17
【问题描述】:

涉及的类:

  1. 作为儿童控件容器的经典 MVVM。
  2. 具有自己的视图模型的子控件(具体为按钮)。
  3. BackgroundWorker 通过套接字轮询远程主机。

行为:

  • 主应用程序开始从 XML 列表加载子控件。 每个 Child 都有父视图中的更新事件的处理程序 模型(我们称之为主视图模型)更新 孩子。
  • Main ViewModel 启动一个 BackgroundWorker
  • BackgroundWorker 调用 Socket 方法并使用 ViewModel 回调进行响应。
  • 回调更新 Main ViewModel 属性。

因为在这里它工作得很好,但是:

  • 更新后的属性会在集合中引发一个事件,并且 Children 处理程序从他们自己的属性更新开始。
  • 更新时引发异常:

调用线程无法访问该对象,因为不同的 线程拥有它。

几秒钟后按钮更新。我猜是因为 UI 线程再次获得了控制权并更新了控件。但是,有没有更好的方法来处理这个问题?

【问题讨论】:

    标签: c# wpf events mvvm backgroundworker


    【解决方案1】:

    要扩展 Uebercoder 的答案,您可能不关心视图模型中的控件,但您仍然需要将后台更新编组到主线程上。

    因此,无论是在您的后台工作人员中,还是更有可能在您的主视图模型中,您都需要执行以下操作:

    Application.Current.Dispatcher.BeginInvoke((Action)(() => { /* update properties */ } )));
    

    【讨论】:

    • 是的,谢谢,很清楚。但是在我的属性集中,我引发了一个事件,而不是直接的属性更新。由于它适用于父视图模型,显然子视图模型中的事件处理程序需要重新加入 UI 线程。但是,有没有更好的方法来设计它而不使用 Dispatcher 技巧?
    • 我不同意子视图模型需要关心编组。是的,这是因为控制访问而爆炸的地方,但是主视图模型知道后台线程,所以它应该负责编组。主视图模型的消费者不必知道它的实现。唯一的另一种方法是让主视图模型轮询后台线程,但这否定了拥有它的意义。
    • 我将此标记为答案,因为我目前正在使用它并且似乎是最快的解决方案。
    【解决方案2】:

    只有拥有的线程才能更新窗口元素(即控件)。

    使用Control.Invoke() 方法更改按钮。我写了一个小助手类来处理这个问题。

    public static class ControlEx
    {
        public static void DoSafely(this Control control, Action action)
        {
            if (control.InvokeRequired)
                control.Invoke(new Action(() =>
                    {
                        action();
                        Application.DoEvents();
                    }));
            else
                action();
        }
    }
    

    所以不要做类似的事情:

    button.Text = "new text";
    

    从另一个线程,我现在调用

    button.DoSafely(() => button.Text = "new text");
    

    【讨论】:

    • 我在 MVVM 中,我不关心 Control,因为我正在更新 ViewModel 属性。
    • 正如 GazTheDestroyer 所说,无论是更新控件上的属性还是模型中的属性,想法都是一样的。
    • 确实如此,但是这样我需要调用每个子视图模型,这是不可接受的。我有事件订阅来避免它。
    猜你喜欢
    • 2013-08-26
    • 2017-05-04
    • 1970-01-01
    • 2016-11-25
    • 1970-01-01
    • 2017-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多