Part 1. Display the properties of the control in MVVM
正如我在评论中所说:
在 MVVM ViewModel 中不应该知道其所在的控件。在这种情况下,请使用附加行为或在视图中保留相同的侧逻辑
ViewModel
不直接与View
,所以仅仅引用控件的名称是不正确的。最好在里面设置一个属性Model
,并将其绑定到View
via ViewModel
,但财产Name
不支持绑定(引用自MSDN http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.name.aspx):
数据绑定名称在技术上是可行的,但这是一种极其罕见的情况,因为数据绑定名称无法满足属性的主要预期目的:为代码隐藏提供标识符连接点。
所以我建议使用Tag
财产或Uid
。在我的示例中(下面给出),我使用Uid
用于这些目的的财产。
Part 2. Communication via ViewModels (pattern Mediator)
其具体实施方式有多种Mediator模式,但我最喜欢的实现是XAML Guy
,简单明了——调解者模式 http://code.msdn.microsoft.com/windowsdesktop/The-Mediator-Pattern-c2c07b54.
Implementation code
public static class Mediator
{
static IDictionary<string, List<Action<object>>> pl_dict = new Dictionary<string, List<Action<object>>>();
static public void Register(string token, Action<object> callback)
{
if (!pl_dict.ContainsKey(token))
{
var list = new List<Action<object>>();
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<object> 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
,各有各的ViewModel
and Model
.
项目结构如下图所示:
Output
当您单击按钮时,ListOfDataViewModel
通过调解器与 DataDetails 进行通信ViewModel
, thus:
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
<Window x:Class="CommunicateWithVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels"
Title="MainWindow"
WindowStartupLocation="CenterScreen"
Height="350"
Width="525">
<Grid>
<ContentControl Name="ListOfData"
ContentTemplate="{StaticResource ListOfDataView}">
<ViewModels:ListOfDataViewModel />
</ContentControl>
<ContentControl Name="DataDetails"
ContentTemplate="{StaticResource DataDetailsView}">
<ViewModels:DataDetailsViewModel />
</ContentControl>
</Grid>
</Window>
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
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels">
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<DataTemplate x:Key="DataDetailsView" DataType="{x:Type ViewModels:DataDetailsViewModel}">
<StackPanel Width="200"
Background="AliceBlue"
HorizontalAlignment="Right"
Visibility="{Binding Path=DataDetailsModel.IsVisible,
Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock Text="Fruit: " />
<TextBlock Text="{Binding Path=DataDetailsModel.SelectedFruit}" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
ListOfDataView
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels">
<DataTemplate x:Key="ListOfDataView" DataType="{x:Type ViewModels:ListOfDataViewModel}">
<StackPanel Width="200"
Background="Azure"
HorizontalAlignment="Left">
<Button Uid="{Binding Path=ListOfDataModel.FruitGreen}"
Content="GreenButton"
Command="{Binding Path=GreenButtonCommand}" />
<Button Uid="{Binding Path=ListOfDataModel.FruitYellow}"
Content="YellowButton"
Command="{Binding Path=YellowButtonCommand}" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
该项目可在此处获得.