Search code examples
wpfmvvmparent-childviewmodel

Can I connect two ViewModels without DependencyProperty?


I have MainWindow (simplified for clarity):

<Window>
    <!-- (...) -->

    <Window.DataContext>
         <vm:MainWindowViewModel />
    </Window.DataContext>

    <!-- (...) -->

    <CheckBox IsChecked="{Binding ShowAdvanced}" Content="Advanced view" />
    <uc:MyUserControl DataContext={Binding MyUserControlViewModel} />
</Window>

MainWindowViewModel:

public partial class MainWindowViewModel : ViewModelBase
{
    public MainWindowViewModel()
    {
         MyUserControlVM = new MyUserControlViewModel();
    }

    private bool _showAdvanced;
    public bool ShowAdvanced
    {
        get => _showAdvanced;
        set { _showAdvanced = value; NotifyPropertyChanged(); }
    }

    private MyUserControlViewModel _myUserControlVM;
    public MyUserControlViewModel MyUserControlVM
    {
        get => _myUserControlVM;
        set { _myUserControlVM= value; NotifyPropertyChanged(); }
    }
}

In my UserControl I have some controls supposed to be hidden when "Show advanced" checkbox is not checked.

<GroupBox Header="Some advanced stuff"
    Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext.(vm:MainWindowViewModel.ShowAdvanced), Converter={StaticResource BoolToVis}}">
    <!-- (...) -->
</GroupBox>

This actually works, but I don't like this because UserControl relies on MainWindow.

How can I connect these viewmodels correctly without DependencyProperty?

I have tried to add this to MyUserControlViewModel:

public MyUserControlViewModel(MainWindowViewModel parent)
{
    Parent = parent;
}

private MainWindowViewModel _parent;
public MainWindowViewModel Parent
{
    get { return _parent; }
    set { _parent = value; NotifyPropertyChanged(); }
}

and bind visibility on one of MyUserControl controls like this:

Visibility="{Binding Parent.ShowAdvanced}"

but this is not working (MyUserControl is not getting notified?).


Solution

  • Add the ShowAdvanced property to the control VM and assign the value to each control VM whenever a new value is assigned to the MainViewModel ShowAdvanced property.

    public class MainViewModel : Base.ViewModelBase
    {
        private bool _showAdvanced;
    
        public MainViewModel()
        {
            MyUserControl1 = new MyUserControlViewModel { Message = "Control 1" };
            MyUserControl2 = new MyUserControlViewModel { Message = "Control 2" };
            MyUserControl3 = new MyUserControlViewModel { Message = "Control 3" };
        }
    
        public bool ShowAdvanced
        {
            get => _showAdvanced;
            set
            {
                this.RaiseAndSetIfChanged( ref _showAdvanced, value );
                MyUserControl1.ShowAdvanced = value;
                MyUserControl2.ShowAdvanced = value;
                MyUserControl3.ShowAdvanced = value;
            }
        }
    
        public MyUserControlViewModel MyUserControl1 { get; }
        public MyUserControlViewModel MyUserControl2 { get; }
        public MyUserControlViewModel MyUserControl3 { get; }
    }
    
    public class MyUserControlViewModel : Base.ViewModelBase
    {
        private bool _showAdvanced;
        private string _message;
    
        public bool ShowAdvanced { get => _showAdvanced; set => this.RaiseAndSetIfChanged( ref _showAdvanced, value ); }
        public string Message { get => _message; set => this.RaiseAndSetIfChanged( ref _message, value ); }
    }