MVVM: 如何将参数传递给 ViewModel 的构造函数

16
我正在使用L. Bugnion的MVVM Light框架。
有哪些推荐的方法可以将参数(例如客户ID)传递到ViewModel的构造函数中?
编辑: 我需要每个ViewModel的参数不是在模型之间共享的东西。它是每个视图模型实例都独有的东西。
5个回答

9
//Create a container class to pass via messenger service
    public class CarSelectedArgs
    {
      #region Declarations

      public Car Car { get; set; }

      #endregion

      #region Constructor

      public CarSelectedArgs(Car car)
      {
        Car = car;
      }

      #endregion
    }


    //example of view model sending message.
    public class SendingViewModel : ViewModelBase
    {
      private Car _car;
      public Car SelectedCar
      {
        get { return _car; }
        set
        {
          _car = value;
          if (value != null)
          {
            //messenger will notify all classes that have registered for a message of this type
            Messenger.Default.Send(new CarSelectedArgs(value));
          }
        }
      }
    }


    //Example of ViewModel registering to recieve a message
    public class SampleViewModel : ViewModelBase
    {
      #region Constructor

      public SampleViewModel()
      {
        Messenger.Default.Register<CarSelectedArgs>(this, OnCarSelected);
      }
      #endregion

      #region LocalMethods

      void OnCarSelected(CarSelectedArgs e)
      {

        var NewCar = e.Car;
      }

      #endregion
    }

3
对我来说,使用MVVM Light的整个重点是避免向视图模型的构造函数中注入任何内容。MVVM Light提供了一种消息传递机制,允许您将参数发送到在视图模型内注册的侦听器。例如,这是我在使用VSTO和WPF的WordWalkingStick项目中的视图模型:
using System;
using System.Xml.Linq;
using GalaSoft.MvvmLight.Messaging;

namespace Songhay.Wpf.WordWalkingStick.ViewModels
{
    using Songhay.Office2010.Word;
    using Songhay.OpenXml;
    using Songhay.OpenXml.Models;
    using Songhay.Wpf.Mvvm;
    using Songhay.Wpf.Mvvm.ViewModels;

    /// <summary>
    /// View Model for the default Client
    /// </summary>
    public class ClientViewModel : ViewModelBase
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ClientViewModel"/> class.
        /// </summary>
        public ClientViewModel()
        {
            if(base.IsInDesignMode)
            {
                #region

                this._flatOpcSourceString = ApplicationUtility
                    .LoadResource(
 new Uri("/Songhay.Wpf.WordWalkingStick;component/PackedFiles/FlatOpcToHtml.xml",
                         UriKind.Relative));
                this._xhtmlSourceString = ApplicationUtility
                    .LoadResource(
 new Uri("/Songhay.Wpf.WordWalkingStick;component/PackedFiles/FlatOpcToHtml.html", 
                         UriKind.Relative));

                #endregion
            }
            else
            {
                this._flatOpcSourceString = "Loading…";
                this._xhtmlSourceString = "Loading…";

                //Receive MvvmLight message:
                Messenger.Default.Register(this, 
                     new Action<GenericMessage<TransformationMessage>>(
                message =>
                {
                    var tempDocFolder = 
 Environment.ExpandEnvironmentVariables("%UserProfile%/Desktop/");
                    var inputPath = tempDocFolder + "temp.docx";
                    var outputPath = tempDocFolder + "temp.html";

                    var flatOpcDoc = 
                            XDocument.Parse(message.Content.TransformationResult);
                    OpenXmlUtility.TransformFlatToOpc(flatOpcDoc, inputPath);

                    this.FlatOpcSourceString = flatOpcDoc.Root.ToString();

                    var settings = new SonghayHtmlConverterSettings()
                    {
                        PageTitle = "My Page Title " + DateTime.Now.ToString("U"),
                        UseEntityMap = false
                    };

                    OpenXmlUtility.WriteHtmlFile(inputPath, outputPath, settings);

                    var xhtmlDoc = XDocument.Load(outputPath);
                    this.XhtmlSourceString = xhtmlDoc.Root.ToString();

                }));
            }
        }

        /// <summary>
        /// Gets or sets the flat opc source string.
        /// </summary>
        /// <value>The flat opc source string.</value>
        public string FlatOpcSourceString
        {
            get
            {
                return _flatOpcSourceString;
            }
            set
            {
                _flatOpcSourceString = value;
                base.RaisePropertyChanged("FlatOpcSourceString");
            }
        }

        /// <summary>
        /// Gets or sets the XHTML source string.
        /// </summary>
        /// <value>The XHTML source string.</value>
        public string XhtmlSourceString
        {
            get
            {
                return _xhtmlSourceString;
            }
            set
            {
                _xhtmlSourceString = value;
                base.RaisePropertyChanged("XhtmlSourceString");
            }
        }

        string _flatOpcSourceString;
        string _xhtmlSourceString;
    }
}

你可以看到,MVVM Light使用其Messenger向构造函数中传递值(而不是注入)进行消息传递(Messenger.Default.Register)。

我需要将参数发送到特定的ViewModel实例,例如客户详细信息ViewModel的客户ID。如何使用信使将消息发送到特定的ViewModel实例? - Yeonho
我不确定你的具体情况能否得到解决。看起来,消息传递可以驱动视图模型中的内部功能,这些功能可以以客户ID为驱动器。在我看来,消息传递不必意识到特定的实例,但消息调用的代码可以这样做。 - rasx
你应该能够使用令牌进行特定实例的消息传递,但个人建议注入参数而不是使用令牌。 - jpierson

2

通过注入,使用接口请求任何您想要的内容。

如果您在多个模型之间共享设置,请实例化一个包含这些值的单例,并通过 ISomethingProviderISomethingEditor 接口公开它们。


感谢您的回复。然而,我需要每个ViewModel的参数并不是跨模型共享的。它是每个viewmodel实例独有的,例如客户详细信息ViewModel的客户ID。 - Yeonho

1
这是我所做的事情:
ViewModel需要显示带有传递参数的汽车ID的汽车窗口:
ViewModel ->向CodeBehind发送消息,以便打开窗口。消息发送ID。
本质上,在代码后台:
var vm = New ViewModel(ID); var view = new view(); view.DataContext = VM; 视图展示();
我的ViewModel具有接受ID的构造函数。

如果您选择“Light”路线,我认为这是最好的选择。除此之外,我建议您仅在设计模式下将该类注册到ServiceLocator中,以提供设计时数据。 - N Jones

0
在撰写针对视图模型的测试时,我有时会创建一个带有参数ISomething的视图模型构造函数重载。我会让默认构造函数调用第二个构造函数,并使用ISomething的默认实现。在测试中,我会使用测试实现调用构造函数。我知道这不是最好的方法,因为它会在两个类之间创建依赖关系......但有时你得走捷径......
public class SomeViewModel
{
  private ISomething internalSomething;

  public void SomeViewModel():this(new Something()){}

  public void SomeViewModel(ISomething something)
  {
    this.internalSomething = something;
  }    
}

更新

在XAML中创建视图可以像这样:

<UserControl xmlns="...."
             xmlns:Example="SomeNamespace">

  <UserControl.DataContext>
     <Example:SomeViewModel />
  </UserControl.DataContext>

  <Grid>
     ...
  </Grid>
</UserControl>

在运行时如何实例化ViewModel,并将其与其视图配对? - Yeonho
如果使用MvvmLight中的ModelViewLocator,则不需要进行任何更改。对于非常简单的项目,我有时会在xaml中创建viewmodel。 我会将其添加到帖子中... - Sorskoot

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