Reputation: 3259
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
?
Upvotes: 3
Views: 1159
Reputation: 5236
Here is the solution using autogenerate. Simply implement the ITypedList interface on the observable collection...
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";
}
}
Upvotes: 1
Reputation: 5236
Here is one way to do this. Do not auto generate columns. Set up every column that is possible. Then bind the visibility of each column to a converter that determines whether the column is visible.
<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>
here is the static resource for the converter...
<Window.Resources>
<local:ColumnVisibilityConverter x:Key="ColumnVisibility"/>
</Window.Resources>
here is the converter...
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;
}
I struggled a bit with binding visibility to the data grid column. Found the answer here: Binding Visibility for DataGridColumn in WPF
Setting up the columns manually is best practice in my view. If you really want to autogenerate them, there is another way to do this. You can implement an ICustomTypeDescriptor on your collection to return the PropertyDescriptors for the properties of the derived type that is contained in the collection.
Upvotes: 4