views:

167

answers:

1

Hi!

I have two questions regarding communication between ViewModels.

I am developing a customer management program. I'm using Laurent Bugnion's MVVM Light framework.

  1. In the main page, there's a list of customers. when each customer is clicked, a child windows shows up with information about that customer. the user should be able to open up multiple child windows at the same time and compare information between customers. how do you pass customer object from the main page's ViewModel to the child window's ViewModel in an MVVM-friendly fashion?

  2. In the child window that shows customer information, there are a number of tabs, each showing different areas of information. I've created separate ViewModels for each of the tabs. how can you share the current customer information between each tab's viewmodels?

Thanks a lot!

A: 

In my project I'm passing ViewModels to child windows too. I create a dependency property for the ViewModel in my child window's code behind and in the setter of this property I pass the ViewModel along to my child window's ViewModel. This means you're creating a separate ViewModel class just for your child window.

To answer your second question, you could have your child window's ViewModel contain properties that each tab cares about, but have their data context still be the same as the child window's data context so they have access to shared properties. This is actually very easy since they automatically get the child window's data context.

Here's an example illustrating the two concepts above.

The child window view DetailsWindow.xaml (note that I've gotten in the habit of naming my child window views *Window.xaml instead of *View.xaml)

<controls:ChildWindow x:Class="DetailsWindow"
                      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                      xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
                      xmlns:Views="clr-namespace:Views"
                      Title="Details"
                      DataContext="{Binding DetailsWindowViewModel, Source={StaticResource Locator}}"
                      >
    <Grid>
        <sdk:TabControl>
            <sdk:TabItem Header="First Tab" Content="{Binding FirstTabContent}" />
            <sdk:TabItem Header="Second Tab" Content="{Binding SecondTabContent}" />
        </sdk:TabControl>
    </Grid>
</controls:ChildWindow>

The child window view's code behind DetailsWindow.xaml.cs and its interface IDetailsWindow.cs

public partial class DetailsWindow : ChildWindow, IDetailsWindow
{
    private IDetailsWindowViewModel ViewModel
    {
        get { return this.DataContext as IDetailsWindowViewModel; }
    }

    public DetailsWindow()
    {
        InitializeComponent();
    }

    #region Customer dependency property

    public const string CustomerViewModelPropertyName = "Customer";

    public ICustomerViewModel Customer
    {
        get
        {
            return (ICustomerViewModel)GetValue(CustomerViewModelProperty);
        }
        set
        {
            SetValue(CustomerViewModelProperty, value);
            if (ViewModel != null)
            {
                ViewModel.Customer = value;
            }
        }
    }

    public static readonly DependencyProperty CustomerViewModelProperty = DependencyProperty.Register(
        CustomerViewModelPropertyName,
        typeof(ICustomerViewModel),
        typeof(CustomerDetailsWindow),
        null);

    #endregion
}

public interface IDetailsWindow
{
    ICustomerViewModel Customer { get; set; }
    void Show();
}

The child window view model DetailsWindowViewModel.cs and its interface IDetailsWindowViewModel

public class DetailsWindowViewModel : ViewModelBase, IDetailsWindowViewModel
{
    public DetailsWindowViewModel(IMessenger messenger)
        : base(messenger)
    {
    }

    #region Properties

    #region Customer Property

    public const string CustomerPropertyName = "Customer";

    private ICustomerViewModel _customer;

    public ICustomerViewModel Customer
    {
        get { return _customer; }
        set
        {
            if (_customer == value)
                return;

            var oldValue = _customer;
            _customer = value;
            RaisePropertyChanged(CustomerPropertyName, oldValue, value, true);
        }
    }

    #endregion

    #region FirstTabContent Property

    public const string FirstTabContentPropertyName = "FirstTabContent";

    private FrameworkElement _firstTabContent;

    public FrameworkElement FirstTabContent
    {
        get { return _firstTabContent; }
        set
        {
            if (_firstTabContent == value)
                return;

            _firstTabContent = value;
            RaisePropertyChanged(FirstTabContentPropertyName);
        }
    }

    #endregion

    #region SecondTabContent Property

    public const string SecondTabContentPropertyName = "SecondTabContent";

    private FrameworkElement _secondTabContent;

    public FrameworkElement SecondTabContent
    {
        get { return _secondTabContent; }
        set
        {
            if (_secondTabContent == value)
                return;

            _secondTabContent = value;
            RaisePropertyChanged(SecondTabContentPropertyName);
        }
    }

    #endregion

    #endregion
}

public interface IDetailsWindowViewModel
{
    ICustomerViewModel Customer { get; set; }
    FrameworkElement FirstTabContent { get; set; }
    FrameworkElement SecondTabContent { get; set; }

    void Cleanup();
}

And you can show the child window from your MainPageViewModel.cs like this.

public class MainViewModel : ViewModelBase, IMainViewModel
{
    private readonly IDetailsWindow _detailsWindow;

    public MainViewModel(IMessenger messenger, IDetailsWindow DetailsWindow)
        : base(messenger)
    {
        _detailsWindow = DetailsWindow;
    }

    private void DisplayCustomerDetails(ICustomerViewModel customerToDisplay)
    {
        _detailsWindow.Customer = customerToDisplay;
        _detailsWindow.Show();
    }
}

Note that I create interfaces for all of my view models and child windows and I use an DI/IoC container in my ViewModelLocator so that all of my ViewModels' dependencies are injected for me. You don't have to do this, but I like how it works.

Matt Casto