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

C# 将参数传递给ViewModel中的构造函数,c#,wpf,mvvm,dependency-injection,C#,Wpf,Mvvm,Dependency Injection,我正在用MVVM模式构建一个WPF浏览器应用程序 我有一个带有数据网格的第一页(ConsultInvoice)。当我双击其中一行时,我想导航到另一个页面(EditInvoice),将参数中的选定行传递给我的构造函数 我知道如果我想把事情做好,我应该使用依赖注入,但我真的不知道如何在这里使用它 我如何简单地传递这个构造函数 咨询VoiceViewModel private Invoice _selected; public Invoice Selected { get {

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

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

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

我如何简单地传递这个构造函数

咨询VoiceViewModel

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()));
    }
}
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*/
    }
}
EditInvoiceViewModel

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()));
    }
}
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*/
    }
}
公共类EditInvoiceViewModel:ViewModelBase { 公共上下文ctx=新上下文(); 发票; 预发票预发票; #区域属性 私人收藏; 公开收集 { 得到 { 回收; } 设置 { 收集=价值; 不动产变更(“收款”); } } #端区 public EditInvoiceViewModel(发票库存) { /*做事*/ } }
基本上,您应该避免将此类参数传递到ViewModels构造函数中,因为将其与控制反转/依赖注入相连接会让人感到痛苦。虽然您可以使用抽象工厂模式来解析带有运行时参数的对象,但它并不适用于ViewModels

相反,我总是建议使用一种形式的导航模式,类似于微软的模式与实践团队使用Prism所做的。在这里,您可以实现一个
INavigationAware
界面。它有两种方法,
NavigateTo
NavigateFrom

还有导航服务。导航服务将切换视图,然后在当前视图模型中切换调用
NavigateFrom
(如果它实现了它。可以使用它检查是否保存了数据,如有必要,取消导航。加载新视图并为其分配ViewModel后,在新导航的ViewModel中调用
NavigateTo

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

我以前的回答中的一个基本实现(可以找到):

INavigationService公共接口
{
//T是基础ViewModel类的名称
void NavigateTo(),其中T ViewModel;
void NavigateToNewWindow();
void NavigateToNewWindow(对象参数);
void NavigateTo(对象参数);
}
公共类导航服务:INavigationService
{
专用IUnityContainer容器;
公共导航服务(IUnityContainer容器)
{
this.container=容器;
}
public void NavigateToWindow(对象参数),其中T:IView
{
//配置IoC容器以解析给定ViewModel的视图
//即容器。在您的
//成分根
IView view=container.Resolve();
视窗视窗=视窗;
如果(窗口!=null)
window.Show();
INavigationAware导航=查看为INavigationAware;
如果(导航!=null)
导航到(参数);
}
}
//IPlotView是一个空接口,仅用于能够解析
//PlotWindow不需要参考其具体实现,如下所示
//调用navigationService.NavigateToWindow(userId);将违反
//MVVM模式,其中navigationService.NavigateToWindow(userId)不支持。还有其他涉及字符串或命名的方法
//常规,但这超出了这个答案的范围。我会的
//只需实现“objectdatacontext{get;set;}”属性,该属性已经存在
//实现的控制对象
公共类绘图窗口:窗口、IView、IPlotView
{
}
公共类PlotViewModel:ViewModel、INotifyPropertyChanged、INavigationAware
{
私有int plotId;
public void导航到(对象参数),其中T:IView
{
如果(!参数为int)
return;//传递了错误的参数类型
this.plotId=(int)参数;
任务。开始(()=>{
//加载数据
PlotData=加载图(plotId);
});
}
私有绘图数据;
公共绘图数据{
获取{return plotData;}
设置
{
if(plotData!=值)
{
plotData=值;
OnPropertyChanged(“绘图数据”);
}
}
}
}
Prism中使用的
INavigationAware
接口示例可在上找到


这使得传递参数和
async
加载数据变得很容易(没有任何干净的方法通过构造函数来实现,因为你不能
等待
构造函数中的
async
操作而不锁定,在构造函数中做这种事情是非常不受欢迎的).

可能只有我一个人,但我有点困惑。您不是已经在ViewModel(ConsultInvoiceViewModel)中向构造函数(EditInvoiceViewModel(Invoice inv))传递了一个参数(选定的)吗?你到底在问什么?@Rowbear是的,但目标是打开一个新的EditInvoiceView.xaml,并且它只接受一个默认构造函数。所以我试图做的不起作用。啊!我明白了。你打算让EditInvoiceView显示为一个弹出窗口还是一个新窗口?你打算如何控制EditInvoiceView的显示?仅使用.show()/.ShowDialog()?ConsultInvoiceViewModel中所有可能的发票共享一个视图有意义吗?或者您可以一次编辑多个发票吗?@Rowbear,我计划将其显示为一个新窗口,但弹出窗口也可以。在我不知道如何导航之后。这就是重点。在我的其他页面之间,我使用Hyperlink.B