3

I'm building a WPF software to manage a stock of electronics components.

I have the following structure:

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

Resistor and Capacitor are subtypes of Part.

I'm binding a DataGrid to an ObservableCollection<Part>, and using a ListCollectionView to add filtering and grouping functionality.

What I'm trying to accomplish is when I filter the ListCollectionView to get only the Resistor subtype, I want the DataGrid to update it's columns to show the properties of Resistor type and it's base class Part (so I would get the columns Manufacturer, PartNumber, Resistance and Power). At the same time, if I filter the ListCollectionView to get Capacitor subtype, the DataGrid should have the Capacitor class public properties and the Part public properties (Manufacturer, PartNumber, Capacitance and Voltage). Finally, If there's no filtering applied, the DataGrid would show only Part properties (Manufacturer and PartNumber).

I tried to use the AutoGenerateColumns=true but the DataGrid only shows Part properties, even if I filter the ListCollectionView to have only Resistors. I also tried to change the type of the ObservableCollection to dynamic and it didn't work either.

How can I change the DataGrid columns based on type of the object contained in the ObservableCollection?

4

2 回答 2

4

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

    <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。

于 2017-03-01T05:15:28.183 回答
1

这是使用自动生成的解决方案。只需在可观察集合上实现 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";
    }
}
于 2017-03-01T18:50:01.003 回答