C# MVVM 中兩個 View Model 之間如何分享組件
文章目錄
需求
我有兩個 View Model — MainViewModel 和 OptionViewModel -, 他們共用一個名為 CustomConfiguration 的元件. 需求是在 MainWindow 中將資料載入 MainViewModel,並當在 MainWindow 上點選 Option 按鈕時, 會開啟 OptionWindow,並將 CustomConfiguration 資料傳遞給它.
Code
MainWindow.xaml
這裡只有 bind data (CustomConfig.GeneralSettings.IsLogEnabled) 和設定 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
和 MainWindow.xaml 類似, 這裡只有 bind data (CustomConfig.GeneralSettings.IsLogEnabled) 和設定 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
這裡沒有特別的指令.
1public partial class MainWindow : Window {
2 public MainWindow() {
3 InitializeComponent();
4 }
5}
OptionWindow.xaml.cs
這裡沒有特別的指令.
1public partial class OptionWindow : Window {
2 public OptionWindow() {
3 InitializeComponent();
4 }
5}
OptionWindow.xaml.cs => If you want to clone data.
在前一個版本中,CustomConfiguration 的 instance 從 MainViewModel 傳到 OptionViewModel,這樣它們就共用一個實例. 如果你想要在 OptionViewModel 中 複製 CustomConfiguration,你可以使用這個 constructor. => 差別在於, 你在 OptionViewModel 中的改變, 不會即時反應到 MainViewModel.
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
如果您想要共用 CustomConfiguration 的一個實例,請務必檢查 Shared 變數,並將其設置為 true. 如果您想要在 OptionViewModel 中創建一個 CustomConfiguation 的副本,請將其設置為 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: 共用一個 instance
- Run the program.
- Click OpenOptions button.
- Click Enable Logging check box.
- You shuold see the flag in MainWindow is switched.
Case 2: 不共用一個 instance
- 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.