WPF MVVM:根据另一个数据网格的选定项更新数据网格

WPF MVVM: update datagrid based on selecteditem of another datagrid

Class Diagram 我正在使用 MVVM 开发一个 wpf 应用程序 pattern.I 想根据第一个数据网格的选择更新第二个数据网格,如果第二个数据网格的 Itemsource 有任何变化,我想在保留第一个数据网格的选择的同时更新该更改。谁能帮我解决这个问题。

需求与此大致相似DataGrid SelectionChanged MVVM。但是每当第一个数据网格集合自动更新时,第二个数据网格中的数据必须为第一个数据网格的选定项目更新。

O8Lwl.png

根据您的评论,我创建了一个显示绑定的最小示例。我希望这就是您要找的。

MainWindow.xaml 和后面的代码

<Window x:Class="WpfApp1.MainWindow"
        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="MainWindow" Height="400" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="300*"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <DataGrid Grid.Column="0" Grid.Row="0" Width="Auto" ItemsSource="{Binding DeviceList}" SelectedItem="{Binding SelectedDevice}" AutoGenerateColumns="True" />
        <DataGrid Grid.Column="1" Grid.Row="0" Width="Auto" DataContext="{Binding SelectedDevice}" ItemsSource="{Binding Path=FaultList}" AutoGenerateColumns="True"/>
        <Button Content="Trigger DataGrid 1 update" Grid.Column="0" Grid.Row="1" Margin="10,10,10,10" Width="Auto" Height="Auto" Click="Button_Click"/>
    </Grid>
</Window>

// Code behind in MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        private MainViewModel vm;

        public MainWindow()
        {
            InitializeComponent();
            vm = new MainViewModel();
            DataContext = vm;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            vm.AddDevice();
        }
    }
}

MainViewModel 和 MainModel

using System;
using System.Collections.ObjectModel;
using System.Linq;
using WpfApp1.ViewModels;

namespace WpfApp1
{
    public class MainViewModel : ViewModelBase<MainModel>
    {
        private DeviceViewModel selectedDevice;
        public ObservableCollection<DeviceViewModel> DeviceList
        {
            get { return Model.DeviceList; }
        }
        public DeviceViewModel SelectedDevice
        {
            get { return selectedDevice; }
            set
            {
                selectedDevice = value;
                RaisePropertyChanged("SelectedDevice");
            }
        }
        public MainViewModel() : base(new MainModel())
        {

        }

        public void AddDevice()
        {
            int rnd = new Random().Next(1, 100);
            if (!Model.DeviceList.Any(x => x.Name == $"Device_{rnd}"))
                Model.DeviceList.Add(new DeviceViewModel($"Device_{rnd}"));

            RaisePropertyChanged("DeviceList");
        }
    }
}

using System.Collections.ObjectModel;
using WpfApp1.ViewModels;

namespace WpfApp1
{
    public class MainModel
    {
        private ObservableCollection<DeviceViewModel> deviceList;

        public ObservableCollection<DeviceViewModel> DeviceList
        {
            get { return deviceList; }
            set { deviceList = value; }
        }

        public MainModel()
        {
            deviceList = new ObservableCollection<DeviceViewModel>();
        }
    }
}

DeviceViewModel.cs 和 Device.cs

using System.Collections.ObjectModel;
using WpfApp1.Models;

namespace WpfApp1.ViewModels
{
    public class DeviceViewModel : ViewModelBase<Device>
    {
        private Fault selectedFault = null;

        public string Name
        {
            get { return Model.Name; }
            set
            {
                Model.Name = value;
                RaisePropertyChanged("Name");
            }
        }
        public string SerialNumber
        {
            get { return Model.Id.ToString(); }
        }
        public ObservableCollection<FaultViewModel> FaultList
        {
            get { return Model.FaultList; }
        }
        public Fault SelectedFault
        {
            get { return selectedFault; }
            set
            {
                selectedFault = value;
                RaisePropertyChanged("SelectedFault");
            }
        }

        public DeviceViewModel() : base(new Device())
        {
            FaultList.CollectionChanged += FaultList_CollectionChanged;
        }

