从WPF中的View中设置ViewModel上的属性

dev*_*tal 2 wpf xaml dependency-properties mvvm

我的ViewModel上有一个依赖属性,它是我的View的DataContext.ViewModel没有对View的引用.ViewModel上的属性将引用视图上的控件,但我需要能够在XAML中设置此属性.

这怎么可能?我有一个想法是开发一个具有Property属性和Value属性的自定义控件,因此您可以在View中执行类似的操作以在ViewModel上设置属性:

<PropertySetter Property="{Binding MyViewModelDependencyProperty}" Value="{Binding ElementName=aControlOnMyView" />
Run Code Online (Sandbox Code Playgroud)

在我走这条路之前,我想检查一下我是否还有其他方法可以采取?


感谢雷的详细回复,但如果我给你一些关于我试图解决的问题的更多细节,你可能会更好地了解我为什么提到我做的方法.

基本上,我正在尝试做的是当用户点击按钮时将焦点设置为文本框.我写了一个附加属性,你可以将它附加到Button控件,指定触发事件是什么(在这种情况下是'Click'事件),然后是要关注的控件.这非常好用,并且保留了XAML中的所有内容.

但是,我现在有一个用例,其焦点应设置为按钮上的单击事件的任意文本框,该按钮是工具栏的一部分.这个工具栏本身就是一个用户控件,它位于另一个用户控件内,该控件位于另一个用户控件内!此工具栏需要可以在各种不同的表单中重复使用,并且每次单击按钮后设置焦点的控件在每个表单中都会有所不同.

这就是为什么我想让焦点控件(即文本框)成为视图模型本身的属性(在我的ViewModel基础上是精确的),并且具有ViewModel基本代码(工具栏绑定到的),设置单击按钮时对焦于控件(并且在ViewModel基础上调用例如Add/Edit方法).

在单元测试中,关注属性的控件将为null,因此它的.Focus()方法就不会被调用.所以我在那里看不到问题.我的问题是如何从XAML设置焦点控制属性,这就是我有PropertySetter想法的原因.

我不喜欢ViewModel对视图上的控件有任何引用这一事实,但我看不到另一种方法来实现我需要的东西.如果决定是否将焦点设置到控件的逻辑非常复杂怎么办?这肯定会在ViewModel中吗?因此,具有此UIElement属性的ViewModel是否有任何损害?它仍然对它所绑定的特定视图一无所知,它只知道在ViewModel上发生某些动作时需要设置焦点.

Ray*_*rns 7

我的第一反应(也是一个强烈的反应)是这样说的"不要那样做!" 通过为您的视图模型提供对UI的一部分的引用,您打破了使视图模型如此强大和有用的封装.

例如,如果要对视图模型进行单元测试或将其序列化为磁盘,该怎么办?在每种情况下,您的UI都不会出现,因为根本没有视图.您的测试将错过覆盖范围,您的重建将不完整.

如果您的视图模型实际上需要对UI对象的引用,并且没有更好的方法来构建它,那么最好的解决方案是让视图模型本身构造它需要引用的控件.然后,您的视图可以通过绑定将该控件合并为ContentPresenter的内容,并提供用于配置控件的Style,包括用于提供其内容的ControlTemplate.正是如此:

public class MyViewModel
{
  public ListBox SpecialControl { get; set; }
  public MyViewModel()
  {
    SpecialControl = new ListBox();
  }
}
Run Code Online (Sandbox Code Playgroud)

<DataTemplate TargetType="{x:Type local:MyViewModel}">
  <DataTemplate.Resources>
    <Style TargetType="ListBox" ... />
  </DataTemplate.Resources>
  ...
  <ContentPresenter Content="{Binding SpecialControl}" />
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)

其他可能性是:

  1. 让视图模型实际派生自Control类,然后重写OnApplyTemplate()并使用GetTemplateChild查找名称以"PART_"开头的模板项
  2. 实现一个附加属性,该属性获取属性名称,在DataContext中查找该属性,并将其设置为附加属性的DependencyObject.
  3. 实现您的PropertySetter想法

我的选项#2看起来像这样:

<DataTemplate TargetType="{x:Type MyViewModel}">
  ...
  <TextBox local:PropertyHelper.SetViewModelToThis="SpecialControl" />
  ...
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)

SetViewModelToThis PropertyChangedCallback中的代码将从DataContext获取视图模型,反映它以查找"SpecialControl"属性,然后将其设置为TextBox.请注意,SetViewModelToThis的实现必须考虑到DataContext没有立即设置的可能性,并且可能需要更改它,需要删除旧设置并创建一个新设置.