【问题标题】:WPF binding Datagrid to ObservableCollection with nested PropertiesWPF 将 Datagrid 绑定到具有嵌套属性的 ObservableCollection
【发布时间】:2017-05-14 15:45:00
【问题描述】:

我想将 DataGrid 绑定到嵌套属性的集合。 我尝试了来自
WPF: Bound datagrid does not update items properties 的解决方案 但到目前为止没有运气。

我得到的是一个空的 DataGrid 和这样的控制台输出:

System.Windows.Data 错误:2:找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement。绑定表达式:路径=AGNR.Key;数据项=空;目标元素是“DataGridTextColumn”(HashCode=38805039);目标属性是“标题”(类型“对象”)

也是 tcc_CollectionChanged 的​​输出(但不是 PropertyChangedHandler):

30.12.2016 11:11:22,收藏已更改

我使用现代 UI(一楼)框架。这是我的窗口代码:

public partial class Home : UserControl
{

    private ObservableTable<TableClass> tcc;

    public Home()
    {
        InitializeComponent();
    }

    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
        TableClass tc;
        List<TableClass> tcl = new List<TableClass>();

        tcc = new ObservableTable<TableClass>();

        tcc.CollectionChanged += tcc_CollectionChanged;
        tcc.ItemPropertyChanged += PropertyChangedHandler;

        for (int i = 0; i < 10; i++)
        {
            tc = new TableClass();

            tc.AGNR.Name = "AGNr";
            tc.AGNR.Value = i.ToString();

            tc.MNR.Name = "MNr";
            tc.MNR.Value = i.ToString() + " M";

            tc.MST.Name = "MSt";
            tc.MST.Value = i % 2 == 0 ? "production" : "stopped";

            tcc.Add(tc);
        }

    }

    static void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine(DateTime.Now.ToString() + ", Property changed");
        return;
    }

    static void tcc_CollectionChanged(object sender, EventArgs e)
    {
        Console.WriteLine(DateTime.Now.ToString() + ", Collection changed");
        return;
    }
}

对应的 XAML:

<UserControl x:Class="MuiWpfTestApp.Pages.Home"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"
         Loaded="UserControl_Loaded">
