WPF MVVM 绑定数据上下文最佳实践
WPF MVVM binding datacontext best practice
有人告诉我,MVVM 的最佳实践是将视图模型传递给视图,这样它在运行前不知道数据上下文。 (系统 1)
然而,我发现每个 post 都会将视图模型传递到 xaml 数据上下文中,这对于 MVVM 来说似乎并不理想。 (系统 2)
在 WPF MVVM 中绑定数据上下文的最佳方法是什么?这对事件处理程序绑定有何影响?
示例:
我有一个组合框链接到我的 ViewModel 中的一个项目。当我使用绑定系统 2 时,组合框选择会触发 'ComboChanged' 事件。使用绑定系统 1 时,组合框选择会发生变化,但事件不会触发。整个页面加载完成后,手动更改组合框时将触发该事件。
主机(系统 1):
public override void Initialise()
{
element = new Plugin(new ViewModel(Credentials));
element.combobox.SelectedIndex = 0;
}
插件(系统 1):
public Plugin(ViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
this.DataContext = ViewModel;
ViewModel.ComboChanged += new EventHandler<ComboChangedEventArgs>(performComboChanged);
}
ViewModel(系统 1):
public ViewModel(Credentials credentials)
{
//Initialisation code
}
主机(系统 2):
public override void Initialise()
{
element = new Plugin(Credentials)
element.combobox.SelectedIndex = 0;
}
插件(系统 2):
public Plugin(Credentials credentials)
{
InitializeComponent();
ViewModel = ((ViewModel)this.DataContext);
ViewModel.Credentials = credentials;
ViewModel.ComboChanged += new EventHandler<ComboChangedEventArgs>(performComboChanged);
}
//Plugin.xaml
<UserControl.DataContext>
<local:ViewModel/>
</UserControl.DataContext>
视图模型(系统 2):
public ViewModel()
{
//Initialisation code
}
提前致谢
关于您的事件处理实现:
简而言之:
- 你不应该在代码隐藏中将事件绑定到 VM,甚至不应该在代码隐藏中使用你的 VM:表示和业务代码会混合在一起,如果你的视图发生变化,你可能会破坏你的代码逻辑
- 您永远不应该将您的视图或其控件之一用于您的视图模型:VM 是业务场所,而不是演示场所
有一次我总是牢记这一点,尤其是当我不得不完全重新组织一个大的复杂视图(重命名控件、从列表框切换到网格视图、重新组织网格等)时:因为我的 MVVM 很干净(严格VM/business 与 view/presentation 的分离)我能够做到这一点,而无需触及我的业务逻辑中的任何一行。如果我将所有控件事件直接绑定到我的视图模型,这将是一场噩梦。
从这里你有(至少)两个选择:
- 使用交互库,使您能够将控件事件绑定到视图模型命令或方法
- 创建您自己的行为以在控制事件和视图模型命令或 属性 之间实现此 link 或 属性。
这个 post 总结了交互选项:MVVM events
有人告诉我,MVVM 的最佳实践是将视图模型传递给视图,这样它在运行前不知道数据上下文。 (系统 1)
然而,我发现每个 post 都会将视图模型传递到 xaml 数据上下文中,这对于 MVVM 来说似乎并不理想。 (系统 2)
在 WPF MVVM 中绑定数据上下文的最佳方法是什么?这对事件处理程序绑定有何影响?
示例: 我有一个组合框链接到我的 ViewModel 中的一个项目。当我使用绑定系统 2 时,组合框选择会触发 'ComboChanged' 事件。使用绑定系统 1 时,组合框选择会发生变化,但事件不会触发。整个页面加载完成后,手动更改组合框时将触发该事件。
主机(系统 1):
public override void Initialise()
{
element = new Plugin(new ViewModel(Credentials));
element.combobox.SelectedIndex = 0;
}
插件(系统 1):
public Plugin(ViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
this.DataContext = ViewModel;
ViewModel.ComboChanged += new EventHandler<ComboChangedEventArgs>(performComboChanged);
}
ViewModel(系统 1):
public ViewModel(Credentials credentials)
{
//Initialisation code
}
主机(系统 2):
public override void Initialise()
{
element = new Plugin(Credentials)
element.combobox.SelectedIndex = 0;
}
插件(系统 2):
public Plugin(Credentials credentials)
{
InitializeComponent();
ViewModel = ((ViewModel)this.DataContext);
ViewModel.Credentials = credentials;
ViewModel.ComboChanged += new EventHandler<ComboChangedEventArgs>(performComboChanged);
}
//Plugin.xaml
<UserControl.DataContext>
<local:ViewModel/>
</UserControl.DataContext>
视图模型(系统 2):
public ViewModel()
{
//Initialisation code
}
提前致谢
关于您的事件处理实现:
简而言之:
- 你不应该在代码隐藏中将事件绑定到 VM,甚至不应该在代码隐藏中使用你的 VM:表示和业务代码会混合在一起,如果你的视图发生变化,你可能会破坏你的代码逻辑
- 您永远不应该将您的视图或其控件之一用于您的视图模型:VM 是业务场所,而不是演示场所
有一次我总是牢记这一点,尤其是当我不得不完全重新组织一个大的复杂视图(重命名控件、从列表框切换到网格视图、重新组织网格等)时:因为我的 MVVM 很干净(严格VM/business 与 view/presentation 的分离)我能够做到这一点,而无需触及我的业务逻辑中的任何一行。如果我将所有控件事件直接绑定到我的视图模型,这将是一场噩梦。
从这里你有(至少)两个选择:
- 使用交互库,使您能够将控件事件绑定到视图模型命令或方法
- 创建您自己的行为以在控制事件和视图模型命令或 属性 之间实现此 link 或 属性。
这个 post 总结了交互选项:MVVM events