Sharing components between two view models in C# MVVM
Overview
Reqruiement
I have two view models - MainViewModel and OptionViewModel -, that share a common component CustomConfiguration. My goal is to load the data into MainViewModel in MainWindow, and when the Option button ic clicked on MainWindow, OptionWindow will be opened, and the CustomConfiguration data will be passed into it.
Code
MainWindow.xaml
Only bind a data (CustomConfig.GeneralSettings.IsLogEnabled) and assign the command.
1<Window.DataContext>
2 <local:MainViewModel />
3</Window.DataContext>
4<StackPanel>
5 <TextBlock Text="{Binding CustomConfig.GeneralSettings.IsLogEnabled}" />
6 <Button Content="Open Options" Command="{Binding OpenOptionsCommand}" />
7</StackPanel>
OptionWindow.xaml
The same as MainWindow.xaml, only bind a data (CustomConfig.GeneralSettings.IsLogEnabled) and assign the command.
1<Window.DataContext>
2 <local:OptionViewModel />
3</Window.DataContext>
4<StackPanel>
5 <CheckBox Content="Enable Logging" IsChecked="{Binding CustomConfig.GeneralSettings.IsLogEnabled}" />
6 <Button Content="OK" Command="{Binding OKCommand}" />
7</StackPanel>
MainWindow.xaml.cs
No special commands here.
1public partial class MainWindow : Window {
2 public MainWindow() {
3 InitializeComponent();
4 }
5}
OptionWindow.xaml.cs
No special commands here.
1public partial class OptionWindow : Window {
2 public OptionWindow() {
3 InitializeComponent();
4 }
5}
OptionWindow.xaml.cs => If you want to clone data.
In the previous version, the CustomConfiguration instance was passed from MainViewModel into OptionViewModel, so they share one instance. If you want to create a copy of CustomConfiguration in OptionViewModel, you can use this constructor.
1 public OptionWindow(CustomConfiguration config) {
2 InitializeComponent();
3
4 //_initialConfig = config;
5 _viewModel = new OptionViewModel(config.Clone());
6 DataContext = _viewModel;
7 }
CustomConfiguration.cs
1public class CustomConfiguration : INotifyPropertyChanged {
2 private GeneralSettingsClass _generalSettings = new GeneralSettingsClass();
3
4 public GeneralSettingsClass GeneralSettings {
5 get => _generalSettings;
6 set {
7 _generalSettings = value;
8 OnPropertyChanged();
9 }
10 }
11
12 public event PropertyChangedEventHandler PropertyChanged;
13 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
14 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
15 }
16}
17
18public class GeneralSettingsClass : INotifyPropertyChanged {
19 private bool _isLogEnabled = false;
20
21 public bool IsLogEnabled {
22 get => _isLogEnabled;
23 set {
24 _isLogEnabled = value;
25 OnPropertyChanged();
26 }
27 }
28
29 public event PropertyChangedEventHandler PropertyChanged;
30 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
31 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
32 }
33}
MainViewModel.cs
Please remember to check the Shared variable, if you want to share one instance of CustomConfiguration, you can set it to true. If you want to create a copy of CustomConfiguation, you can set it to false.
1public class MainViewModel : INotifyPropertyChanged {
2 public CustomConfiguration CustomConfig { get; } = new CustomConfiguration();
3
4 public RelayCommand OpenOptionsCommand { get; }
5
6 public MainViewModel() {
7 OpenOptionsCommand = new RelayCommand(OpenOptionsCommandExecute);
8 }
9
10 private void OpenOptionsCommandExecute() {
11 bool Shared = false;
12 if (Shared) {
13 // This ViewModel will be shared
14 var optionViewModel = new OptionViewModel(CustomConfig);
15 var optionWindow = new OptionWindow() { DataContext = optionViewModel };
16 optionWindow.ShowDialog();
17 } else {
18 // This ViewModel will be cloned
19 var optionWindow = new OptionWindow(CustomConfig.Clone());
20 optionWindow.ApplyChanges += (s, config) => {
21 this.CustomConfig = config;
22 };
23 optionWindow.ShowDialog();
24 }
25 }
26
27 public event PropertyChangedEventHandler PropertyChanged;
28 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
29 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
30 }
31}
OptionViewModel.cs
1public class OptionViewModel : INotifyPropertyChanged {
2 private CustomConfiguration _customConfig;
3
4 public CustomConfiguration CustomConfig {
5 get => _customConfig;
6 set {
7 _customConfig = value;
8 OnPropertyChanged();
9 }
10 }
11
12 public RelayCommand OKCommand { get; }
13
14 public OptionViewModel(CustomConfiguration customConfig) {
15 CustomConfig = customConfig;
16 OKCommand = new RelayCommand(OKCommandExecute);
17 }
18
19 private void OKCommandExecute() {
20 CustomConfig.Save();
21 // Close the option window
22 (Application.Current.MainWindow as Window)?.Close();
23 }
24
25 public event PropertyChangedEventHandler PropertyChanged;
26 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
27 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
28 }
29}
Test
Case 1: share one instance
- Run the program.
- Click OpenOptions button.
- Click Enable Logging check box.
- You shuold see the flag in MainWindow is switched.
Case 2: Not share one instance (create a copy)
- Set Shared to false in MainViewModel.cs.
- Run the program.
- Click OpenOptions button.
- Click Enable Logging check box.
- You shuold see the flag in MainWindow is NOT changed.