<Grid Style="{StaticResource ContentRoot}">
    <ScrollViewer>
        <DataGrid x:Name="tcgrid" ItemsSource="{Binding tcc, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="250" Width="250">
            <DataGridTextColumn Binding="{Binding AGNR.Value, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                                Header="{Binding AGNR.Key, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
            <DataGridTextColumn Binding="{Binding MNR.Value, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                                Header="{Binding MNR.Key, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
            <DataGridTextColumn Binding="{Binding MST.Value, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                                Header="{Binding MST.Key, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
        </DataGrid>
    </ScrollViewer>
</Grid>

INotifyPropertyChanged 在 TableClass 中实现:

 class TableClass : INotifyPropertyChanged
{
    private KeyValueClass _mnr;
    private KeyValueClass _agnr;
    private KeyValueClass _mst;

    public KeyValueClass MNR
    {
        get
        {
            return _mnr;
        }
        set
        {
            _mnr = value;
            NotifyPropertyChanged("MNR");
        }
    }
    public KeyValueClass AGNR
    {
        get
        {
            return _agnr;
        }
        set
        {
            _agnr = value;
            NotifyPropertyChanged("AGNR");
        }
    }
    public KeyValueClass MST
    {
        get
        {
            return _mst;
        }
        set
        {
            _mst = value;
            NotifyPropertyChanged("MST");
        }
    }

    public TableClass()
    {
        MNR = new KeyValueClass();
        AGNR = new KeyValueClass();
        MST = new KeyValueClass();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

}

而且 KeyValueClass 很简单(这里需要什么?):

class KeyValueClass
{
    private string _name;
    private string _val;

    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
        }
    }
    public string Value
    {
        get
        {
            return _val;
        }
        set
        {
            _val = value;
        }
    }
}

我需要集合中的这个嵌套属性,因为我可以用不同的语言获取这些数据。所以我无法对网格的标题进行编码。

【问题讨论】:

    标签: c# wpf data-binding datagrid observablecollection


    【解决方案1】:

    由于您只能绑定到公共属性,因此您必须这样定义“tcc”。您的 TableClass 和 KeyValueClass 类型也必须是公共的:

    public partial class Home : UserControl
    {
        public ObservableCollection<TableClass> tcc { get; set; }
    
        public Home()
        {
            InitializeComponent();
            tcc = new ObservableCollection<TableClass>();
            DataContext = this;
        }
    
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            TableClass tc;
            List<TableClass> tcl = new List<TableClass>();
    
            tcc.CollectionChanged += tcc_CollectionChanged;
    
            for (int i = 0; i < 10; i++)
            {
                tc = new TableClass();
    
                tc.AGNR.Name = "AGNr";
                tc.AGNR.Value = i.ToString();
    
                tc.MNR.Name = "MNr";
                tc.MNR.Value = i.ToString() + " M";
    
                tc.MST.Name = "MSt";
                tc.MST.Value = i % 2 == 0 ? "production" : "stopped";
    
                tcc.Add(tc);
            }
    
        }
    
        static void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
        {
            Console.WriteLine(DateTime.Now.ToString() + ", Property changed");
            return;
        }
    
        static void tcc_CollectionChanged(object sender, EventArgs e)
        {
            Console.WriteLine(DateTime.Now.ToString() + ", Collection changed");
            return;
        }
    }
    
    public class TableClass : INotifyPropertyChanged
    {
        private KeyValueClass _mnr;
        private KeyValueClass _agnr;
        private KeyValueClass _mst;
    
        public KeyValueClass MNR
        {
            get
            {
                return _mnr;
            }
            set
            {
                _mnr = value;
                NotifyPropertyChanged("MNR");
            }
        }
        public KeyValueClass AGNR
        {
            get
            {
                return _agnr;
            }
            set
            {
                _agnr = value;
                NotifyPropertyChanged("AGNR");
            }
        }
        public KeyValueClass MST
        {
            get
            {
                return _mst;
            }
            set
            {
                _mst = value;
                NotifyPropertyChanged("MST");
            }
        }
    
        public TableClass()
        {
            MNR = new KeyValueClass();
            AGNR = new KeyValueClass();
            MST = new KeyValueClass();
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void NotifyPropertyChanged(String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
    }
    
    public class KeyValueClass
    {
        private string _name;
        private string _val;
    
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }
        public string Value
        {
            get
            {
                return _val;
            }
            set
            {
                _val = value;
            }
        }
    }
    

    没有内置类 ObservableTable,所以我在上面的示例代码中将其更改为 ObservableCollection。

    您还应该将列添加到 DataGrid 的 Columns 集合中:

    <DataGrid x:Name="tcgrid" ItemsSource="{Binding tcc, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                      AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding AGNR.Value, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                                    Header="{Binding AGNR.Key, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
            <DataGridTextColumn Binding="{Binding MNR.Value, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                                    Header="{Binding MNR.Key, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
            <DataGridTextColumn Binding="{Binding MST.Value, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                                    Header="{Binding MST.Key, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
        </DataGrid.Columns>
    </DataGrid>
    

    然后它似乎工作:

    【讨论】:

    • 您好 mm8 感谢您的回答以及关于将属性“tcc”公开的提示。我之前将其设为私有,因为它在没有公开我的类型的情况下给出了错误。我仍然收到相同的错误找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement,正如您在图片中看到的那样,网格的标题为空。结果对我来说仍然是一张完全空的桌子。可观察表是从我在我的问题中引用的问题中“获取”的,看起来像这样
    • ...无法在此处粘贴代码。如果 KeyValueClass 中的属性发生变化,我想从课程中得到通知。
    • 列标题的绑定应该是 AGNR.Name 等。如果我尝试这个,仍然是空标题
    • 每列只有一个列标题,并且列标题没有 DataContext 那么您希望在标题中显示什么类的什么属性?一个列标题映射到多个 TableClass 对象,因此将标题绑定到 TableClass 对象的属性是没有意义的......
    猜你喜欢
    • 2017-03-19
    • 2011-02-19
    • 2013-10-14
    • 1970-01-01
    • 2014-08-23
    • 2017-02-08
    • 2016-08-28
    • 1970-01-01
    • 2015-01-26
    相关资源
    最近更新 更多