使用ViewModelLocator抓取其他虚拟机用于另一个虚拟机是不是很糟糕?

时间:2013-07-15 16:35:41

标签: windows-phone-7 mvvm-light

我正在使用MVVM灯,因为ViewModelLocator可用于抓取任何视图模型,因此我可以使用它来获取值。

我一直在做这样的事情

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

这样,如果我需要在视图之间进行操作,我可以轻松获得其他值。我不确定这是不是最好的做法(看起来很方便让我怀疑这是不好的做法大声笑)因为我知道有一些信使类的东西而且不起诉,如果这是我应该这样做的方式。 / p>

修改

  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 个答案:

答案 0 :(得分:1)

是的,您可以这样做,因为代码可以正常工作,但是您可能会遇到一个很大的潜在问题。

使用MVVM模式的一个强有力的论据是,它可以更容易地编写易于测试的代码。
如果您使用的是上述代码,则无法使用ViewModel1ViewModelLocator来测试ViewModel2。可能这本身并不是一件坏事,但你已经开创了这种类型的强类耦合是可以接受的先例。将来,当你

时会发生什么

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

这可能意味着你有一个像这样的构造函数:

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

你这样称呼:

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

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

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

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

<强>更新

如果您在第一个视图/ vm中捕获商店,那么为什么不从第二个视图将其(ID&amp; /或Name)传递给第二个VM?然后,第二个VM可以将其发送到服务器,并在第二个视图中捕获数据。

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

答案 1 :(得分:1)

我正在尝试了解您的方案是什么。在MVVMlight论坛中,您为此问题添加了以下上下文:

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

为了在VM之间传递数据,我也 - 就像上面的Matt一样 - 使用MVVMLight的Messenger类,只要它是“火上浇油”。但这可能是“可能又回来了”的评论听起来很棘手。

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

但这只是一个案例。 所以也许如果你能提供更多的背景,我会很乐意尝试帮助。

答案 2 :(得分:1)

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

以下是一些仍在使用SimpleIoc

的示例代码
public ViewModelLocator() 
{  
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);                        
    SimpleIoc.Default.Register<IMyClass, MyClass>();              
}  

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

以下是SimpleIOC的评论 - how to use MVVMLight SimpleIoc?

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

这是我使用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。

答案 3 :(得分:0)

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

因此,避免这种情况的一种方法是考虑依赖注入。这将使您的依赖项显式,同时保持可测试性。另一个选择是使用你也提到的MVVMLight的Messenger类。

为了在MVVM的上下文中编写可维护和可测试的代码,ViewModels应尽可能松散耦合。这是MVVMLight的Messenger可以提供帮助的地方。这是关于Messenger类的目的的quote from Laurent

  

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

所以,为了回答你提到的更具体的场景,一个视图弹出另一个“商店选择”视图,后者必须在返回第一个视图时设置当前商店,这是一种方法( “信使方式”):

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

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

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

你可能会争辩说这是来回传递的很多信息,但它会使视图模型和视图分离,并且不需要很多代码。

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

基于服务的方法: 对于更基于服务的方法,请查看由MVVMLight的发明者撰写的最近的MSDN Magazine article。它提供了两种方法的代码示例:Messenger方法和基于DialogService的方法。但是,它不会详细介绍如何从对话框窗口中获取值。

this article中解决了这个问题,而不依赖于Messenger。注意IModalDialogService接口:

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

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

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

我希望这会进一步帮助你!