问题描述
我建设有MVVM模式一个WPF浏览器应用程序。
我有一个第一页(ConsultInvoice)有一个DataGrid。当我双击我想转到另一个页面(EditInvoice)的参数选定行传递给我的构造函数行之一。
我知道,如果我想要做的正确的事情,我应该使用依赖注入,但我实在不明白如何在这里使用它。
我怎么能简单地通过这个构造?
ConsultInvoiceViewModel
专用发票_selected;
公共发票选择
{
得到
{
返回_selected;
}
组
{
_selected =价值;
OnPropertyChanged(选定);
}
}
私人无效编辑()
{
EditInvoiceViewModel editInvoice =新EditInvoiceViewModel(选择);
/ *在这里做一些事情* /
}
公共ICommand的EditCommand
{
得到
{
返回editCommand? (editCommand =新RelayCommand(p值=> this.Edit()中,p => this.CanEdit()));
}
}
EditInvoiceViewModel
公共类EditInvoiceViewModel:ViewModelBase
{
公共语境环磷酰胺=新的上下文();
发票的发票;
PreInvoice preInvoice;
#地区的物业
私人的ObservableCollection< PreInvoice>采集;
公众的ObservableCollection< PreInvoice>收集
{
得到
{
返回收集;
}
组
{
=收藏价值;
OnPropertyChanged(集合);
}
}
#endregion
公共EditInvoiceViewModel(发票INV)
{
/ *做的东西* /
}
}
基本上,你应该避免这样的参数传递到的ViewModels构造,与控制/依赖注入反转接线就变成了痛苦。虽然你可以使用抽象工厂模式来解决与运行参数的对象,这恕我直言,不适合的ViewModels。
相反,我总是建议使用导航模式的一种形式,类似于如何微软的模式与放大器;实践团队与棱镜完成。有你有哪些您的ViewModels可以实现一个 INavigationAware
接口。它有2种方法,的NavigateTo
和 NavigateFrom
。
和有一个导航服务。导航服务将切换的意见和切换调用 NavigateFrom
当前视图模型(如果它实现它。我们可以用它来检查,如果数据被保存,必要时取消前导航后新的View已经加载和视图模型分配给它,调用的NavigateTo
在新导航视图模型。
下面你会传入的视图模型所需的参数,你的情况 invoiceId
。尽量避免将整个模型或复杂的对象。使用 invoiceid
来取发票数据并填充您的编辑视图模型。
这是我以前的答案一个basinc实现(可以发现的):
公共接口INavigationService
{
// T是无论你基地的ViewModel类被称为
无效的NavigateTo< T>(),其中T视图模型;
无效NavigateToNewWindow< T>();
无效NavigateToNewWindow< T>(对象参数);
无效的NavigateTo< T>(对象参数);
}
公共类的NavigationService:INavigationService
{
私人IUnityContainer容器;
公众的NavigationService(IUnityContainer容器)
{
this.container =容器;
}
公共无效NavigateToWindow< T>(对象参数)其中T:IVIEW
{
//配置您的IoC容器来解决一个给定的视图模型
视图//即container.Register< IPlotView,PlotWindow>();在
//成分根
IVIEW视图= container.Resolve< T>();
窗口窗口=视图窗口;
如果(!=窗口NULL)
window.Show();
INavigationAware NAV =视图INavigationAware;
如果(NAV!= NULL)
nav.NavigatedTo(参数);
}
}
// IPlotView是一个空的接口,仅用于能够解决
//将PlotWindow W / O需要引用到它的具体实现作为
//调用navigationService.NavigateToWindow< PlotWindow>(用户ID);违反
// MVVM模式,其中navigationService.NavigateToWindow< IPlotWindow>(用户ID);不。也有涉及字符串或命名
//约定等方式,但这是超出范围的这个答案。 IVIEW将
//只实现对象的DataContext {获取;集;}属性,这已经是
//落实防治对象
公共类PlotWindow:窗口,IVIEW,IPlotView
{
}
公共类PlotViewModel:视图模型,INotifyPropertyChanged的,INavigationAware
{
私人诠释plotId;
公共无效NavigatedTo(对象参数),其中T:IVIEW
{
如果(!参数为int)
的回报; //错误的参数类型通过
this.plotId =(INT)参数;
Task.Start(()=> {
//加载数据
PlotData = LoadPlot(plotId);
});
}
自留地plotData;
公共地块PlotData {
{返回plotData; }
组
{
如果(plotData =价值!)
{
plotData =价值;
OnPropertyChanged(PlotData);
}
}
}
}
这是Prism中使用了 INavigationAware
接口的例子可以在的。
这可以很容易地通过参数和异步
加载数据(那里没有任何清晰的方式通过构造函数来做到这一点,因为你不能等待
的异步
构造函数中的操作无锁定,并在构造函数做这种东西的非常灰心)。
I am building a WPF browser application with MVVM pattern.
I have a first page (ConsultInvoice) with a dataGrid. When I double click on one of the row I want to navigate to another page (EditInvoice) passing the selected row in argument to my constructor.
I know if I want do things properly I should use a dependency injection, but I don't really see how to use it here.
How can I simply pass this constructor?
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*/
}
}
Basically you should avoid passing such parameters into the ViewModels constructor, as wiring it with Inversion of Control/Dependency Injection becomes a pain. While you can use Abstract Factory pattern to resolve objects with runtime parameters, it's imho not suitable for ViewModels.
Instead I always suggest using a form of navigation pattern, similar to how Microsoft's Patterns & Practices team has done with Prism. There you have an INavigationAware
interface which your ViewModels can implement. It has 2 methods, NavigateTo
and NavigateFrom
.
And there is a navigation service. The navigation service will switch the views and before switching calling NavigateFrom
in the current ViewModel (if it implements it. One can use it to check if data is saved and if necessary cancel the navigation. After the new View has been loaded and the ViewModel assigned to it, call NavigateTo
in the newly navigated ViewModel.
Here you'd pass the parameters required for the ViewModel, in your case invoiceId
. Try avoid passing whole models or complex objects. Use the invoiceid
to fetch the invoice data and to populate your editing ViewModel.
A basinc implementation from my former answer (can be found 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");
}
}
}
}
An example of the INavigationAware
interface used in Prism can be found on the projects github repository.
This makes it easy to pass parameter and async
load your data (where there isn't any clean way to do this via constructor, as you can't await
an async
operation inside the constructor without locking, and doing this kind of things in the constructor is very discouraged).
这篇关于通过参数设置为视图模型构造的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!