3

我正在使用 MVVM light 并发现 ViewModelLocator 可用于抓取任何视图模型,因此我可以使用它来抓取值。

我一直在做这样的事情

public class ViewModel1
{
   public ViewModel1()
   {
        var vm2 = new ViewModelLocator().ViewModel2;
        string name = vm2.Name;
   }
}

这样,如果我需要在视图之间切换,我可以轻松获得其他值。我不确定这是否是最佳做法(这似乎很方便,让我怀疑这是否是不好的做法哈哈),因为我知道有一些信使类的东西,不起诉我是否应该这样做。

编辑

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

            SimpleIoc.Default.Register<ViewModel1>();
SimpleIoc.Default.Register<ViewModel2>();
        }


  [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
         "CA1822:MarkMembersAsStatic",
         Justification = "This non-static member is needed for data binding purposes.")]
        public ViewModel1 ViewModel1
        {
            get
            {
                return ServiceLocator.Current.GetInstance<ViewModel1 >();
            }
        }

编辑

这是我试图解决的一个场景。

我有一个观点,你可以添加价格和商店名称。当您单击商店名称的文本框时,您将被转移到另一个视图。此视图有一个文本框,您可以在其中键入您要查找的商店,当您键入一个选择列表时,将填充所有可能的匹配项和有关该商店的信息。

然后用户选择他们想要的商店。他们被转移回他们“添加价格”的视图,现在商店名称也被填写了。

如果他们点击“添加”按钮,它将获取价格、商店名称和条形码(来自“添加价格视图”之前的视图)并发送到服务器。

如您所见,我需要来自不同视图的数据。

4

4 回答 4

1

是的,您可以这样做,只要代码可以工作,但您将来可能会遇到一个很大的潜在问题。

使用 MVVM 模式的有力论据之一是它使编写易于测试的代码变得更加容易。有了上面的代码,没有and
就无法测试。可能这本身并不是一件坏事,但您已经开创了一个先例,即这种类型的强耦合类是可以接受的。将来会发生什么,当你ViewModel1ViewModelLocatorViewModel2

从测试的角度来看,您可能会从能够注入您的依赖项中受益。这意味着将您需要的信息的外部对象传递给构造函数。

这可能意味着您有这样的构造函数:

public ViewModel1(string vm2Name)
{
    string name = vm2Name;
}

你这样称呼:

var vm1 = new ViewModel1(ViewModelLocator.ViewModel2.name);

您可能还需要考虑其他一些问题。

您还创建了一个新ViewModelLocator的来访问它的属性之一。您可能已经在应用程序级别定义了定位器的实例。如果您正在更新额外的、不必要的实例,那么您正在为自己(和处理器)创造更多的工作。

ViewModel2如果您只需要名称,您真的需要一个完整的实例吗?避免创建和传递超出您需要的内容。

更新

如果您在第一个视图/虚拟机中捕获存储,那么为什么不将该(ID 和/或名称)从第二个视图传递给第二个虚拟机?然后,第二个 VM 可以将其与在第二个视图中捕获的数据一起发送到服务器。

另一种方法可能是只为两个视图使用一个视图模型。这可能会让你的整个问题消失。

于 2013-07-17T13:45:49.527 回答
1

如果您在 1 个视图或视图模型中有需要由第二个(或其他)视图或视图模型访问的属性,我建议创建一个新类来存储这些共享属性,然后将此类注入每个视图模型(或通过定位器访问它)。在这里查看我的答案......两个视图 - 一个 ViewModel

这是一些仍在使用 SimpleIoc 的示例代码

public ViewModelLocator() 
{  
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);                        
    SimpleIoc.Default.Register<IMyClass, MyClass>();              
}  

public IMyClass MyClassInstance
{
    get{ return ServiceLocator.Current.GetInstance<IMyClass>();}
} 

这里是对 SimpleIOC 的回顾——如何使用 MVVMLight SimpleIoc?

但是,正如我在评论中提到的,我改用 Autofac 容器,以便我的支持/共享类可以注入多个视图模型。这样我就不需要实例化定位器来访问共享类。我相信这是一个更清洁的解决方案。

这就是我使用 Autofac 容器注册 MyClass 和 ViewModels 的方式-

var builder = new ContainerBuilder();
var myClass = new MyClass();
builder.RegisterInstance(myClass);
builder.RegisterType<ViewModel1>();
builder.RegisterType<ViewModel2>();
_container = builder.Build();
ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(_container));   

