一个用于UserControl和Window的ViewModel或单独的ViewModel
我有MainWindow
和AddEdit
UserControl
。 在MainWindow
内部,我将此AddEdit呈现为 ,之前此命名空间已添加到Window元素:
xmlns:Views="clr-namespace:MyProject.WPF.Views" +++++++++++++++ ++++++++++++++++ ListOfData + + DataDetails + + + + DataOne + + Name: txtBox1+ DataTwo + + + DataThree + + + + + Save data + +++++++++++++++ ++++++++++++++++
当用户选择左侧的数据时(例如DataTwo)我想在AddEdit用户控件(DataDetails面板)中显示它的属性(为简单起见,只有Name属性)。
由于此UserControl
与MainWindow分开存储,我应该使用相同的MainWindowViewModel和相同的datacontext,还是应该为AddEdit UserControl
创建单独的ViewModel?
希望这听起来很清楚,如果不是,请询问详细信息。
Part 1. Display the properties of the control in MVVM
正如我在评论中所说:
在MVVM中,ViewModel不应该知道所在的控件。 在这种情况下,请使用附加的行为或在View中保留相同的侧逻辑
ViewModel
不与View
直接关联,因此只需引用控件的名称就不对了。 最好在Model
设置一个属性,并通过ViewModel
将其绑定到View
,但属性Name
不支持Binding(来自MSDN的引用):
数据绑定名称在技术上是可行的,但是这是一种非常罕见的情况,因为数据绑定名称无法满足属性的主要用途:为代码隐藏提供标识符连接点。
所以我建议使用Tag
属性或Uid
。 在我的例子中(给出一个下面的),我使用Uid
属性来达到这些目的。
Part 2. Communication via ViewModels (pattern Mediator)
Mediator模式有几个实施例,但我最喜欢XAML Guy
的实现,它简单明了 – Mediator模式 。
Implementation code
public static class Mediator { static IDictionary>> pl_dict = new Dictionary>>(); static public void Register(string token, Action
为了演示他的工作,我创建了一个小例子,它由两个Views
组成,每个Views
都有自己的ViewModel
和Model
。
项目结构如下所示:
Output
当您单击Button时,ListOfData ViewModel
通过mediator与DataDetails ViewModel
通信,因此:
Mediator.NotifyColleagues("ShowDetails", true); Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen);
与属性交互的所有过程都必须注册其ViewModel
如下所示:
private void ShowDetails_Mediator(object args) { bool showDetails = (bool)args; if (showDetails == true) { DataDetailsModel.IsVisible = true; } else { DataDetailsModel.IsVisible = false; } } private void SetSelectedFruit_Mediator(object args) { string selectedFruit = (string)args; DataDetailsModel.SelectedFruit = selectedFruit; } public DataDetailsViewModel() { DataDetailsModel = new DataDetailsModel(); Mediator.Register("ShowDetails", ShowDetails_Mediator); Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator); }
在示例中,我使用了DataTemplate
而不是UserControl
。 以下是该项目的主要部分:
MainWindow.xaml
Models
DataDetailsModel
public class DataDetailsModel : NotificationObject { #region SelectedFruit private string _selectedFruit = ""; public string SelectedFruit { get { return _selectedFruit; } set { _selectedFruit = value; NotifyPropertyChanged("SelectedFruit"); } } #endregion #region IsVisible private bool _isVisible = false; public bool IsVisible { get { return _isVisible; } set { _isVisible = value; NotifyPropertyChanged("IsVisible"); } } #endregion }
ListOfDataModel
public class ListOfDataModel : NotificationObject { #region FruitGreen private string _fruitGreen = "Apple"; public string FruitGreen { get { return _fruitGreen; } set { _fruitGreen = value; NotifyPropertyChanged("FruitGreen"); } } #endregion #region FruitYellow private string _fruitYellow = "Limon"; public string FruitYellow { get { return _fruitYellow; } set { _fruitYellow = value; NotifyPropertyChanged("FruitYellow"); } } #endregion }
ViewModels
DataDetailsViewModel
public class DataDetailsViewModel { #region DataDetailsModel private DataDetailsModel _dataDetailsModel = null; public DataDetailsModel DataDetailsModel { get { return _dataDetailsModel; } set { _dataDetailsModel = value; } } #endregion #region ShowDetails_Mediator private void ShowDetails_Mediator(object args) { bool showDetails = (bool)args; if (showDetails == true) { DataDetailsModel.IsVisible = true; } else { DataDetailsModel.IsVisible = false; } } #endregion #region SetSelectedFruit_Mediator private void SetSelectedFruit_Mediator(object args) { string selectedFruit = (string)args; DataDetailsModel.SelectedFruit = selectedFruit; } #endregion #region DataDetailsViewModel Constructor public DataDetailsViewModel() { DataDetailsModel = new DataDetailsModel(); Mediator.Register("ShowDetails", ShowDetails_Mediator); Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator); } #endregion }
ListOfDataViewModel
public class ListOfDataViewModel { #region ListOfDataModel private ListOfDataModel _listOfDataModel = null; public ListOfDataModel ListOfDataModel { get { return _listOfDataModel; } set { _listOfDataModel = value; } } #endregion #region GreenButtonCommand private ICommand _greenButtonCommand = null; public ICommand GreenButtonCommand { get { if (_greenButtonCommand == null) { _greenButtonCommand = new RelayCommand(param => this.GreenButton(), null); } return _greenButtonCommand; } } private void GreenButton() { Mediator.NotifyColleagues("ShowDetails", true); Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen); } #endregion #region YellowButtonCommand private ICommand _yellowButtonCommand = null; public ICommand YellowButtonCommand { get { if (_yellowButtonCommand == null) { _yellowButtonCommand = new RelayCommand(param => this.YellowButton(), null); } return _yellowButtonCommand; } } private void YellowButton() { Mediator.NotifyColleagues("ShowDetails", true); Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitYellow); } #endregion #region ListOfDataViewModel Constructor public ListOfDataViewModel() { ListOfDataModel = new ListOfDataModel(); } #endregion }
Views
DataDetailsView
ListOfDataView
该项目可在此链接中找到 。
由于UserControl是单独维护的,而不是Window内容的一部分。 我建议有单独的ViewModel 。
拥有单独的ViewModel的好处:
-
可重用性 – 将来如果你想对UserControl相关的数据进行一些更改( 可能是一些逻辑更改 ),你只需要去ViewModel并更新它,它就会反映在所有窗口中。 您不必担心转到每个Window的视图模型并更新代码。
-
可测试性 – 如果您想测试与您的控件相关的逻辑( 数据部分我在这里谈论的不是视图部分) ,您可以单独编写它。 无需担心Window视图模型代码的测试。
-
松散耦合 – 不止一个人可以孤立地工作。 假设一个开发人员必须更新一些与主窗口相关的代码,而其他人必须更新一些与UserControl相关的代码。 有了一个ViewModel,就会有一些重叠,它们不能孤立地工作,因为在他/她可以在ViewModel中插入他/她的代码之前,依赖于其他人来完成它的工作。
还可以在此处查看不同ViewModel之间的通信,因为您可能需要在Window视图模型和User Control View模型之间进行通信,以便在左侧窗口中传递所选数据。