WPF:具有子UserControl(MVVM)的Window的正确配置

时间:2010-08-07 18:16:25

标签: wpf xaml mvvm binding viewmodel

我正在努力正确地完成以下操作。我有一个UserControl(ProgramView)。它有一个viewmodel(ProgramViewViewModel)。 ProgramView作为Window(ProgramWindow)中的子控件使用。 ProgramWindow有一个公共属性ProgramId,因此窗口的使用者可以指定要显示的所需程序(数据实体)。 ProgramView有一个属性ProgramId,因为它的主要工作是显示这些数据。 ProgramWindow只不过是这个用户控件的包装器窗口。

ProgramViewViewModel还有一个属性ProgramId。对此属性的更改驱逐了视图模型的操作,这些操作使用ProgramView可以绑定到的其他属性从视图模型中浮出。

我试图从ProgramView和ProgramWindow的使用者隐藏视图模型的操作。

这个ProgramId应该通过所有这些层绑定。对ProgramWindow.ProgramId的更改应该流向ProgramView.ProgramId,然后流向ProgramViewViewModel.ProgramId。我无法弄清楚如何正确实现这一点。

我目前的方法是将所有三个类中的ProgramId表示为DP。在Window中,我想象ProgramView会被实例化:

<local:ProgramView ProgramId="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ProgramWindow}}, Path=ProgramId}" />

这看起来确实有效。在ProgramView中,我确实获得了属性的更改事件,并且它们看起来确实具有正确的值。 FindAncestor似乎运作正常。

我应该如何同步ProgramViewViewModel.ProgramId属性?我看到两种方式。一种方法是在ProgramViewViewModel实例上设置Binding,也使用FindAncestor,并在ProgramViewViewModel上找到ProgramId。这有两个缺点。它需要ProgramViewViewModel将ProgramId表示为依赖属性。我宁愿避免这种情况,但这可能是可以接受的。无论哪种方式,我都无法在XAML中完成它。

<local:View.DataContext>
    <local:ProgramViewViewModel
        ProgramId="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ProgramView}}, Path=ProgramId}" />
</local:View.DataContext>

这不起作用。看来我无法在实例的实例化中引入绑定表达式。 FindAncestor报告它无法找到ProgramView。我的理论是实例在逻辑树之外,因此不能遍历它的父级。

更有意义的第二个选项是将ProgramView.ProgramId属性绑定到“ProgramId”(在DataContext中)。我无法实现这一点,因为我无法弄清楚如何在代码隐藏中定义的属性上指定绑定表达式。在XAML中是必需的,但实际上存在类型ProgramId。我无法弄清楚如何指定这个属性。

如果我手动(在ProgramView的代码隐藏中)创建一个Binding实例并调用SetBinding(ProgramIdProperty,binding),则该值不再流入View本身。我相信这是因为我刚刚替换了以前由ProgramWindow设置的ProgramView.ProgramId上的绑定。每个属性一个绑定?

我剩下的想法是在ProgramView中提供两个ProgramId属性。一个绑定到DataContext,另一个绑定到消费者(ProgramWindow),然后编写同步这两个的OnValueChanged处理程序。这感觉就像一个黑客。另一种是在ProgramView的代码隐藏中手动监视ProgramView.ProgramId和ProgramView.DataContext的更改,并自己传播值。这些想法都不合理。

我正在寻找其他建议。

1 个答案:

答案 0 :(得分:0)

您的描述似乎很详细,但我无法理解您为什么需要实施此设计。我忍不住想想干。

如果需要在两个这样的相关视图模型中公开依赖项属性,我建议您为子视图模型(对于用户控件视图)创建第一个属性(对于程序窗口视图)。类似的东西:

public class MainViewModel : ViewModelBase
{
   public ProgramViewModel ChildViewModel { get; private set; }

}

public class ProgramViewModel : ViewModelBase
{
   private int _ProgramId;

   public int ProgramId
   {
      get { return _ProgramId; }
      set
      {
         if (value != _ProgramId)
         {
             // set and raise propery changed notification
         }
      }
   }
}

MainView可以使用ChildViewModel.ProgramId(数据上下文设置为MainViewModel)获取属性。 ProgramView通过ProgramId访问它(数据上下文设置为MainViewModel.ChildViewModel)。