然后每个需要 MyClass 实例的 ViewModel (ViewModel1, ViewModel2) 只需将其添加为我最初链接的构造函数参数。

MyClass 将根据需要为其属性实现 PropertyChanged。

于 2013-08-07T18:26:18.367 回答
1

我正在尝试了解您的情况。在 MVVMlight 论坛中,您向该问题添加了以下上下文:

“我有一些数据需要传递到多个屏幕并可能再次返回。”

为了在虚拟机之间传递数据,我也会 - 正如上面的马特 - 使用 MVVMLight 的 Messenger 类,只要它是“即发即弃”。但听起来很棘手的是“可能再次回来”的评论。

我可以想象一些可能需要这样做的场景。例如。向导界面。在这种情况下,我将对向导负责收集的数据进行建模,然后将所有视图绑定到同一个 VM,以表示该模型对象。

但这只是一种情况。因此,如果您可以提供更多背景信息,我将很乐意尝试提供帮助。

于 2013-08-01T22:25:30.903 回答
0

好的,我首先回答您的原始问题是:是的,我认为从另一个 VM 访问一个 VM 是不好的,至少在这个问题的代码示例中是这样。出于与马特相同的原因 - 可维护性和可测试性。通过以这种方式“更新”另一个 ViewModelLocator,您可以将依赖项硬编码到您的视图模型中。

因此,避免这种情况的一种方法是考虑依赖注入。这将使您的依赖关系明确,同时保持可测试性。另一种选择是使用您还提到的 MVVMLight 的 Messenger 类。

为了在 MVVM 的上下文中编写可维护和可测试的代码,ViewModel 应该尽可能松耦合。这就是 MVVMLight 的 Messenger 可以提供帮助的地方。这是Laurent关于 Messenger 类的用途的引述:

我在必须进行解耦通信的地方使用它。通常我在 VM 和视图之间以及 VM 和 VM 之间使用它。严格来说,你可以在多个地方使用它,但我总是建议人们小心使用它。它是一个强大的工具,但由于非常松散的耦合,很容易失去对您正在做的事情的概述。在有意义的地方使用它,但不要用消息替换所有事件和命令。

因此,要回答您提到的更具体的场景,其中一个视图会弹出另一个“商店选择”视图,而后者必须在返回第一个视图时设置当前商店,这是一种方法(“Messenger 方式"):

1) 在第一个视图中,在第一个视图的 TextBox 上使用来自 MVVMLight 的 EventToCommand 将所需的事件(例如 GotFocus)绑定到视图模型公开的命令。可能是例如。命名为 OpenStoreSelectorCommand。

2) OpenStoreSelectorCommand 使用 Messenger 发送消息,请求打开 Store Selector 对话框。StoreSelectorView(弹出视图)订阅此消息(向 Messenger 注册该类型的消息)并打开对话框。

3) 当视图关闭并选择了新商店时,它会再次使用 Messenger 发布当前商店已更改的消息。主视图模型订阅此消息,并且可以在收到消息时采取任何所需的操作。例如。更新 CurrentStore 属性,该属性绑定到主视图上的字段。

您可能会争辩说这是来回传递大量消息,但它使视图模型和视图保持解耦,并且不需要大量代码。

这是一种方法。正如马特暗示的那样,这可能是“旧式”,但它会起作用,并且比创建硬依赖更好。

基于服务的方法:有关更多基于服务的方法,请查看最近的MSDN 杂志文章,该文章由 MVVMLight 的发明者撰写。它提供了两种方法的代码示例:Messenger 方法和基于 DialogService 的方法。但是,它没有详细说明如何从对话窗口中获取值。

在这篇文章中,这个问题在不依赖 Messenger 的情况下得到了解决。注意 IModalDialogService 接口:

public interface IModalDialogService
{
  void ShowDialog<TViewModel>(IModalWindow view, TViewModel viewModel, Action<TViewModel> onDialogClose);

  void ShowDialog<TDialogViewModel>(IModalWindow view, TDialogViewModel viewModel);
}

第一个重载有一个 Action 委托参数,该参数作为对话框的 Close 事件的事件处理程序附加。委托的参数 TViewModel 设置为对话窗口的 DataContext。最终结果是导致对话框最初显示的视图模型可以在对话框关闭时访问(更新的)对话框的视图模型。

希望对您有所帮助!

于 2013-08-05T10:11:27.373 回答