MVVM Light,Windows Phone,页面之间的视图和视图模型导航

4

我有一个页面,基本上你可以选择一组选项(配置),然后进入下一页,在那里你可以做一些事情。
使用MVVM Light工具包,我有一个视图模型绑定到第一页的视图。当用户点击按钮时,它会重定向到另一个视图,也就是第二个页面。
例如:

Page2Command = new DelegateCommand((obj) => 
    Messenger.Default.Send<Uri>(new Uri("/DoStuffView.xaml", UriKind.Relative), 
                                Common.CommonResources.GoToDoStuffRequest)) });

问题是,第二个视图的视图模型(在我看来)在构造函数中有几个参数,这些参数基本上是第一页设置的配置所依赖的。

例如:

public DoStuffViewModel(ICollection<Note> availableNotes, SoundMappers soundType)
{
}

问题在于如何使用用户在第一页上动态选择的数据来实例化视图模型?由于这些视图模型没有任何依赖关系,它们只是单独存在(或者可以从数据库、文件或其他地方检索数据,但它们没有任何动态输入数据),因此我无法使用MVVM Light提供的ViewModelLocator模式。我可以通过视图的构造函数来完成它,实例化视图模型,并将新创建的视图模型分配给视图的DataSource,但我认为这样做不太好。有什么建议吗?
3个回答

3

根据您使用Messenger类发送消息的情况,我认为您熟悉MVVM Light中的消息传递。您需要定义自己的消息类型,该类型应该从页面1接受您的参数:

    public class Page2ViewModelCreateMessage : MessageBase
{
    public ICollection<Note> AvailableNotes{get;set;}
    public SoundMappers SoundType{get;set;}

    public Page2ViewModelCreateMessage ()
    {

    }

    public Page2ViewModelCreateMessage(ICollection<Note> availableNotes, SoundMappers soundType)
    {
        this.AvailableNotes = availableNotes;
        this.SoundType = soundType;
    }
}

您需要发送一个 Page2ViewModelCreateMessage 实例,其中包含您的参数,并在导航时发送它:

var message = new Page2ViewModelCreateMessage(myAvailableNotes, mySoundType)
Messenger.Default.Send(message);

在第二页,您需要注册以接收类型为Page2ViewModelCreateMessage的消息。
        Messenger.Default.Register<Page2ViewModelCreateMessage>(this, OnPage2ViewModelCreateMessage);
    ..
    public void OnPage2ViewModelCreateMessage(Page2ViewModelCreateMessage message)
    {
        var page2ViewModel = new Page2ViewModel(messsage.AvailableNotes, message.SoundType);
    }   

正如您所看到的,我已经将您的DoStuffViewModel替换为Page2ViewModel,以使其更加清晰。

我希望这会对您有所帮助。

注意:我不能保证代码能够正常运行,因为它是在记事本中编写的。


+1消息是一种很好的一致的方法来分离ViewModel,尽管值得说明的是,这是众多可用选项之一。 - Adam Houldsworth
你好Todor。如果我没记错的话,OnPage2ViewModelCreateMessage方法在Page2View上。所以最终,视图将创建视图模型,并将其分配给其DataSource属性。这正确吗?虽然消息系统发送参数进行创建而不是视图实例化它们,但我不确定视图是否应该创建视图模型。 - Daniel Perez
嗨,丹尼尔。你说得没错。OnPage2ViewMNodelCreateMessage方法在Page2View上,ViewModel是在那里创建的。另一个选项是使用ViewModelLocator来获取ViewModel并将其引用到View.DataContext中。但是第一种选项不会“太过”破坏MVVM模式。 - tbmihailov

1
我实现这个的方法是有一个中央控制器类,所有的ViewModel都通过接口知道它。在让手机为我执行导航之前,我会将状态设置到其中。然后每个ViewModel都会查询这个中央类以获取它所需的状态。
对我来说,这样做有很多好处:
  • 它允许我拥有非静态的ViewModel。
  • 我可以使用Ninject注入控制器类的具体实现,并将其作为单例范围。
  • 最重要的是,在墓碑模式下,我只需要获取当前的ViewModel和控制器类。
我遇到了一个消息传递的问题,我的ViewModel是注册的监听器,因为我是View First而不是ViewModel First,所以我被迫使用静态的ViewModel引用。否则,ViewModel无法及时创建以接收消息。
我与消息一起使用控制器类(它基本上是UI周围所有消息的接收者),因此如果未来进行重构,我不需要改变太多,只需要更改消息的接收者即可。
想想看,控制器类也是我的导航汇聚点 - 因为我有一些自定义导航代码,可以跳过某些页面的返回分页等。
以下是我当前设置的示例:
public interface IController
{
    Foo SelectedFoo { get; }
}

public class ViewModel
{
    private IController _controller;

    public ViewModel(IController controller)
    {
        _controller = controller;
    }

    private void LoadData()
    {
        // Using selected foo, we load the bars.
        var bars = LoadBars(_controller.SelectedFoo);
    }
}

虽然您正在注入依赖项,但您的ViewModels不应从其他地方获取数据,因为这会破坏VM的分离和可测试性。通常我们让控制器管理VM并将值传递给它们,而不是让VM从外部获取值。VM应尽可能无知。这只是一个观点,随意忽略 :) - iCollect.it Ltd
2
有时候 ViewModel 需要某些东西是合理的。我认为,让 ViewModel 获得信息源接口和让 ViewModel 依赖于先前接收到的消息之间没有区别。至于测试,可以使用模拟接口进行测试...因此它完全可测试。 - Adam Houldsworth

0
你可以使用 PhoneApplicationService 字典来保存从第一个事件导航时需要的数据,并在导航到第二个页面时解析它。你也可以在你的 ViewModels 中使用这些数据。 类似于这样:
 PhoneApplicationService.Current.State["DatatFromFirstPage"] = data;

当导航到第二页时:

 if (PhoneApplicationService.Current.State.ContainsKey("DatatFromFirstPage"))
 {
      var dataUsedOnSeconPage= PhoneApplicationService.Current.State["DatatFromFirstPage"];
 }

你可以在整个应用程序中全局使用这些数据


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