        public DeviceViewModel(string name) : this()
        {
            Name = name;

            for (int i = 0; i < 5; i++)
                Model.FaultList.Add(new FaultViewModel() { Name = $"Fault_{i} of {name}" });

            RaisePropertyChanged("FaultList");
        }

        private void FaultList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            RaisePropertyChanged("FaultList");
        }
    }
}


using System;
using System.Collections.ObjectModel;

namespace WpfApp1.Models
{
    public class Device
    {
        private string name = "";
        private Guid id;
        private ObservableCollection<FaultViewModel> faultList;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public Guid Id
        {
            get { return id; }
            set { id = value; }
        }

        public ObservableCollection<FaultViewModel> FaultList
        {
            get { return faultList; }
            set { faultList = value; }
        }

        public Device()
        {
            this.id = new Guid();
            this.faultList = new ObservableCollection<FaultViewModel>();
        }

        public Device(string name) : this()
        {
            this.name = name;
        }
    }
}

FaultViewModel.cs 和 Fault.cs

using WpfApp1.Models;

namespace WpfApp1
{
    public class FaultViewModel : ViewModelBase<Fault>
    {
        public string Name
        {
            get { return Model.FaultName; }
            set
            {
                Model.FaultName = value;
                RaisePropertyChanged("Name");
            }
        }
        public string Id
        {
            get { return Model.FaultId.ToString(); }
        }

        public FaultViewModel() : base(new Fault())
        {

        }
    }
}

using System;

namespace WpfApp1.Models
{
    public class Fault
    {
        private Guid faultId;
        private string faultName;

        public Guid FaultId
        {
            get { return faultId; }
            set { faultId = value; }
        }

        public string FaultName
        {
            get { return faultName; }
            set { faultName = value; }
        }


        public Fault()
        {
            this.faultId = new Guid();
        }
    }
}

最后但同样重要的是:ViewModelBase.cs

using System.ComponentModel;

namespace WpfApp1
{
    public class ViewModelBase<T> : INotifyPropertyChanged
    {
        T model;
        public T Model { get { return model; } }

        public ViewModelBase(T model)
        {
            this.model = model;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null && !string.IsNullOrEmpty(propertyName))
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

如果你运行这个应用程序你可以点击按钮,它在后面的代码中模拟设备列表的更新。然后您可以 select 一个设备,第二个 DataGrid 将显示该设备的 FaultList。


旧答案

更新: 与您的评论和 class 图相关:

第一件事:

我只能在 class 设备中看到一个列表。我假设这是第一个 DataGrid 的来源?因此,您想在第一个 DataGrid 中显示 Fault 类型对象的属性。如果是这样,第二个 DataGrid 的来源在哪里?还是您在 class 错误中缺少集合 属性?

第二个:

对于数据绑定,您必须使用实现 INotifyCollectionChanged 的​​ ObservableCollection。您不能使用列表<>。

第三名:

在没有看到您的代码的情况下,我只能猜测出了什么问题。假设 class 设备包含 ObservableCollection<Fault> FaultList 并且 class 故障包含 ObservableCollection<string> FaultDetails。 DataGrid 1 显示故障列表,如果您 select 其中之一,DataGrid 2 会显示一些故障详细信息。在您的 DeviceViewModel 中,您将拥有 ObservableCollection<Fault> FaultList 和 属性 Fault SelectedFault。现在 FaultList 必须是第一个 DataGrid 的 ItemSource,SelectedFault 必须绑定到 DataGrid.SelectedItem 属性。 Datagrid 2 的 ItemSource 必须是 FaultDetails,DataContext 必须是 SelectedFault。也许您需要传播 属性.

的更改

我还没有测试过这个! 最好能有一个能说明问题的最小可执行示例。


旧答案:

自从我编写上一个 WPF 应用程序以来已经有一段时间了,但可能是您在错误的位置引发了第二个 DataGrid 的 Notify属性Changed 事件,或者您引发了错误的事件属性.

DataGrid 有一个 SelectionChanged 事件。也许您可以从那里访问您的 VM 并为您的第二个 DataGrid 引发正确的 属性Changed 事件。