【问题标题】:WPF & MVVM : best architecture for Time-consuming search results?WPF 和 MVVM:耗时搜索结果的最佳架构?
【发布时间】:2011-03-28 13:18:38
【问题描述】:

我正在为进行搜索的模块设计架构。 搜索需要一些时间,我希望 UI 能够响应,因此我可以将数据检索委托给一个类,该类将在单独的线程上进行搜索。

那么,我可以想到两个选择:

要么 1° : search 方法返回一个带有空数据的视图模型作为返回值(占位符),但是一旦搜索过程结束,视图模型的成员将被更新并通过数据绑定将结果显示在屏幕上,

2° : 搜索方法没有任何返回类型,但是一旦搜索过程结束,就会引发一个事件,并将具有最终值的视图模型传递到事件 args 中,以便调用方可以使用它代码。 (最终被视图消费)

对这个有什么想法吗?

编辑:当然,对于解决方案 1°,我指的是搜索结果“占位符”对象返回的对象上的 WPF 数据绑定

【问题讨论】:

  • 传入一个回调函数,当搜索完成时更新视图。

标签: wpf architecture mvvm domain-driven-design


【解决方案1】:

这是我在最近的一个项目中所做的。

IsBusy 属性是我在所有视图模型的基类中拥有的东西。它是一个布尔值,如果视图想要显示某种等待控件(如微调器或其他),则可以绑定到它。

_retrieveData 字段只是我在构建视图模型期间设置的一个动作。我这样做是因为 viewmodel 可能会以几种不同的方式获取其 Cars 列表 - 因此 _retrieveData 可能会根据使用的构造函数在其中包含不同的代码。 _retrieveData 获取数据后,将设置私有 backer,_cars 与数据。所以在 _retrieveData 完成后,使用 _cars 中的新数据将公共 Cars 设置为值会导致 PropertyChangedEvent 让视图知道自己更新。

所以效果是view第一次去取数据的时候,立马返回但是得到null。然后几秒钟后,它得到了实际数据。在此期间,UI 是响应式的。此外,如果 UI 想让用户知道它正在后台工作,则 IsBusy 为 true。

不确定这是否是处理它的好方法,但到目前为止它对我有用。

public List<Car> Cars
{
   get
   {
       if (this._cars == null)
       {
           base.IsBusy = true;

           // Start a background thread to get the data...
           ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object nullObject)
           {
               this._retrieveData.Invoke();
               this.Cars = this._cars;
               base.IsBusy = false;
           }));

           // While waiting on the background thread, return null for now.  When the background thread
           // completes, the setter will raise OnPropertyChanged and the view will know its time to bind...
           return this._cars;
       }

       return this._cars;
   }
   set
   {
       this._cars = value;
       base.OnPropertyChanged("Cars");
   }

}

【讨论】:

    【解决方案2】:

    如果您使用BackgroundWorker,则设计模式已为您完成。在DoWork 事件处理程序中调用您的搜索方法,并将结果放入传入的DoWorkEventArgsResults 属性中。

    使用 RunWorkerCompleted 事件处理程序中的结果更新 UI(它们将位于 RunWorkerCompletedEventArgs 对象的 Results 属性中)。

    如果您想在搜索过程中更新 UI,请让您的搜索方法调用 ReportProgress 并在 ProgressChanged 事件处理程序中更新 UI。您可以将任何您喜欢的内容放入ProgressChangedEventArgsUserState 属性中,包括中间搜索结果,但您必须小心不要传递后台线程在继续执行时将要触及的任何对象。

    【讨论】:

      【解决方案3】:

      您还可以利用优先绑定 (http://msdn.microsoft.com/en-us/library/system.windows.data.prioritybinding.aspx)。它提供了快/慢选项,您不必担心更新线程。当然,除非您希望添加一些视觉效果来让用户知道正在发生的事情。

      【讨论】:

        【解决方案4】:

        我将利用数据绑定并将您的搜索结果表示控件绑定到可观察的搜索结果集合,并从后台工作线程更新您的 ViewModel 中的该集合。然后,它不需要在您的代码中进行任何回调或更新消息,它就像您期望的那样工作,利用 WPF 中的管道。

        【讨论】:

        • 从后台线程更新 INotifyPropertyChanged 属性或 INotifyCollectionChanged 集合(如 ObservableCollection)将引发异常,除非您使用 Dispatcher.Invoke 之类的东西,这实际上是在创建对 UI 线程的回调。除非您在 DoWork 期间进行多次独立更新,否则通常更直接地应用 RunWorkerCompleted 回调中的更改。
        • 我的回答本可以更清楚。我实际上不会从后台工作线程更新集合。我会让后台工作线程从我的搜索中收集结果,然后我会将这些结果放入 View 绑定到 ViewModel 的可观察集合中。这样,在获取搜索结果时 UI 会保持响应,我们真的不需要担心回调。有很多方法可以剥去这只猫的皮,正如您所指出的,有些方法比其他方法更危险。
        • backgroundworker 有一个在主线程中引发的事件,因此:更新视图模型值没有问题。实际上,我的问题更多是关于是否将 WPF 管道用于这种“延迟反应”或在收到数据时手动将数据推送到视图模型,我相信,Dave 令人信服地回答了这一点。至少,这证实了我自己的感觉,而且没有人指出这是异端邪说,这就是我要走的路;)谢谢 :)
        猜你喜欢
        • 1970-01-01
        • 2016-06-29
        • 1970-01-01
        • 2014-05-13
        • 2013-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-16
        相关资源
        最近更新 更多