一个用于UserControl和Window的ViewModel或单独的ViewModel

我有MainWindowAddEdit 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 callback) { if (!pl_dict.ContainsKey(token)) { var list = new List>(); list.Add(callback); pl_dict.Add(token, list); } else { bool found = false; foreach (var item in pl_dict[token]) if (item.Method.ToString() == callback.Method.ToString()) found = true; if (!found) pl_dict[token].Add(callback); } } static public void Unregister(string token, Action callback) { if (pl_dict.ContainsKey(token)) { pl_dict[token].Remove(callback); } } static public void NotifyColleagues(string token, object args) { if (pl_dict.ContainsKey(token)) { foreach (var callback in pl_dict[token]) callback(args); } } } 

为了演示他的工作,我创建了一个小例子,它由两个Views组成,每个Views都有自己的ViewModelModel

项目结构如下所示:

项目结构

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的好处:

  1. 可重用性 – 将来如果你想对UserControl相关的数据进行一些更改( 可能是一些逻辑更改 ),你只需要去ViewModel并更新它,它就会反映在所有窗口中。 您不必担心转到每个Window的视图模型并更新代码。

  2. 可测试性 – 如果您想测试与您的控件相关的逻辑( 数据部分我在这里谈论的不是视图部分) ,您可以单独编写它。 无需担心Window视图模型代码的测试。

  3. 松散耦合 – 不止一个人可以孤立地工作。 假设一个开发人员必须更新一些与主窗口相关的代码,而其他人必须更新一些与UserControl相关的代码。 有了一个ViewModel,就会有一些重叠,它们不能孤立地工作,因为在他/她可以在ViewModel中插入他/她的代码之前,依赖于其他人来完成它的工作。

还可以在此处查看不同ViewModel之间的通信,因为您可能需要在Window视图模型和User Control View模型之间进行通信,以便在左侧窗口中传递所选数据。