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 事件。
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 事件。