我正在做一个Surface WPF项目,我们尝试实现MVVM模式。在这个项目中,我们正在构建一些自定义控件,这些控件绑定到不同的视图模型。
例如,我们有一个设置控件,它有一个设置视图模型,我们有一个主视图模型,它是"整体"视图模型。
在我们的 surfacewindows.xaml 页面中,我们使用 mvvm-light 中的视图模型定位器将数据上下文设置为主视图模型。同样在我们的 surfacewindow.xaml 上,我们添加了设置控件,并且在控件上我们将数据上下文设置为设置视图模型。
现在我们需要两个视图模型相互交互:当前的情况是我们需要设置设置控件的可见性。我们在主视图模型上有一个属性,它是一个布尔值(IsSettingsControlVisible),通过使用转换器将布尔值转换为可见性对象,该属性绑定到控件可见性属性。
现在,当我们需要通过单击设置控件上的关闭按钮将可见性设置为不可见时,就会出现问题。由于我们已将控件上的数据上下文设置为设置视图模型,因此无法访问主视图模型。
到目前为止,我们一直在考虑将设置视图模型作为属性添加到主视图模型中,并从设置控件中删除数据上下文。在设置控件中,我们将使用绑定作为 SettingsProperty.Property。我们也可以从设置控件访问主视图模型。这有意义吗?有没有更好的方法来进行这种交互?
我真的很想听听你关于如何实现这些互动的想法。
我倾向于使用温莎城堡构建的视图模型图。顶级视图模型使用构造函数注入来接收它所需的下一级视图模型。在视图中,我将内容表示器绑定到视图模型的属性以创建相应的视图图。
这样做,父子视图模型很容易进行通信,但对于同级或更远的视图模型来说,通信就有点困难了。
在这些情况下,我倾向于使用事件聚合器或 Messenger 来允许视图模型进行通信。
由于您已经在使用 MVVMLight,因此我建议使用 MVVM Light 工具包 Messenger 系统。它用于视图模型之间的消息交换。背后的概念是Mediator pattern
,不同的对象在不相互了解的情况下交换信息。
下面是一个示例:
在设置视图模型中注册到一个事件,该事件告知显示设置对话框
public SettingsViewModel()
{
Messenger.Default.Register<ShowSettingsMessage>(this, ShowSettingsDialog);
}
private void ShowSettingsDialog(ShowSettingsMessage showSettingsMessage)
{
// Set the visibility:
this.IsVisible = showSettingsMessage.Content;
}
在 MainViewModel 中,您发送包裹在消息中的通知:
// make the settings visible, e.g. the button click command:
Messenger.Default.Send(new ShowSettingsMessage(true));
这是消息:
// the message:
public class ShowSettingsMessage : GenericMessage<bool>
{
public ShowSettingsMessage(bool isVisible)
: base(isVisible)
{ }
}
我不建议将SettingsViewModel
作为Mainviewmodel
的属性,因为您失去了在不同上下文中使用SettingsViewModel
甚至删除/交换它的可能性。
尝试在名为 IsSettingControlVisible 的设置控件上创建依赖项属性,并将其与父视图模型绑定。
编辑:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(UserControl1), new UIPropertyMetadata(0));
}
并像这样使用它...
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 MyProperty="{Binding Path=ParentViewModelProperty, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" />
</Grid>
</Window>