0

I'm building a WPF MVVM application but I'm having problems updating a parent view from a child view model.

I call the doAction() method on my parent view model which updates a property and raises a PropertyChangeEvent. When this method is called from the MainViewModel everything works great however when I call the same method from my child view model the PropertyChangedEvent get's raised but the view doesn't update.

Example:

ChildViewModel()
{
    private ParentViewModel parent; 

    parent.doAction(); // Raised event but MainView doesn't update
}

ParentViewModel()
{
    public void doAction()
    {
        this.Property = true;
        OnPropertyChange("Property"); 
    }
}

My Views are created using XAML:

<MainView>
   <TabItem>
      <view:ChildView/>
   </TabItem>
</MainView>

Propery Change event is raised like so:

protected void OnPropertyChanged(string name)
{
    LOGGER.Info("Property Changed: " + name);
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(name));
    }
}

My question is how do I get the parent view to listen and update to a property change event raised by a child view.

Edit:

Base Class:

public abstract class AbstractBaseViewModel : INotifyPropertyChanged
{

        public event PropertyChangedEventHandler PropertyChanged;
        public ICommand CloseCommand { get; set; }

        public AbstractBaseViewModel()
        {
            this.CloseCommand = new CloseCommand(this);
        }

        public void CloseWindow()
        {
            Application.Current.MainWindow.Close();
        }

        protected void OnPropertyChanged(string name)
        {
            LOGGER.Info("Property Changed: " + name);
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
}

Parent ViewModel:

public class ParentViewModel : AbstractBaseViewModel
{
        private Dispatcher dispatcher;
        private bool visible;

        public bool Visible
        {
            get { return visible; }
            set { visible= value; OnPropertyChanged("Visible"); }
        }

        public MainWindowViewModel()
        {
            this.dispatcher = Dispatcher.CurrentDispatcher;
            this.manager = manager;
        }

        public void ShowTab(){
            this.Visible = true;
        }
 }

Child View Model:

 public class ChildViewModel : AbstractBaseViewModel
 {

    private ParentViewModel parentVm;

    public GeneralViewModel(ParentViewModel vm)
    {
        this.parentVm= vm;
    }

    public void Command(){
        vm.ShowTab();
    }
 }

ParentView Xaml:

<TabItem Header="ViewWeWantToHide/Show" 
     Visibility="{Binding Visible,Converter={converter:BooleanToVisibilityConverter}}">
     <views:SomeOtherView/>
</TabItem>

<TabItem Header="ChildView Tab"> 
     <views:ChildView/>
</TabItem>
Johnny.Minty
  • 143
  • 1
  • 10
  • 2
    I do not see any problem in your code (except that you raise the `PropertyChanged` event in the function that changes the property, instead of the property setter). Please post the full code, and give more description. – Mohammad Dehghan Nov 21 '13 at 05:54
  • Post XAML as well to show us where you are using that `Property`. – Suresh Nov 21 '13 at 06:32
  • Silly question but you're definitely deriving from INotifyPropertyChanged in your ParentViewModel? – GazTheDestroyer Nov 21 '13 at 09:09
  • Hey Guys, Thanks for your response. In my real code I'm not raising the OnPropertyChange directly from a method it is called when setting this.Property = true. Yes the ParentViewModel extends a AbstractClass which implements INotifyPropertyChanged. – Johnny.Minty Nov 21 '13 at 20:44
  • Your code seems very reasonable and it should work. Something else must be wrong. – atomaras Nov 24 '13 at 00:16

2 Answers2

1

Without seeing all of your code, it'd be hard to guess what is causing your problem. However, I'd have to say that it is much more common in WPF to display view model instances in the UI and have WPF automatically display the relevant views, rather than displaying the views directly as you have. If you use view models rather than views then you'll have access to the child view model(s) from the parent view models.

If you did, or do have access to the child view model from the parent view model then I would advise that you use a simple delegate to 'pass your message' from the child view model to the parent view model instead. You can even define a parameter-less delegate and use it to just send a signal, rather than any property value.

You can declare a delegate, add a property of that type to your child UserControl and attach a handler in the main view model. Then you can call the delegate from your child UserControl and in the handler in the parent view model, you can call your method. For more information on this technique, please see my answer to the Passing parameters between viewmodels question (which answers a similar, but not exactly the same problem).

Community
  • 1
  • 1
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Hey Sheridan, I have updated my question with more code for you to digest. The way I have structured the application is that I declare the view in my main view'ss XAML like so: The constructor of NewView uses Unity to resolve the ViewModel for that view and set the Datacontext. public NewView(){ this.DataContext = Unity.Resolve(); } Is there something wrong with this approach? Should I be using a different method to instantiate my views and assign them a view model. – Johnny.Minty Nov 21 '13 at 21:53
  • No, although I've no personal experience with Unity, I know it's well respected and loved. As WPF is so data-centric, it just seems logical (to me at least) to work with the data, in this case, the view models and let the Framework take care of displaying the UI, the views. If you prefer to work that way, that's fine but I won't be able to tell you how to get Unity to do what I suggested. – Sheridan Nov 21 '13 at 23:13
  • No problem thanks for you feedback. As for the base problem do you have any suggestions on why the PropertyChangeEvent when raised from another view model is not updating the main view? – Johnny.Minty Nov 22 '13 at 00:06
  • No, sorry, I can't see anything obviously wrong in your code. – Sheridan Nov 22 '13 at 09:05
0

Thank you everyone for your assistance.

I found the issue which was unrelated to WPF and was actually a product of how I was setting the datacontext on my child views. In my parent window I was creating and registering a singleton instance of my ParentViewModel with the Unity container. This instance would then be injected in to all child views, the problem was the InitializeComponent method was being called before my parent view model was created and registered with the unity container meaning all child views were receiving completely a different instance.

Not working:

public MainWindow()
{

    InitializeComponent();

    if (DesignerProperties.GetIsInDesignMode(this))
    {
        this.DataContext = new ParentViewModel();
    }
    else
    {      
         IUnityContainer container = UnityFactory.Retrieve();

         ParentViewModel parentVM = container.Resolve<ParentViewModel>();
         container.RegisterInstance<ParentViewModel>(parentVM);

         this.DataContext = parentVM;

     }
}

Working:

public MainWindow()
{

    if (DesignerProperties.GetIsInDesignMode(this))
    {
        this.DataContext = new ParentViewModel();
    }
    else
    {     
         IUnityContainer container = UnityFactory.Retrieve();

         ParentViewModel parentVM = container.Resolve<ParentViewModel>();
         container.RegisterInstance<ParentViewModel>(parentVM );

         this.DataContext = parentVM;
     }

     /**
       * Initialize after registering parent VM and setting the datacontext
       */
     InitializeComponent();

  }
Johnny.Minty
  • 143
  • 1
  • 10