【问题标题】:WPF DataGrid dynamic columns based on object type and subtype基于对象类型和子类型的 WPF DataGrid 动态列
【发布时间】:2017-07-19 20:41:20
【问题描述】:

我正在构建一个 WPF 软件来管理电子元件的库存。

我有以下结构:

public class Part
{
    public string Manufacturer { get; set; }
    public string PartNumber { get; set; }
}
public class Resistor : Part
{
    public string Resistance { get; set;}
    public string Power { get; set;}
} 
public class Capacitor : Part
{
    public string Capacitance { get; set; }
    public string Voltage { get; set; }
}

电阻器和电容器是零件的子类型。

我将DataGrid 绑定到ObservableCollection<Part>,并使用ListCollectionView 添加过滤和分组功能。

我想要完成的是,当我过滤 ListCollectionView 以仅获取 Resistor 子类型时,我希望 DataGrid 更新其列以显示 Resistor 类型的属性及其基类Part(所以我会得到制造商、零件号、电阻和功率列)。同时,如果我过滤ListCollectionView 以获得Capacitor 子类型,DataGrid 应该具有Capacitor 类公共属性和Part 公共属性(制造商、零件编号、电容和电压)。 最后,如果没有应用过滤,DataGrid 将只显示Part 属性(制造商和零件编号)。

我尝试使用AutoGenerateColumns=true,但DataGrid 仅显示Part 属性,即使我将ListCollectionView 过滤为只有Resistors。我也试过把ObservableCollection的类型改成dynamic,也没用。

如何根据ObservableCollection 中包含的对象类型更改DataGrid 列?

【问题讨论】:

    标签: wpf datagrid observablecollection listcollectionview


    【解决方案1】:

    这是执行此操作的一种方法。不要自动生成列。设置所有可能的列。然后将每列的可见性绑定到一个转换器,该转换器确定该列是否可见。

        <FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
        <DataGrid x:Name="dataGrid" ItemsSource="{Binding PartCollection}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Manufacturer" Binding="{Binding Manufacturer}"/>
                <DataGridTextColumn Header="Part Number" Binding="{Binding PartNumber}" />
                <DataGridTextColumn Header="Power" Binding="{Binding Power}" Visibility="{Binding DataContext.PartCollection, Source={x:Reference dummyElement}, Converter={StaticResource ColumnVisibility}, ConverterParameter=Resistor}"/>
                <DataGridTextColumn Header="Resistance" Binding="{Binding Resistance}" Visibility="{Binding DataContext.PartCollection, Source={x:Reference dummyElement}, Converter={StaticResource ColumnVisibility}, ConverterParameter=Resistor}"/>
                <DataGridTextColumn Header="Capacitance" Binding="{Binding Capacitance}" Visibility="{Binding DataContext.PartCollection, Source={x:Reference dummyElement}, Converter={StaticResource ColumnVisibility}, ConverterParameter=Capacitor}"/>
                <DataGridTextColumn Header="Voltage" Binding="{Binding Voltage}" Visibility="{Binding DataContext.PartCollection, Source={x:Reference dummyElement}, Converter={StaticResource ColumnVisibility}, ConverterParameter=Capacitor}"/>
            </DataGrid.Columns>
        </DataGrid>
    

    这里是转换器的静态资源...

    <Window.Resources>
        <local:ColumnVisibilityConverter x:Key="ColumnVisibility"/>
    </Window.Resources>
    

    这是转换器...

        public class ColumnVisibilityConverter : IValueConverter
        {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            ObservableCollection<Part> collection = value as ObservableCollection<Part>;
            string collectionType = parameter as string;
            if(collection != null && collectionType != null && collection.Count > 0)
            {
                switch(collectionType)
                {
                    case "Resistor": return collection[0].GetType() == typeof(Resistor) ? Visibility.Visible : Visibility.Hidden;
                    case "Capacitor": return collection[0].GetType() == typeof(Capacitor) ? Visibility.Visible : Visibility.Hidden;
                    default: return Visibility.Hidden;
                }
            }
            return Visibility.Hidden;
        }
    

    我在绑定数据网格列的可见性方面有点吃力。在这里找到答案:Binding Visibility for DataGridColumn in WPF

    在我看来,手动设置列是最佳做法。如果您真的想自动生成它们,还有另一种方法可以做到这一点。您可以在集合上实现 ICustomTypeDescriptor,以返回集合中包含的派生类型的属性的 PropertyDescriptor。

    【讨论】:

    • 我使用您的想法创建了一个布尔属性并将列可见性绑定到该属性。成功了!
    • @aquirky 我会测试,但是当它尝试显示绑定到该类型不存在的属性的列时,这会产生绑定错误吗?
    • 好吧,它不会产生绑定错误。对此有点困惑。这很好用,谢谢!!!!
    【解决方案2】:

    这是使用自动生成的解决方案。只需在可观察集合上实现 ITypedList 接口...

    public class Parts : ObservableCollection<Part>, ITypedList
    {
        public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            if(Count == 0)
            {
                return TypeDescriptor.GetProperties(typeof(Part));
            }
            else
            {
                PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this[0]);
                return pdc;
            }
        }
    
        public string GetListName(PropertyDescriptor[] listAccessors)
        {
            return "Parts";
        }
    }
    

    【讨论】:

    • 这行得通,但只有在启动应用程序时才会调用 GetItemProperties。每次应用从 ObservableCollection 获得的 ListViewCollection 过滤时,我都需要刷新它。这可能吗?
    • 是的,但不是很优雅... dataGrid.ItemsSource = null; dataGrid.ItemsSource = PartCollection;
    • 谢谢。工作得很好。
    猜你喜欢
    • 2011-02-25
    • 2010-12-11
    • 1970-01-01
    • 1970-01-01
    • 2019-11-10
    • 2016-11-11
    • 2014-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多