1
votes

The application I'm currently writing is using MVVM with the ViewModel-first pattern. I have XAML similar to the following:

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

Every VM is a DependencyObject. Every property is a DependencyProperty. Depending upon the state of the application, the value of the BarViewModel property of the FooViewModel can change, thus changing the value of the View property. Unfortunately when this happens, the new view is not displayed, and the old one remains.

This is extremely frustrating. I thought that if any part of a path expression changed, the binding would update, but that doesn't appear to be the case. When I've used shallower path expressions, such as FooViewModel.View and I've changed the value of the FooViewModel property, that has updated the ContentControl to which it's bound, but not in this case.

If your solution is that I abandon ViewModel-first, that is not an option, though I appreciate your advice. I must get this working as is.

CLARIFICATION

This is a question about data binding, and not about MVVM or how to implement it. You can safely ignore the MVVM aspects of this if it helps you to think about the problem, or if you have a different idea about how MVVM should be implemented. This is a large, existing project in which the MVVM design pattern cannot be changed. (It is far too late for that.)

So, with that said, the correct question to be answering is the following:

Given a binding path expression in which every element is a DependencyProperty and the final property is a view bound to a ContentControl, why does a change in a property in the middle of the path not cause the binding to update?

2
you should post your FooViewModel, BarViewModel and View. i think you mix up some stuff.blindmeis
Could you use containers to manage the deep paths? In other words, bind your Content on the ContentControl to View, then surround it in a Grid with DC bound to BarViewModel, and surround that with a Grid with DC bound to FooViewModel. I haven't tried this binding to dependency properties (hence why it's a comment not an answer), but it could work.HiredMind
I have kind of the same issue trying to set xaml to a path of an inner class on my mvvm. like in my mvvm have to RaisePropertyChange("MvvmClass.InnerClass.PropertyName") this wont work.ramnz

2 Answers

1
votes

Although I would expect this to work, there are several problems with your approach.

Firstly, your view models should not use DependencyObject or DependencyProperty, this ties them in to WPF. They should instead implement INotifyPropertyChanged. This makes your view models reusable in other presentation technologies such as Silverlight.

Secondly, your view models shouldn't have references to your views, so you shouldn't require a View property on your view models.

I would seriously consider using an MVVM framework for view composition - Caliburn.Micro, for example, makes view model first development extremely straightforward, and already provides a view model base class which implements INotifyPropertyChanged, and a mechanism for building view compositions with conventions.

I.e. you can have a conductor view model which has an ActiveItem property, and you simply place a ContentControl on your view with the same name as the property:

<ContentControl x:Name="ActiveItem" />

You can use the ActivateItem() method to change the current active item.

Caliburn.Micro also has a host of other features, such as being able to place a Button control with x:Name="Save" on your view, and your Save method on your view model will automatically be invoked when the button is clicked.

0
votes

Every VM is a DependencyObject. Every property is a DependencyProperty.

why? a viewmodel should be a simple class with INotifyPropertyChanged and the Properties should be simple properties.

and if you want your different viewmodel be rendered in a different way - you should use 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>

EDIT: btw you always bind to the last Property: FooViewModel.BarViewModel.View --> so the INotifyPropertyChanged (if raised) just work for the .View

EDIT2: another approach could be to get the BindingExpression of your content control and call.

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

EDIT3: and a simple mvvm way - just use 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"); }
    }
}