1

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?

Gregory Higley
  • 15,923
  • 9
  • 67
  • 96
  • you should post your FooViewModel, BarViewModel and View. i think you mix up some stuff. – blindmeis Jan 31 '12 at 12:05
  • 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 Apr 24 '12 at 22:11
  • 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 Dec 27 '12 at 18:49

2 Answers2

1

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.

devdigital
  • 34,151
  • 9
  • 98
  • 120
  • Thanks, but I have no problems with MVVM. This is a large, existing project in which all other aspects are working fine. It is WPF with no plans ever to use Silverlight. The properties are dependency properties because our base class uses CSLA.NET's ViewModel, which is a dependency object. So we cannot take any of your suggestions and must work with it as is. – Gregory Higley Jan 31 '12 at 12:20
  • Yes, I don't know why Rocky took that approach, we took his view model base as an example of working with CSLA, and wrote our own which uses just INotifyPropertyChanged. Having said that, Rocky's view model base also implements INotifyPropertyChanged, albeit not in a particularly elegant way, as he only implements a string based OnPropertyChanged helper method. So, you could try implementing the BarViewModel and View properties as INotifyPropertyChanged properties instead of DependencyProperty's and see if that works. – devdigital Jan 31 '12 at 12:28
  • I'll try that. By the way, you state that our view models shouldn't have a reference to our views, but that isn't how VM-first with dependency injection works. In VM first, your view is injected into the constructor as an interface and then exposed as a property. This works fine and is completely testable, since there is no direct reference to the view itself. I have no plans to change it, because I find it to be an extremely flexible approach. – Gregory Higley Jan 31 '12 at 12:34
  • It turns out `INotifyPropertyChanged` was the solution. Fascinating. I wasn't aware you could mix-and-match `INotifyPropertyChanged` with dependency properties in the same object. – Gregory Higley Jan 31 '12 at 12:44
  • I think Reed answers the question very well here - http://stackoverflow.com/a/3670669/248164. You should ask why your view model requires a reference to the view. Presumably your exposing certain types of controls on your view interface, which again probably ties your view models to WPF, and mixes the concerns of the view with the code of the view model. – devdigital Jan 31 '12 at 12:44
  • I read Reed's post, and it doesn't apply to me. I have no view logic whatsoever in my view models, which are clean and testable. My views are referenced purely through abstract interfaces which are usually empty. They are used purely for ViewModel-first DI and binding. – Gregory Higley Jan 31 '12 at 12:51
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/7196/discussion-between-gregory-higley-and-devdigital) – Gregory Higley Jan 31 '12 at 12:52
  • 2
    i upvoted you, because an answer with -1 look not that nice :) – blindmeis Jan 31 '12 at 14:14
0

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"); }
    }
}
blindmeis
  • 22,175
  • 7
  • 55
  • 74
  • As I said to DevDigital, this is a large, existing project in which all other aspects are working fine. The properties are dependency properties because our base class uses CSLA.NET's ViewModel, which is a dependency object. So we cannot take any of your suggestions and must work with it as is. I understand how data binding works and what it is you need to bind to. That is not my problem. Please re-read my question. – Gregory Higley Jan 31 '12 at 12:21
  • 2
    regarding to mvvm and viewmodel in special - i dont like that the viewmodel inherit from system.windows namespace. unit-testing is much more cleaner without this namesspace. but this is just my thought. i have in mind that someone (maybe josh smith) wrote as far as you have a System.Windows in your usings - its not a clean viewmodel anymore. – blindmeis Jan 31 '12 at 14:31