MVVM 模式,我不明白我应该如何编写 "Detail" ViewModel

MVVM Pattern, I don't understand how should I code a "Detail" ViewModel

假设我有一个显示通用客户列表的视图。在这种情况下,我将实现一个 CustomersViewModel,其中 RelayCommand 绑定到 XAML 按钮,下载它并填充 CustomerObservableCollection,绑定到ListView.

如果我想定义一个 CustomerDetailView 视图来显示有关客户的一些其他信息,我会创建一个 CustomerDetailViewModel、一个 CustomerView,然后重复相同的逻辑.但不同的是,ViewModel 必须将选择的客户作为参数,而 CustomersViewModel 可以在没有外部参数的情况下每次显示。 在 WinForm 解决方案中,我会放置一个参数来构造我需要的对象的形式。

我的问题是:为了尊重 MVVM 模式,实现此类导航的正确方法是什么?

我的导航逻辑:

static ViewModelLocator()
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

    var nav = new NavigationService();
    nav.Configure("CustomersView", typeof(CustomersView));
    nav.Configure("CustomerDetailView", typeof(CustomerDetailView));

    SimpleIoc.Default.Register<INavigationService>(() => nav);

    SimpleIoc.Default.Register<CustomersViewModel>();
    SimpleIoc.Default.Register<CustomerDetailViewModel>();
}

private RelayCommand _navigateToCustomerDetailCommand;
public RelayCommand NavigateToCustomerDetailCommand
{
    get
    {
        return _navigateToCustomerDetailCommand
            ?? (_navigateToCustomerDetailCommand = new RelayCommand(
            () =>
            {
                _navigationService.NavigateTo("CustomerDetail");
            }
    {
}

我想的选项:

  1. 以某种方式将参数传递给"NavigateTo"函数,并在构造函数中定义具有该参数的相对ViewModel。我没有找到办法做到这一点,虽然我觉得这很合理。

  2. 如上导航,然后向 ViewModel 发送消息。这个想法是可行的,但我不相信它。在我看来,这似乎是一种将简单的事情复杂化的方法。

        return _navigateToCustomerDetailCommand
            ?? (_navigateToCustomerDetailCommand = new RelayCommand(
            () =>
            {
                _navigationService.NavigateTo("CustomerDetail");
                Messenger.Default.Send(new CustomMessageCustomerSelected(SelectedCustomer));            
            }
    

    然后在 ViewModel 中收听该消息:

    public CustomerDetailViewModel(NavigationService _navigationService)
    {
        // ...
                    Messenger.Default.Register<CustomMessageCustomerSelected>
                    (
                        this,
                        (action) =>
                        {
                            SelectedCustomer = action.Value;
                        }
                    );
        // ...
    
    }
    

这两种选择都是合适的方法。您应该使用哪一个取决于您的应用程序的 UI 和工作流程。具体来说,您如何访问详细信息视图?例如,如果您单击一个按钮打开一个新的 "screen"(启动一个全新的视图来替换用户正在查看的任何内容),那么您应该使用选项 1。如果您有一个客户列表 side-by-side 使用详细信息视图,单击新客户时视图应该更新,然后使用选项 2。

要实现第一个选项,您需要使用 SimpleIoc 控制反转容器和 ViewModelLocator class。您使用定位器注册您的 ViewModel,然后该定位器用于处理对新 VM 的请求并调用它们的构造函数。这意味着您可以轻松地发送包含客户作为参数的消息,然后将其提供给 VM 的构造函数。 Here is a decent introduction to ViewModelLocator, albeit a few years old, and here is a quick and dirty intro to SimpleIoc.

顺便说一句,如果您要有多个并发用户(尤其是选项 2),我建议发送客户的 ID,而不是对象。这意味着您必须从存储中获取它,这对性能影响不大,但也意味着您的用户在打开视图时始终获得最新数据,从而减少了编辑冲突。