【问题标题】:Accessing data from different ViewModels从不同的 ViewModel 访问数据
【发布时间】:2019-02-22 11:38:15
【问题描述】:

我想在一个视图中访问不同 ViewModel 的数据。 但是 DataContext 是完全不同的(MainView 中的 MainViewModel)。是否可以为每个窗口控件设置各自的 ViewModel? 还是只在 MainViewModel 中创建和引用 ObservableCollection<Student> Students 更好?

目前我想将 ViewModel StudentViewModel 中的属性 Students 分配给此 ComboBox。

MainViewModel(将 ApplicationViewModel 设置为 CurrentViewModel)

public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            CurrentViewModel = ApplicationViewModel;            
            ShowStudentViewCommand = new RelayCommand(ShowStudentView);
        }


        public ViewModelBase CurrentViewModel
        {
            get => _currentViewModel;

            set
            {
                if (_currentViewModel == value) return;
                _currentViewModel = value;
                RaisePropertyChanged("CurrentViewModel");
            }
        }


        private ViewModelBase _currentViewModel;
        private static readonly ApplicationViewModel ApplicationViewModel = new ApplicationViewModel();
        private static readonly StudentViewModel StudentViewModel = new StudentViewModel();



        public ICommand ShowStudentViewCommand { get; }        
        public ICommand ShowApplicationViewCommand { get; }


        private void ShowStudentView()
        {
            CurrentViewModel = StudentViewModel;
        }


        private void ShowApplicationView()
        {
            CurrentViewModel = ApplicationViewModel;
        }     

    }

ApplicationViewModel 和 StudentViewModel(加载数据并创建 ObservableCollection)

public class ApplicationViewModel : ViewModelBase
    {
        public ApplicationViewModel()
        {

        }
    }


 public class StudentViewModel : ViewModelBase
 {
        private ObservableCollection<Student> _students;

        public StudentViewModel()
        {
            DataStudentService dataService = new DataStudentService();
            Students = new ObservableCollection<Student>(dataService.GetAllStudents());
        }

        public ObservableCollection<Student> Students
        {
            get => _students;

            private set
            {
                if (_students == value) return;
                _students = value;
                RaisePropertyChanged("Students");
            }
        }
    }

MainView.xaml(设置要显示的 CurrentViewModel)

<Window x:Class="Test.View.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="Test" Height="750" Width="700" 

        DataContext="{Binding Main, Source={StaticResource Locator}}">

    <Grid>
        <ContentControl Grid.Row="1" Content="{Binding CurrentViewModel}" />
    </Grid>
</Window>

ApplicationView.xaml(当前显示)

<UserControl x:Class="Test.View.ApplicationView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:viewModel="clr-namespace:Test.ViewModel"
             mc:Ignorable="d">
<Grid >


    <ComboBox
        TextSearch.TextPath=""
        ItemsSource="{Binding Path=Students}"
        DisplayMemberPath="Name"
        SelectedValuePath="Name"
        SelectedValue="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
        SelectedItem="{Binding Path=SelectedStudent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
        SelectedIndex="{Binding Path=SelectedStudentIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
        IsSynchronizedWithCurrentItem="True"
        IsEditable="True"
        Width="200"
        Margin="0,0,20,0"
        VerticalContentAlignment="Center" />



    </Grid>
</UserControl>

【问题讨论】:

  • 请注意,同时绑定 SelectedValue、SelectedItem 和 SelectedIndex 看起来很奇怪。您通常只会绑定其中一个。在所有绑定上设置Mode=TwoWayUpdateSourceTrigger=PropertyChanged 也是多余的。虽然这些设置已经是 Selected... 属性的默认值,但它们对 ItemsSource 绑定完全没有影响。
  • @Clemens:感谢您的提示,我使用 SelectedStudentIndex 重置选择(“-1”)。我正在尝试减少其他参数
  • 一个ViewModel可以有(子)ViewModels的属性,没问题。
  • 仅使用 SelectedValue,重置应该通过将 Name 设置为不存在的值来工作,例如空值。或者没有 SelectedValue(Path),将 SelectedItem 设置为 null。
  • 是的,请注意它是Binding Path=Students 而不是Binding Property=Students。路径意味着它可以是 a.b.c

标签: c# wpf mvvm viewmodel mvvm-light


【解决方案1】:

最直接的解决方案是坚持 MVVM 模式,方法是创建一个集合并用您已经提到的其他类的数据填充它。这很容易,每个人都知道发生了什么。此外,这样数据的 context 将保持在相同的视图模型中,并且 - 如果您更改某些内容 - 它也会保持在该上下文中。在您的情况下,这意味着您必须创建一个 ObservableCollection&lt;Student&gt; Students 并将其内容填写在 MainViewModel 中。


如果必须从整个应用程序访问该数据,您应该考虑实现 DataManager(例如单例),而不是在每个视图模型中复制数据。

【讨论】:

  • 感谢您的回复!不幸的是,我有太多 ObservableCollections。我想在各自的视图模型中保留所有相同的类元素
【解决方案2】:

找到解决办法:

MainViewModel

public class MainViewModel : ViewModelBase
{
    public StudentViewModel StudentViewModel { get; set; }

    public MainViewModel()
    {
        StudentViewModel = new StudentViewModel();
    }
}

主视图

<ComboBox
DataContext="{Binding StudentViewModel}"
ItemsSource="{Binding Path=Students, UpdateSourceTrigger=PropertyChanged}"
/>

StudentViewModel

private ObservableCollection<Student> _students;

public ObservableCollection<Student> Students
    {
        get => _students;
        set
            {
                if (_students == value) return;
                _students = value;
                RaisePropertyChanged("Students");
            }
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-03
    相关资源
    最近更新 更多