Wpf:如何从嵌套的 DataGrid 中绑定 SelectedItem

Posted

技术标签:

【中文标题】Wpf:如何从嵌套的 DataGrid 中绑定 SelectedItem【英文标题】:Wpf: How to bind SelectedItem from nested DataGrid 【发布时间】:2021-12-08 15:11:27 【问题描述】:

我不知道如何从嵌套的 DataGrid 中正确绑定 SelectedItem。在主 DataGrid 中,我有一个这样的绑定:

SelectedItem="Binding SelectedElement"

它工作正常 - 如果我在 MainVM 类的 DataGrid 属性 SelectedElement 中选择 element 设置为所选元素。 我在嵌套的 DataGrid 中有类似的绑定:

SelectedItem="Binding SelectedMyItem"

但它根本不起作用 - 当我在嵌套 DataGrid 中选择 item 时,属性 SelectedMyItem 仍然是 null

我的问题: 如何绑定 SelectedMyItem 属性,以便在 DataGrid 中选择项目后对其进行设置?

我没有从 IDE 获得任何绑定错误信息。

这是一个显示我的问题的简单示例:

类:

using System.Collections.ObjectModel;

namespace NesteGridMVVM

    public class MyItem
    
        public string MyItemName  get; set; 
    
    
    //======================================================================
    
    public class Element
    
        private MyItem _selectedItem;

        public string ElementName  get; set; 
        public ObservableCollection<MyItem> MyItemsList  get; set;  = new ObservableCollection<MyItem>();

        //Binded to SelectedItem in nested DataGrid
        public MyItem SelectedMyItem
        
            get => _selectedItem;
            set
            
                _selectedItem = value;

                //Show, if MyItem was selected - it not work.
                System.Diagnostics.Debug.Print($"Selected MyItem: _selectedItem.MyItemName");
            
        
    
    
    //======================================================================

    public class MainVM
    
        private Element _selectedElement;

        public ObservableCollection<Element> ElementsList  get; set;  = new ObservableCollection<Element>();

        //Binded to SelectedItem in main DataGrid
        public Element SelectedElement
        
            get => _selectedElement;
            set
            
                _selectedElement = value;

                //Show, if Element was selected - it works OK
                System.Diagnostics.Debug.Print($"_selectedElement.ElementName");
            
        

        //ctor - populate view model
        public MainVM()
        
            Element elem1 = new Element()  ElementName = "element-01" ;
            Element elem2 = new Element()  ElementName = "element-02" ;

            elem1.MyItemsList.Add(new MyItem()  MyItemName = "item-A" );
            elem1.MyItemsList.Add(new MyItem()  MyItemName = "item-B" );
            elem2.MyItemsList.Add(new MyItem()  MyItemName = "item-C" );

            ElementsList.Add(elem1);
            ElementsList.Add(elem2);
        
    

XAML:

<Window x:Class="NesteGridMVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:NesteGridMVVM"
        mc:Ignorable="d"
        Title="MainWindow" Height="550" Width="600">

    <Window.DataContext>
        <local:MainVM /> 
    </Window.DataContext>

    <DataGrid
        x:Name="MainDG"
        ItemsSource="Binding ElementsList"
        AutoGenerateColumns="True"
        SelectedItem="Binding SelectedElement"
        RowDetailsVisibilityMode="Visible">
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <DataGrid
                    x:Name="NestedDG"
                    ItemsSource="Binding MyItemsList"
                    AutoGenerateColumns="True"
                    SelectedItem="Binding SelectedMyItem">
                </DataGrid>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>
</Window>

【问题讨论】:

【参考方案1】:

在我看来你可以将绑定触发器修改为PropertyChanged

SelectedItem="Binding SelectedMyItem, UpdateSourceTrigger=PropertyChanged"

不过,请注意:

当我在调试器中运行它时,嵌套的 DataGrid 选择在外部 DataGrid 之前触发。这意味着setSelectedMyItem 将在SelectedElement 之前触发。当然,这只是在您更改外部 DataGrid 中的行时。

完整的.xaml

<Window x:Class="NesteGridMVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:NesteGridMVVM"
        mc:Ignorable="d"
        Title="MainWindow" Height="550" Width="600">

    <Window.DataContext>
        <local:MainVM /> 
    </Window.DataContext>

    <DataGrid
        x:Name="MainDG"
        ItemsSource="Binding ElementsList"
        AutoGenerateColumns="True"
        SelectedItem="Binding SelectedElement"
        RowDetailsVisibilityMode="Visible">
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <DataGrid
                    x:Name="NestedDG"
                    ItemsSource="Binding MyItemsList"
                    AutoGenerateColumns="True"
                    SelectedItem="Binding SelectedMyItem, UpdateSourceTrigger=PropertyChanged">
                </DataGrid>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>
</Window>

【讨论】:

非常感谢。这正是我想要实现的,但我不明白为什么添加 UpdateSourceTrigger = PropertyChanged 可以解决问题? @AdamMroczek 通常只是 WPF 的一个怪癖,很多时候在WPF 中,绑定的默认触发器是当您离开字段或LostFocus 时。我猜测内部 DataGrid 的问题在于,它们每个都有自己独立的可聚焦对象,即使您单击另一个也可以保持“聚焦”。如果您想要一个示例,请使用带有默认文本绑定的TextBox - 它不会触发,直到您点击/点击离开。将触发器更改为 PropertyChanged 会在您键入时触发。

以上是关于Wpf:如何从嵌套的 DataGrid 中绑定 SelectedItem的主要内容,如果未能解决你的问题,请参考以下文章

WPF入门教程系列二十一——DataGrid示例

WPF 使用 id 从数据库表中绑定 DataGrid 列 ComboBox

wpf中,当DataGrid.ItemsSource与ObservableCollection绑定后,值变化时,DataGrid如何刷新?

WPF DataGrid - 如何暂停数据绑定中的 UI 更新并稍后进行批量更新

如何将 DataGrid 中的文本框绑定到 Wpf 中的列表?

如何将 WPF DataGrid 绑定到 ObservableCollection