I have a TreeView
that looks like this:
<TreeView Grid.Row="1" x:Name="InspectionResultsTreeView"
ItemsSource="{Binding Source={StaticResource InspectionTypeGroupViewSource}, Path=Groups}"
ItemTemplate="{StaticResource InspectionTypeGroupsTemplate}">
</TreeView>
The ItemsSource
is a keyed resource that goes by the name of InspectionTypeGroupViewSource
:
<CollectionViewSource x:Key="InspectionTypeGroupViewSource" Source="{Binding Results}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Inspection.InspectionType" />
<PropertyGroupDescription PropertyName="Inspection" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
The role of this little thing is to take the ViewModel's Results
property:
private ObservableCollection<ICodeInspectionResult> _results;
public ObservableCollection<ICodeInspectionResult> Results
{
get { return _results; }
set { _results = value; OnPropertyChanged(); }
}
...and group it on two levels - first by InspectionType
, then by Inspection
- the result is a 3-level hierarchy with inspection types, inspections, and then individual inspection results. At this point a screenshot might help visualizing I guess:
So, the ItemTemplate
of the InspectionResultsTreeView
is another keyed resource, by the name of InspectionTypeGroupsTemplate
- that's the bold "inspection type" items:
<HierarchicalDataTemplate x:Key="InspectionTypeGroupsTemplate"
DataType="{x:Type CollectionViewGroup}"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource InspectionGroupsTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{Binding Name}"
FontWeight="Bold"
TextWrapping="NoWrap"/>
<TextBlock Margin="4,0,4,0"
VerticalAlignment="Center"
Text="{Binding ItemCount, StringFormat=({0})}"
TextWrapping="NoWrap"/>
</StackPanel>
</HierarchicalDataTemplate>
And the ItemTemplate
of that template is an InspectionGroupsTemplate
- that's the individual inspections, with the "severity" icons:
<HierarchicalDataTemplate x:Key="InspectionGroupsTemplate"
DataType="{x:Type CollectionViewGroup}"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource InspectionResultTemplate}">
<StackPanel Orientation="Horizontal">
<Image Style="{StaticResource IconStyle}"
Source="{Binding Name, Converter={StaticResource InspectionIconConverter}}"
VerticalAlignment="Center" />
<TextBlock Margin="4"
VerticalAlignment="Center"
Text="{Binding Name, Converter={StaticResource InspectionDescriptionConverter}}"
TextWrapping="NoWrap"/>
<TextBlock Margin="0,4,0,4"
VerticalAlignment="Center"
Text="{Binding ItemCount, StringFormat=({0})}"
TextWrapping="NoWrap"/>
</StackPanel>
</HierarchicalDataTemplate>
Lastly, the ItemTemplate
of this grouping is an InspectionResultTemplate
, which is for each individual inspection result:
<DataTemplate x:Key="InspectionResultTemplate"
DataType="{x:Type inspections:ICodeInspectionResult}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Margin="4"
Text="{Binding Name}"
TextWrapping="NoWrap"/>
</StackPanel>
</DataTemplate>
The ICodeInspectionResult
interface has a string Name
property that I'm using here; this Name
is different from the Name
that's used in the grouping levels, where it's an object CollectionViewGroup.Name
- the underlying type of that Name
is that of the grouping, so level 1 is an InspectionType
, and level 2 is an Inspection
.
The problem is that I'm using more converters than I believe I'd need to, to convert this object Name
and access the members I need to access and display... but then, I need to display the number of items in each grouping so the DataType
ought to be a CollectionViewGroup
... right?
How can I do this without resorting to a converter for everything that needs to be displayed? How is this supposed to be done? Every TreeView
/ CollectionViewGroup
tutorial I could find was a trivial implementation.
You've encountered the iconic problem with XAML: it's almost too structured.
The most idiomatic solution is writing a custom WPF User Control. (How and what you include in it is up you.) The goal of the WPF User Control is to eliminate the duplicate XAML markup and logic. You can include your Converter
in the User Control, and eliminate the converters from your main control.
There are plenty of tutorials on creating UserControl
objects in WPF, so I'll not go into detail here.
As far as the Converter
issue: this is almost the most idiomatic way. Each converter is reusable, and focuses only on one source type. There's not much else you can do about it, except consider merging converters that support the same source type together. (There's a reason the converter has a Type targetType
parameter, and an object parameter
.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With