【问题标题】:Create a ViewModel with sub ViewModel使用子 ViewModel 创建 ViewModel
【发布时间】:2016-07-27 13:25:03
【问题描述】:

是否有合适的方法来创建包含 subViewModel 的 C#/WPF ViewModel ?

目标是:

我有一个主窗口。该窗口用于读取/创建图像。窗口上有一个按钮,可以在 2 个 UserControl 之间切换,一个带有 IHM 用于读取图像,另一个用于创建。

MainWindow 有一个 MainWindowViewModel :

  • 命令开关
  • 图片长度
  • 应用参数

我希望两个 UserControl 都可以访问 MainWindowViewModel 字段/属性并拥有自己的命令。

构造将是这样的:

public partial class ReadUserControl : UserControl
{
    public ReadUserControl()
    {
        InitializeComponent();
        DataContext = MainViewModel.ReadViewModel;
    }
}

public partial class CreateUserControl : UserControl
{
    public CreateUserControl()
    {
        InitializeComponent();
        DataContext = MainViewModel.CreateViewModel;
    }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = MainViewModel;
    }
}

例如,如果 MainViewModel 包含字段 ImageWidth,则在 CreateUserControl 中设置 ImageWidth 更改 ReadUserControl 的值。

我希望已经清楚了,我不知道如何设计我的 MainViewModel 来达到这个结果

编辑1:

我已将MainWindowViewModel 创建为单身人士,但我仍然无法获得MainViewModel.CreateViewModelMainViewModel.ReadViewModel

public class MainWindowViewModel : ViewModelBase
{   
    private static MainWindowViewModel _instance = null;
    public static MainWindowViewModel Instance
    {
        get
        {
            if (_instance == null)
                _instance = new MainWindowViewModel();
            return _instance;
        }
    }
    private MainWindowViewModel()
        : base()
    {
    }

    #region CreateViewModel
    /* How to create ? */
    #endregion
    #region ReadViewModel
    /* How to create ? */
    #endregion
}

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    您的示例将起作用。至少如果您已将 MainViewModel 设为 Singleton。

    专业的方法可能是这样的构造函数注入。

    public partial class ReadUserControl : UserControl
    {
        public ReadUserControl(MainViewModel vm)
        {
            InitializeComponent();
            DataContext = vm.ReadViewModel;
        }
    }
    

    使用此类 DependencyInjections,您可以实现更高级别的抽象,因为您的 UserControl 可以泛化。 (它们都将具有相同的构造函数)

    另一方面,您赋予每个这样的 UserControl 操作 MainViewModel 的能力,而不知道副作用。

    在您的特殊情况下,只将所需的参数传递给 UserControl 会更安全,而不是给他们一堆信息,他们永远不需要。

    public partial class ReadUserControl : UserControl
        {
            public ReadUserControl(Icommand command, int imageLength, AppParams appParams)
            {
                InitializeComponent();
                ...
                // Do with your Constructorparameters what ever you have to
            }
    }
    

    编辑:

    这里有一个小而笨的实现可以完成:

    代码

     public class MainViewModel : INotifyPropertyChanged {
        private INotifyPropertyChanged _selectedViewModel;
    
        public MainViewModel() {
          var cmd = new RelayCommand(x => {
            MessageBox.Show("HelloWorld");
          }, x => true);
          this.RVM = new ReadViewModel(cmd);
          this.WVM = new WriteViewModel(cmd);
          this.SelectedViewModel = WVM;
        }
    
        private ICommand _switchViewModelCommand;
    
        public ICommand SwitchViewModelCommand => this._switchViewModelCommand ?? (this._switchViewModelCommand = new RelayCommand(x => {
          if (this.SelectedViewModel == RVM) {
    
            this.SelectedViewModel = WVM;
            return;
          }
          this.SelectedViewModel = RVM;
        }));
    
        public INotifyPropertyChanged SelectedViewModel {
          get {
            return this._selectedViewModel;
          }
          set {
            if (Equals(value, this._selectedViewModel))
              return;
            this._selectedViewModel = value;
            this.OnPropertyChanged();
          }
        }
    
        public ReadViewModel RVM {
          get; set;
        }
    
        public WriteViewModel WVM {
          get; set;
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
          this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
      }
    
      public class ReadViewModel : INotifyPropertyChanged {
        public ReadViewModel(ICommand sayHelloCommand) {
          this.HelloCommand = sayHelloCommand;
        }
    
        public ICommand HelloCommand {
          get;
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
          this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
      }
    
      public class WriteViewModel : INotifyPropertyChanged {
    
        public WriteViewModel(ICommand sayHelloCommand) {
          this.HelloCommand = sayHelloCommand;
        }
    
        public ICommand HelloCommand {
          get;
        }
    
        public ICommand HelloMoonCommand => new RelayCommand(x => { MessageBox.Show("Hello Moon"); });
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
          this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    
        }
      }
    

    XAML

     <Window.DataContext>
            <local:MainViewModel/>
        </Window.DataContext>
    
        <Grid Height="200">
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <ContentControl Content="{Binding SelectedViewModel, UpdateSourceTrigger=PropertyChanged}">
                <ContentControl.Resources>
                    <DataTemplate DataType="{x:Type local:ReadViewModel}">
                        <StackPanel>
                            <Button Content="Say Hello world" Command="{Binding HelloCommand}"></Button>
                        </StackPanel>
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type local:WriteViewModel}">
                        <StackPanel>
                            <Button Content="Say Hello world" Command="{Binding HelloCommand}"></Button>
                            <Button Content="Say Hello Moon" Command="{Binding HelloMoonCommand}"></Button>
                        </StackPanel>
                    </DataTemplate>
                </ContentControl.Resources>
            </ContentControl>
            <Button Content="Switch VM" Command="{Binding SwitchViewModelCommand}" Grid.Row="1"/>
        </Grid>
    

    【讨论】:

    • 我添加了 EDIT1: MainWindowViewModel as a Singleton 在我看来是个好主意,但我不知道如何创建 subViewModels(添加的参数将难以维护,因为我将添加其他参数,并且我是唯一开发此应用程序的人,我可以避免副作用)
    • 我在做一个小而实用的例子...等一下
    • @A.Pissicat 添加了示例。注意:一切都符合 MVVM,并且没有代码隐藏。 DataContext 由DataTemplate 设置。此外,您还有一个 ConstructorInjection 的示例
    【解决方案2】:

    您可以将 MainViewModel 作为 DataContext 传递给您的用户控件,并将元素的数据上下文设置为读取/创建模型

    类似

    <Grid> <!--using MainWindowViewModel as data context-->
        <Grid DataContext="{Binding Path=CreateViewModel}"> <!--using CreateViewModel as data context-->
              .....
        </Grid>
    <Grid>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-09
      • 1970-01-01
      相关资源
      最近更新 更多