将参数传递给ViewModel中的构造函数

时间:2015-11-12 06:06:31

标签: c# wpf mvvm dependency-injection

我正在使用MVVM模式构建WPF浏览器应用程序。

我有一个带有dataGrid的第一页(ConsultInvoice)。当我双击其中一行时,我想导航到另一个页面(EditInvoice),将参数中的选定行传递给我的构造函数。

我知道如果我想做正确的事我应该使用依赖注入,但我真的不知道如何在这里使用它。

我怎样才能简单地传递这个构造函数?

ConsultInvoiceViewModel

private Invoice _selected;
public Invoice Selected
{
    get
    {
        return _selected;
    }
    set
    {
        _selected = value;
        OnPropertyChanged("Selected");
    }
}

private void Edit()
{
    EditInvoiceViewModel editInvoice = new EditInvoiceViewModel(Selected); 
   /* doing something here*/
}

public ICommand EditCommand
{
    get
    {
        return editCommand ?? (editCommand = new RelayCommand(p => this.Edit(), p => this.CanEdit()));
    }
}

EditInvoiceViewModel

public class EditInvoiceViewModel : ViewModelBase
{
    public Context ctx = new Context();
    Invoice invoice;
    PreInvoice preInvoice;
    #region properties
    private ObservableCollection<PreInvoice> collection;
    public ObservableCollection<PreInvoice> Collection
    {
        get
        {
            return collection;
        }
        set
        {
            collection = value;
            OnPropertyChanged("Collection");
        }
    }
    #endregion
    public EditInvoiceViewModel(Invoice inv)
    {
        /* do stuff*/
    }
}

1 个答案:

答案 0 :(得分:0)

基本上你应该避免将这些参数传递给ViewModels构造函数,因为使用Inversion of Control / Dependency Injection连接它会变得很麻烦。虽然您可以使用抽象工厂模式来解析具有运行时参数的对象,但它不适用于ViewModel。

相反,我总是建议使用一种导航模式,类似于Microsoft的模式&amp; amp;实践团队已经完成了Prism。在那里,您有一个INavigationAware接口,您的ViewModel可以实现该接口。它有两种方法,NavigateToNavigateFrom

还有导航服务。导航服务将切换视图,然后在当前ViewModel中切换调用NavigateFrom之前(如果它实现了它。可以使用它来检查数据是否已保存,如果需要,取消导航。新视图加载后)以及分配给它的ViewModel,在新导航的ViewModel中调用NavigateTo

在这种情况下,您可以传递ViewModel所需的参数invoiceId。尽量避免传递整个模型或复杂对象。使用invoiceid获取发票数据并填充编辑的ViewModel。

我以前的答案中的basinc实现(可以找到here):

public interface INavigationService 
{
    // T is whatever your base ViewModel class is called
    void NavigateTo<T>() where T ViewModel;
    void NavigateToNewWindow<T>();
    void NavigateToNewWindow<T>(object parameter);
    void NavigateTo<T>(object parameter);
}

public class NavigationService : INavigationService
{
    private IUnityContainer container;
    public NavigationService(IUnityContainer container) 
    {
        this.container = container;
    }
    public void NavigateToWindow<T>(object parameter) where T : IView
    {
        // configure your IoC container to resolve a View for a given ViewModel
        // i.e. container.Register<IPlotView, PlotWindow>(); in your
        // composition root
        IView view = container.Resolve<T>();

        Window window = view as Window;
        if(window!=null)
            window.Show();

        INavigationAware nav = view as INavigationAware;
        if(nav!= null)
            nav.NavigatedTo(parameter);
    }
}

// IPlotView is an empty interface, only used to be able to resolve
// the PlotWindow w/o needing to reference to it's concrete implementation as
// calling navigationService.NavigateToWindow<PlotWindow>(userId); would violate 
// MVVM pattern, where navigationService.NavigateToWindow<IPlotWindow>(userId); doesn't. There are also other ways involving strings or naming
// convention, but this is out of scope for this answer. IView would 
// just implement "object DataContext { get; set; }" property, which is already
// implemented Control objects
public class PlotWindow : Window, IView, IPlotView
{
}

public class PlotViewModel : ViewModel, INotifyPropertyChanged, INavigationAware
{
    private int plotId;
    public void NavigatedTo(object parameter) where T : IView
    {
        if(!parameter is int)
            return; // Wrong parameter type passed

        this.plotId = (int)parameter;
        Task.Start( () => {
            // load the data
            PlotData = LoadPlot(plotId);
        });
    }

    private Plot plotData;
    public Plot PlotData {
        get { return plotData; }
        set 
        {
            if(plotData != value) 
            {
                plotData = value;
                OnPropertyChanged("PlotData");
            }
        }
    }
}

可以在projects github repository上找到Prism中使用的INavigationAware接口的示例。

这样可以轻松传递参数并async加载您的数据(没有任何干净的方式通过构造函数执行此操作,因为您可以await { {1}}构造函数内部的操作没有锁定,在构造函数中执行此类操作非常不鼓励。)