【问题标题】:WPF MVVM: update datagrid based on selecteditem of another datagridWPF MVVM:根据另一个数据网格的选定项更新数据网格
【发布时间】:2019-10-30 09:53:09
【问题描述】:

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

需求或多或少类似于这个DataGrid SelectionChanged MVVM。但是每当第一个数据网格集合中有更新时,第二个数据网格中的数据必须针对第一个数据网格的选定项进行更新。

O8Lwl.png

【问题讨论】:

  • 欢迎来到 SO。您能否添加一个显示问题的示例?另见:How to create a minimal, reproducible example.
  • 我已经为链接到两个网格的类设置了 INotify 属性。第一个网格在更新类对象时得到更新,但第二个没有得到更新
  • 附加的图像没有帮助。请编辑您的问题并添加一些代码。添加尽可能多的代码,但尽可能少。

标签: c# wpf datagrid mvvm-light


【解决方案1】:

根据您的 cmets,我创建了一个显示绑定的最小示例。我希望这就是你要找的。​​p>

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));
        }
    }
}

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


旧答案

更新:与您的 cmets 和类图相关:

第一件事:

我只能在设备类中看到一个列表。我假设这是第一个 DataGrid 的来源?因此,您希望在第一个 DataGrid 中显示 Fault 类型的对象的属性。如果是这样,第二个 DataGrid 的来源在哪里?或者您是否缺少类 Fault 中的 Collection 属性?

第二个:

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

第三:

没有看到你的代码,我只能猜测出了什么问题。假设 Device 类包含 ObservableCollection&lt;Fault&gt; FaultList,Fault 类包含 ObservableCollection&lt;string&gt; FaultDetails。 DataGrid 1 显示故障列表,如果您选择其中之一,DataGrid 2 将显示一些故障详细信息。在您的 DeviceViewModel 中,您将拥有 ObservableCollection&lt;Fault&gt; FaultList 和属性 Fault SelectedFault。现在 FaultList 必须是第一个 DataGrid 的 ItemSource 并且 SelectedFault 必须绑定到 DataGrid.SelectedItem 属性。 Datagrid 2 的 ItemSource 必须是 FaultDetails,DataContext 必须是 SelectedFault。也许你需要传播属性的变化。

我没有测试过这个! 显示问题的最小可执行示例会很棒。


旧答案:

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

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

【讨论】:

  • 我有一个 A 类,其中包含另一个 B 类的列表。第二个数据网格的数据将从 A 类的选定对象中的 B 类列表更新。第一个数据网格正在响应当类 A 对象更新时,INotify 属性很好。但是只有当我取消选择所选项目并再次选择时,第二个数据网格才会更新。不知道我哪里出错了。
  • 感谢您的回答。 Device 类中的 List 在处理业务逻辑中的设备数据时更新,并且设备数据在第一个数据网格中更新。在选择第一个数据网格中的设备项时,使用视图模型中的可观察故障类集合更新第二个数据网格。将 Device 类中的 List 更改为 Observable 集合会自动更新数据网格中当前所选设备的新故障吗?
  • 非常感谢您提供的最小示例。我已经采用了类似的方法。如果我需要任何进一步的帮助,会及时通知您。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-03
  • 1970-01-01
  • 1970-01-01
  • 2012-06-29
  • 1970-01-01
  • 2016-04-16
  • 2016-05-12
相关资源
最近更新 更多