WPF DataGrid动态列基于对象类型和子类型

时间:2017-02-28 18:26:58

标签: wpf datagrid observablecollection listcollectionview

我正在构建一个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; }
}

电阻器和电容器是Part的子类型。

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

我想要完成的是当我过滤ListCollectionView以仅获取Resistor子类型时,我希望DataGrid将其更新为显示Resistor类型及其基类Part的属性(因此我会得到制造商,PartNumber,Resistance和Power列)。同时,如果我过滤ListCollectionView获取Capacitor子类型,则DataGrid应具有Capacitor类公共属性和Part公共属性(制造商,PartNumber,电容和电压)。 最后,如果没有应用过滤,则DataGrid只会显示Part属性(制造商和PartNumber)。

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

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

2 个答案:

答案 0 :(得分:3)

这是一种方法。不要自动生成列。设置每个可能的列。然后将每列的可见性绑定到确定列是否可见的转换器。

    <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,以返回PropertyDescriptors,以获取集合中包含的派生类型的属性。

答案 1 :(得分:1)

这是使用自动生成的解决方案。只需在observable集合上实现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";
    }
}