views:

3159

answers:

3

I need to figure out how to communicate between ViewModels. I'm new to MVVM so please be kind.

Here's a dumbed down example

class definitions(assume that I have hooked the Child.PropertyChanged event in the ParentViewModel):

public class ParentViewModel : ViewModelBase
{
    public ChildViewModel Child { get; set; }
}

public class ChildViewModel : ViewModelBase
{
    String _FirstName;
    public String FirstName 
    {
        get { return _FirstName; }
        set
        {
            _FirstName = value;
            OnPropertyChanged("FirstName");
        }
    }
}

Here's what you see in the resource dictionary

<DataTemplate DataType="{x:Type vm:ParentViewModel}">
    <vw:ParentView/>
</DataTemplate>

<DataTemplate DataType="{x:Type vm:ChildViewModel}">
    <vw:ChildView/>
</DataTemplate>

and the code-behind of the ChildView:

public partial class QueueView : UserControl
{
    public QueueView()
    {
        InitializeComponent();
        DataContext = new QueueViewModel();
    }
}

The obvious problem is that when the ChildView gets instantiated (via selection from the DataTemplate) it creates a new ChildViewModel class and the ParentViewModel doesn't have access to it.

So how can I instantiate the DataContext of the View to be the original ViewModel that caused the DataTemplate to be selected?

An obvious fix is to mmerge the properties in the ChildViewModel into the ParentViewModel, but I would rather separate it because for reuse.

I'm sure the answer is trivial, I just would like to know what it is. :)

Thanks in advance.

+3  A: 

You should simply remove the line:

DataContext = new QueueViewModel();

The DataContext of the view will be set automatically by WPF. DataTemplates always have their data context set to the data for the template (in this case the ViewModel):

<DataTemplate DataType="{x:Type vm:ChildViewModel}">
    <vw:ChildView/>
</DataTemplate>

The end result is that you can build your view model objects separately (both parent and child classes) and then display them later by simply plugging them into content controls.

Josh G
If the intent is for a ParentView to contains a ChildView, how does this alone set the DataContext of ChildView to be ParentViewModel.Child?
David Cuccia
Well the scenario would probably look something like this:In a factory or a builder method, you would create the parent and child object. This object would be handed to a view.
Josh G
The view would either display the parent in one content control and the child in another, or it would display both parent and child in a HeaderedContent control.
Josh G
+1  A: 

Let's say you have a QueueView that uses a QueueViewModel.

public class QueueViewModel : INotifyPropertyChanged
{
    public ParentType Parent { get; set; }

    public QueueViewModel(ParentType parent)
    {
        this.Parent = parent;
        foreach (ChildType child in Parent)
        {
            child.PropertyChanged += delegate(object sender,
                PropertyChangedEventArgs e)
            {
                if (e.PropertyName != "IsSelected")
                    return;

                //do something like this:
                Parent.IsSelected = AllChildrenAreSelected();
            };
        }
    }

}

public class ParentType : INotifyPropertyChanged
{
    private bool _isSelected;

    public IList<ChildType> Children { get; set; }
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }
}

public class ChildType : INotifyPropertyChanged
{
    private string _name;
    private bool _isSelected;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }
}

-- QueueView part

<StackPanel>
<CheckBlock Text="{Binding Path=Parent.Name}" 
            IsChecked="{Binding Parent.IsSelected}"/>
<ItemsControl ItemsSource="{Binding Path=Parent.Children}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>                                    
            <CheckBox Content="{Binding Path=Name}"
                      IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"/>
        </DataTemplate>
    <ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Soni Ali
Your code samples don't really seem related to the question. Did one or both of us miss the point?
Josh G
I was assuming that the Child Model was a property of the Parent Model. (e.g. Order + List of Order Details). In which case you can use Property-Change notifications to communicate within the view model. But if they are separate view models, you can use the Mediator Pattern to communicate.
Soni Ali
+2  A: 

The easiest way to communicate between viewModels using the MVVM approach is to use the Mediator pateern (EventAggregator in Prism). A good example of this approach can be seen in the following links:

  1. MVVM Mediator Pattern by Sacha Barber
  2. MVVM + Mediator by marlon grech

Also check out the MVVM Sample project framework http://blogs.msdn.com/llobo/archive/2009/05/01/download-m-v-vm-project-template-toolkit.aspx

Soni Ali