MVVM通知UI

4

注意:请原谅我的英语,这不是我母语 - 我有时会拼错单词或使用错误的时态

情况描述

首先,我将描述一下情况。

我正在开发一个 Windows Phone 8 应用程序 (WP8.0,使用 .NET 4.5C#/XAML)。这是我的第一个 WP8 应用程序,周围没有太多人可以帮助我,所以我通过在网络上的教程和一些书籍学习 (换句话说,我是个新手,经验不足)。

我从一个 Web服务(稍后会描述)中收集了“不太多”的数据,并决定使用 Model-View-ViewModel (MVVM) 来存储提供的数据。

MVVM 结构:

以下是 MVVM 结构的伪示意图,下面附有说明。

MainViewModel
--------------
|
+ several properties (Username, Password, etc...)
|
+ Commands (loadData1, loadData2, flush, ...  - implementations of ICommand)
|
+ ------ PersonalInfoModel
|        -----------------
|        + several properties (name, surname, phonenumber, etc...)
|
|             
+ ------ DataGroup1Model
|        ---------------
|        +several properties
|        +ObservableCollection<Item1> (roughly 0 - 50 items)
|        +ObservableCollection<Item2> (roughly 0 - 5 items)
|        +ObservableCollection<string> (roughly 0 - 5 items)
|        
|                  Item1                         Item2
|                  -----                         -----
|                  +several properties           +several other properties
|                  
|
+ ------ DataGroup2Model (similar to previous)
...et cetera...

我有一个包含几个属性和几个命令(实现ICommand)以及多个模型的MainViewModel对象。这些模型包含其他属性和ObservableCollections。
MainViewModel不会一次性填充数据,而是随着用户浏览应用程序并请求数据而逐步填充。主要是因为有更多的Web服务需要从中收集数据 - 其中一些数据必须根据用户指定的条件(例如从哪个日期到哪个日期等)进行收集。
我决定创建一个“持久”(不确定是否是正确的词)的ViewModel,该ViewModel将在整个应用程序中存在,因为有时需要在页面中组合来自多个模型的数据。
例如,我需要始终在“userInfoBar”中显示一些基本用户信息,该信息始终存在于页面上,还需要一些来自DataGroup1Model和DataGroup2Model的信息(这些信息通过两个Web服务中的数据填充,并且用于更多的组合)。
我使用Web服务收集数据来填充ViewModel。具体地说,我使用一个具有9种方法的Web服务,不确定如何描述它 - 每种方法都为我提供有关某些内容的数据,我将其存储在datagroupModels中(有时是两个datagroupmodels,例如personalinfoModel和Datagroup1Model)。
Web服务已经提供给我,我无法更改它们,我只能使用它们。
“问题”
我正在尝试找出一种好的方法,向我的页面发出某些任务完成的信号。例如登录序列:
1. 我有一个包含两个TextBoxes的页面,它们的值绑定到MainViewModel属性UserName和Password以及绑定到MainViewModel中的Command Login的Button。 2. 登录命令只是启动“doLogin”方法,该方法使用用户名和密码属性,并将其作为凭据提供给Visual Studio自动生成的webserviceclient实例,客户端执行其中一个Web服务方法(异步方法) - 例如登录。 3. 当此方法完成时,它会引发与之关联的Completed事件,并在其CompletedArguments中提供Result对象。 4. 在完成事件中,我将数据从Result对象复制到viewModel中。
此时,向UI发出一些信号表明登录任务已完成并且应导航到另一页将是不错的。
我该如何做呢?是否有首选的方法?

P.S.:如果您对这里描述的内容有建议,我将很乐意听取。请记住,我是个新手,有时需要解释或在某些基本或愚蠢的事情上卡住。


这一定是我在SO上看到的最好格式化的问题。+1 - Mikael Östberg
@MikaelÖstberg 嗯,我花了一个小时把它写下来。 :) 但是我认为我正在问的同样的问题可能是其他人试图解决的问题,因此最好将其正确地写下来,以防其他人也需要帮助。 - mishan
同意 - 这就是你提问的方式,当有人请求帮助时,如果他们没有花足够的时间传达他们的问题,那么这是相当烦人的。人们似乎忘记了我们这些卑微的凡人不是无所不知的,不能从远处洞悉另一个人的思维深处......也就是说,把它写下来,否则我们就得猜测,如果我们必须猜测,我们可能不会给你一个好答案!然而,这是一个“应该放在问题中的光辉例子”。干得好先生! - Charleh
3个回答

2
如果通知需要发送到相同的视图/视图模型,那么实现INotifyPropertyChanged以获取通知就足够了。因此,通常您会更新一个Success属性,该属性反过来也会调用属性更改事件。
或者,如果此登录通知需要传递到其他模块。为了将状态传递给不在实现时知道的不同模块(模块),可以使用发布-订阅模型。https://msdn.microsoft.com/en-us/magazine/dn745866.aspx展示了如何使用MVVM Lights的发布机制。
也可以探索Prism的EventAggregator。

2
通常,您可以通过更改数据来从视图模型通知UI。例如,您可以在UI中绑定到一些通用的“状态”State属性。根据该属性的值,UI可以显示不同的内容。通常使用数据触发器来实现此功能。如果情况较为简单,您也可以只使用一个布尔值IsDataLoaded来告诉您是否已从Web服务加载了数据。或者更简单地,如果您想在视图中显示结果,则只需使用属性Result即可。只要您还没有加载数据,该值就为null,视图将显示登录表单。如果数据不为null(即已加载数据),则它将显示数据。
至于导航,通常您需要为每个导航页面拥有自己的视图模型。因此,如果您从登录屏幕开始,您将拥有一个“登录视图模型”,如果下一页是数据可视化,则您将拥有一个“数据视图模型”。通常使用数据模板来完成这一点。数据模板是依赖于类型的模板。您将拥有一个“元”视图模型,它基本上只有一个用于当前显示的视图模型的插槽。然后,对于每种可能的类型,您都会定义一个数据模板,为特定的视图模型类型定义其视图。然后要进行导航,您所要做的就是在元视图模型中更改当前视图模型。

这就是我脑海里想要去的地方,尽管我并不确定。我已经实现了 INotifyPropertyChanged 接口,因此可以使用它(另外在 viewModel 中我已经有了 IsDataLoaded 属性,也许我的想法是创建一个 event 并在 UI 中注册它,这样当登录完成时我只需要触发事件,UI 就会自动导航到另一个页面,而不用担心 IsDataLoaded 可能发生的所有情况)。非常感谢。 :) - mishan
1
好的,你可以使用事件,但通常人们也避免在视图中指定视图模型的确切类型(请注意,一般来说,将视图与视图模型耦合是可以的,而反过来则不行)。通常,你只需创建一些单用途的属性,然后间接通知视图。 - poke
这就是我所知道的,我想让我的视图知道viewmodel已经改变了,由于它依赖于在viewmodel中处理的异步操作的结果,我需要比使用已更改属性名称的propertychanged事件处理程序更好的方法,我认为最好的方法是在异步操作完成时引发一个事件告诉我这个特定的操作已经完成,并在视图中逻辑地处理该事件,因为视图负责导航,而viewmodel仅提供通知数据加载已完成的手段。 - mishan

1
我建议您查看棱镜http://compositewpf.codeplex.com/。这样可以让您在应用程序中发布事件或聚合命令,并轻松切换视图。您需要阅读相当多的文档,但这是值得的。现在我无法想象开发一个强大的mvvm应用程序没有它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接