我目前正在编写的应用程序正在使用具有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的视图,为什么路径中间的属性更改不会导致绑定更新?
答案 0 :(得分:1)
虽然我希望这可行,但您的方法存在一些问题。
首先,您的视图模型不应使用DependencyObject
或DependencyProperty
,这会将它们绑定到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"); }
}
}