One ViewModel for UserControl and Window or separate ViewModels

Part 1. Display the properties of the control in MVVM

As I said in comments:

In MVVM ViewModel should not know about the controls, which are located. In such cases, use the attached behavior or leave the same side logic in View

ViewModel is not directly associated with a View, so just refer to the name of the control would not be right. It would be better to set a property in the Model, and bind it into View via ViewModel, but the property Name does not support Binding (quote from the MSDN):

Data binding a Name is technically possible, but is an extremely uncommon scenario because a data-bound Name cannot serve the main intended purpose of the property: to provide an identifier connection point for code-behind.

so I suggest to use the Tag property or Uid. In my example (give an below), I use Uid property for these purposes.

Part 2. Communication via ViewModels (pattern Mediator)

There are several embodiments of the Mediator pattern, but I like the most the implementation by XAML Guy, it is simple and clear – The Mediator Pattern.

Implementation code

public static class Mediator
{
    static IDictionary<string, List<Action<object>>> pl_dict = new Dictionary<string, List<Action<object>>>();

    static public void Register(string token, Action<object> callback)
    {
        if (!pl_dict.ContainsKey(token))
        {
            var list = new List<Action<object>>();
            list.Add(callback);
            pl_dict.Add(token, list);
        }
        else
        {
            bool found = false;
            foreach (var item in pl_dict[token])
                if (item.Method.ToString() == callback.Method.ToString())
                    found = true;
            if (!found)
                pl_dict[token].Add(callback);
        }
    }

    static public void Unregister(string token, Action<object> callback)
    {
        if (pl_dict.ContainsKey(token))
        {
            pl_dict[token].Remove(callback);
        }
    }

    static public void NotifyColleagues(string token, object args)
    {
        if (pl_dict.ContainsKey(token))
        {
            foreach (var callback in pl_dict[token])
                callback(args);
        }
    }
}

To demonstrate his work, I created a small example, which consists of two Views, each has its own ViewModel and Model.

The project structure is shown below:

Project structure

Output

Using example

When you click on Button, ListOfData ViewModel communicates via mediator with DataDetails ViewModel, thus:

Mediator.NotifyColleagues("ShowDetails", true);
Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen);

All procedures that interact with the properties must register their ViewModel like this:

private void ShowDetails_Mediator(object args)
{
    bool showDetails = (bool)args;

    if (showDetails == true)
    {
        DataDetailsModel.IsVisible = true;
    }
    else
    {
        DataDetailsModel.IsVisible = false;
    }
}

private void SetSelectedFruit_Mediator(object args)
{
    string selectedFruit = (string)args;

    DataDetailsModel.SelectedFruit = selectedFruit;
}

public DataDetailsViewModel() 
{
    DataDetailsModel = new DataDetailsModel();

    Mediator.Register("ShowDetails", ShowDetails_Mediator);
    Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator);
}

In the example I used a DataTemplate instead UserControl. Below are the main part of the project:

MainWindow.xaml

<Window x:Class="CommunicateWithVM.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels"
    Title="MainWindow" 
    WindowStartupLocation="CenterScreen"
    Height="350"
    Width="525">

    <Grid>
        <ContentControl Name="ListOfData"
                        ContentTemplate="{StaticResource ListOfDataView}">

            <ViewModels:ListOfDataViewModel />            
        </ContentControl>

        <ContentControl Name="DataDetails"
                        ContentTemplate="{StaticResource DataDetailsView}">

            <ViewModels:DataDetailsViewModel />
        </ContentControl>
    </Grid>
</Window> 

Models

DataDetailsModel

public class DataDetailsModel : NotificationObject
{
    #region SelectedFruit

    private string _selectedFruit = "";

    public string SelectedFruit
    {
        get
        {
            return _selectedFruit;
        }

        set
        {
            _selectedFruit = value;
            NotifyPropertyChanged("SelectedFruit");
        }
    }

    #endregion

    #region IsVisible

    private bool _isVisible = false;

    public bool IsVisible
    {
        get
        {
            return _isVisible;
        }

        set
        {
            _isVisible = value;
            NotifyPropertyChanged("IsVisible");
        }
    }

    #endregion
}

ListOfDataModel

public class ListOfDataModel : NotificationObject
{
    #region FruitGreen

    private string _fruitGreen = "Apple";

