将ContentControl绑定到WPF中的深层路径

时间:2012-01-31 11:48:37

标签: wpf xaml binding mvvm

我目前正在编写的应用程序正在使用具有ViewModel优先模式的MVVM。我的XAML类似于以下内容:

<ContentControl Content="{Binding FooViewModel.BarViewModel.View, Mode=OneWay}"/>

每个VM都是DependencyObject。每个属性都是DependencyProperty。根据应用程序的状态,BarViewModel的{​​{1}}属性的值可以更改,从而更改FooViewModel属性的值。不幸的是,当发生这种情况时,不显示新视图,旧视图仍然存在。

这非常令人沮丧。我认为如果路径表达式的任何部分发生更改,绑定将更新,但似乎并非如此。当我使用较浅的路径表达式(例如View并且我已更改FooViewModel.View属性的值时,更新了FooViewModel它受到约束,但不是在这种情况下。

如果您的解决方案是我放弃ViewModel优先,那么这不是一个选择,但我感谢您的建议。我必须按原样运作。

澄清

这是关于数据绑定关于MVVM或如何实现它的问题。如果它可以帮助您思考问题,或者您对如何实现MVVM有不同的想法,则可以安全地忽略 MVVM方面。这是一个大型的现有项目,其中MVVM设计模式无法更改。 (现在为时已晚。)

因此,如上所述,正在回答的正确问题如下:

给定一个绑定路径表达式,其中每个元素都是ContentControl,并且最终属性是绑定到ContentControl的视图,为什么路径中间的属性更改不会导致绑定更新?

2 个答案:

答案 0 :(得分:1)

虽然我希望这可行,但您的方法存在一些问题。

首先,您的视图模型不应使用DependencyObjectDependencyProperty,这会将它们绑定到WPF。他们应该实施INotifyPropertyChanged。这使您的视图模型可以在其他演示技术(如Silverlight)中重用。

其次,您的视图模型不应该引用您的视图,因此您不应在视图模型上要求View属性。

我会认真考虑使用MVVM框架进行视图合成 - Caliburn.Micro,例如,使视图模型的第一次开发非常简单,并且已经提供了一个实现INotifyPropertyChanged的视图模型基类,以及构建具有约定的视图合成的机制。

即。您可以拥有一个具有ActiveItem属性的导体视图模型,只需在视图上放置ContentControl,其名称与属性相同:

<ContentControl x:Name="ActiveItem" />

您可以使用ActivateItem()方法更改当前有效项目。

Caliburn.Micro还有许多其他功能,例如能够在您的视图上放置Button x:Name="Save"控件,并且您的视图模型上的Save方法将会单击按钮时自动调用。

答案 1 :(得分:0)

  

每个VM都是DependencyObject。每个属性都是   的DependencyProperty。

为什么呢? viewmodel应该是一个带有INotifyPropertyChanged的简单类,而Properties应该是简单的属性。

如果您希望以不同的方式呈现不同的viewmodel - 您应该使用DataTemplate。

 <Window>
  <Window.Resources>
    <DataTemplate DataType="{x:Type local:MyViewModelA}>
     <MyViewA/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:MyViewModelB}>
     <MyViewB/>
    </DataTemplate>
   </Windows.Resources>
   <Grid>
     <ContentControl Content="{Binding MyActualVM}"/>
   </Grid>
  </Window>

编辑:顺便说一下你总是绑定到最后一个属性:FooViewModel.BarViewModel.View - &gt;所以INotifyPropertyChanged(如果引发)只适用于.View

EDIT2:另一种方法可能是获取内容控件的BindingExpression并调用。

System.Windows.Data.BindingExpression expr = //get it from your contentcontrol
expr.UpdateTarget();

EDIT3:以及简单的 mvvm 方式 - 只需使用INotifyPropertyChanged

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.MyFooVM = new FooVM();
        this.MyFooVM.MyBarVM = new BarVM(){View = "erster"};

        this.DataContext = this;

    }

    public FooVM MyFooVM { get; set; }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        this.MyFooVM.MyBarVM = new BarVM(){View = "zweiter"};
    }
}

public class INPC : INotifyPropertyChanged
{
    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropChanged(string property)
    {
        var handler = PropertyChanged;

        if(handler != null)
            handler(this, new PropertyChangedEventArgs(property));
    }

    #endregion
}

public class FooVM:INPC
{
    private BarVM _myBarVm;
    public BarVM MyBarVM
    {
        get { return _myBarVm; }
        set { _myBarVm = value;OnPropChanged("MyBarVM"); }
    }
}

public class BarVM : INPC
{
    private string _view;
    public string View
    {
        get { return _view; }
        set { _view = value;OnPropChanged("View"); }
    }
}