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

时间:2010-06-22 07:58:57

标签: wpf xaml mvvm dependency-properties

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

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

<PropertySetter Property="{Binding MyViewModelDependencyProperty}" Value="{Binding ElementName=aControlOnMyView" />

在我走这条路之前,我想检查一下我是否还有其他办法吗?


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

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

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

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

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

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

2 个答案:

答案 0 :(得分:7)

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

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

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

public class MyViewModel
{
  public ListBox SpecialControl { get; set; }
  public MyViewModel()
  {
    SpecialControl = new ListBox();
  }
}

<DataTemplate TargetType="{x:Type local:MyViewModel}">
  <DataTemplate.Resources>
    <Style TargetType="ListBox" ... />
  </DataTemplate.Resources>
  ...
  <ContentPresenter Content="{Binding SpecialControl}" />
</DataTemplate>

其他可能性是:

  1. 让视图模型实际派生自Control类,然后重写OnApplyTemplate()并使用GetTemplateChild查找名称以“PART _”开头的模板项
  2. 实现带有属性名称的附加属性,在DataContext中查找该属性,并将其设置为附加属性的DependencyObject。
  3. 实施您的PropertySetter构思
  4. 我的选项#2看起来像这样:

    <DataTemplate TargetType="{x:Type MyViewModel}">
      ...
      <TextBox local:PropertyHelper.SetViewModelToThis="SpecialControl" />
      ...
    </DataTemplate>
    

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

答案 1 :(得分:2)

首先,控件的DataContext应该是ViewModel对象,而不是它的属性。其次,当TwoWayViewModel的属性绑定到您的控件时,控件值的更改将更新(在您的情况下,'set')ViewModel属性的值。