Question

I'm working on a Surface WPF project where we try to implement the MVVM pattern. Within this project we are building a few custom controls which we bind to different viewmodels.

For example we have a settings control which has a settings viewmodel and we have a mainviewmodel which is the "overall" viewmodel.

In our surfacewindows.xaml page we are setting the datacontext to the main viewmodel by using the viewmodel locator in mvvm-light. Also on our surfacewindow.xaml we have added our settings control and on the control we have set the datacontext to the settings viewmodel.

Now we need both viewmodels to interact with each other: The current case is that we need to set the visibility of the settings control. We have a property on the main viewmodel that is a boolean (IsSettingsControlVisible) which is bound to the controls Visibility property by using a converter to convert the boolean to a visibility object.

The problem arises now when we need to set the visibility to not visible by clicking on a close button on the settings control. Because we have set the datacontext on the control to the settings viewmodel, we cannot access the mainviewmodel.

What we have thought of until now is adding the settings viewmodel as a property to the mainviewmodel and remove the datacontext from the settings control. In the settingscontrol we will than use the binding as SettingsProperty.Property. Than we can access the mainviewmodel too from the setttings control. Does that make sense? Are there better ways of doing these kind of interactions?

I really like to hear your ideas about how to make these interactions happen.

Was it helpful?

Solution

I tend to work with graphs of view models that are constructed using Castle Windsor. The top level view model uses constructor injection to receive the next level of view models that it requires. And in the views I bind content presenters to properties on the view models to create the corresponding view graph.

Doing this, it's quite easy for parent child view models to communicate, but a bit harder for sibling or more distant view models to communicate.

In these instances, I tend to use an event aggregator, or Messenger to allow the view models to communicate.

OTHER TIPS

As you are already using MVVMLight, I'd suggest using the MVVM Light toolkits Messenger system. It's intended for message exchange between ViewModels. The concept behind is the Mediator pattern where different objects exchange information without knowing each other.

Here's an example:

In the SettingsViewModel register to an event that tells to show the settings dialog

public SettingsViewModel()
{
  Messenger.Default.Register<ShowSettingsMessage>(this, ShowSettingsDialog);
}

private void ShowSettingsDialog(ShowSettingsMessage showSettingsMessage)
{
  // Set the visibility:
  this.IsVisible = showSettingsMessage.Content;
}

In your MainViewModel you send the notification, wrapped in a Message:

// make the settings visible, e.g. the button click command:    
Messenger.Default.Send(new ShowSettingsMessage(true));

And here's the message:

// the message:
public class ShowSettingsMessage : GenericMessage<bool>
   {
     public ShowSettingsMessage(bool isVisible)
       : base(isVisible)
     {  }
   }

I wouldn't recommend making the SettingsViewModel a property of the Mainviewmodel as you lose the possibility to use the SettingsViewModel in a different context or even remove/exchange it.

Try to create a Dependency Property on the Settings control called IsSettingControlVisible and bind it with the parent viewModel.

EDIT:

public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }


        public int MyProperty
        {
            get { return (int)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.Register("MyProperty", typeof(int), typeof(UserControl1), new UIPropertyMetadata(0));
    }

and use it like this...

 <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <local:UserControl1 MyProperty="{Binding Path=ParentViewModelProperty, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" />
        </Grid>
    </Window>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top