我正在使用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*/
}
}
答案 0 :(得分:0)
基本上你应该避免将这些参数传递给ViewModels构造函数,因为使用Inversion of Control / Dependency Injection连接它会变得很麻烦。虽然您可以使用抽象工厂模式来解析具有运行时参数的对象,但它不适用于ViewModel。
相反,我总是建议使用一种导航模式,类似于Microsoft的模式&amp; amp;实践团队已经完成了Prism。在那里,您有一个INavigationAware
接口,您的ViewModel可以实现该接口。它有两种方法,NavigateTo
和NavigateFrom
。
还有导航服务。导航服务将切换视图,然后在当前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}}构造函数内部的操作没有锁定,在构造函数中执行此类操作非常不鼓励。)