    public string FruitGreen
    {
        get
        {
            return _fruitGreen;
        }

        set
        {
            _fruitGreen = value;
            NotifyPropertyChanged("FruitGreen");
        }
    }

    #endregion

    #region FruitYellow

    private string _fruitYellow = "Limon";

    public string FruitYellow
    {
        get
        {
            return _fruitYellow;
        }

        set
        {
            _fruitYellow = value;
            NotifyPropertyChanged("FruitYellow");
        }
    }

    #endregion
}

ViewModels

DataDetailsViewModel

public class DataDetailsViewModel
{
    #region DataDetailsModel

    private DataDetailsModel _dataDetailsModel = null;

    public DataDetailsModel DataDetailsModel
    {
        get
        {
            return _dataDetailsModel;
        }

        set
        {
            _dataDetailsModel = value;
        }
    }

    #endregion

    #region ShowDetails_Mediator

    private void ShowDetails_Mediator(object args)
    {
        bool showDetails = (bool)args;

        if (showDetails == true)
        {
            DataDetailsModel.IsVisible = true;
        }
        else
        {
            DataDetailsModel.IsVisible = false;
        }
    }

    #endregion

    #region SetSelectedFruit_Mediator

    private void SetSelectedFruit_Mediator(object args)
    {
        string selectedFruit = (string)args;

        DataDetailsModel.SelectedFruit = selectedFruit;
    }

    #endregion

    #region DataDetailsViewModel Constructor

    public DataDetailsViewModel() 
    {
        DataDetailsModel = new DataDetailsModel();

        Mediator.Register("ShowDetails", ShowDetails_Mediator);
        Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator);
    }

    #endregion
}

ListOfDataViewModel

public class ListOfDataViewModel
{
    #region ListOfDataModel

    private ListOfDataModel _listOfDataModel = null;

    public ListOfDataModel ListOfDataModel
    {
        get
        {
            return _listOfDataModel;
        }

        set
        {
            _listOfDataModel = value;
        }
    }

    #endregion

    #region GreenButtonCommand

    private ICommand _greenButtonCommand = null;

    public ICommand GreenButtonCommand
    {
        get
        {
            if (_greenButtonCommand == null)
            {
                _greenButtonCommand = new RelayCommand(param => this.GreenButton(), null);
            }

            return _greenButtonCommand;
        }
    }

    private void GreenButton()
    {
        Mediator.NotifyColleagues("ShowDetails", true);
        Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen);
    }

    #endregion

    #region YellowButtonCommand

    private ICommand _yellowButtonCommand = null;

    public ICommand YellowButtonCommand
    {
        get
        {
            if (_yellowButtonCommand == null)
            {
                _yellowButtonCommand = new RelayCommand(param => this.YellowButton(), null);
            }

            return _yellowButtonCommand;
        }
    }

    private void YellowButton()
    {
        Mediator.NotifyColleagues("ShowDetails", true);
        Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitYellow);
    }

    #endregion

    #region ListOfDataViewModel Constructor

    public ListOfDataViewModel() 
    {
        ListOfDataModel = new ListOfDataModel();
    }

    #endregion
}

Views

DataDetailsView

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels">

    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />

    <DataTemplate x:Key="DataDetailsView" DataType="{x:Type ViewModels:DataDetailsViewModel}">
        <StackPanel Width="200" 
                    Background="AliceBlue"
                    HorizontalAlignment="Right"
                    Visibility="{Binding Path=DataDetailsModel.IsVisible, 
                                         Converter={StaticResource BooleanToVisibilityConverter}}">

            <TextBlock Text="Fruit: " />
            <TextBlock Text="{Binding Path=DataDetailsModel.SelectedFruit}" />
        </StackPanel>
    </DataTemplate>    
</ResourceDictionary>

ListOfDataView

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels">

    <DataTemplate x:Key="ListOfDataView" DataType="{x:Type ViewModels:ListOfDataViewModel}">
        <StackPanel Width="200" 
                    Background="Azure"
                    HorizontalAlignment="Left">

            <Button Uid="{Binding Path=ListOfDataModel.FruitGreen}"
                    Content="GreenButton"
                    Command="{Binding Path=GreenButtonCommand}" />

            <Button Uid="{Binding Path=ListOfDataModel.FruitYellow}"
                    Content="YellowButton" 
                    Command="{Binding Path=YellowButtonCommand}" />
        </StackPanel>
    </DataTemplate>    
</ResourceDictionary>

This project is available at this link.

Leave a Comment