Reputation: 13030
All examples I can find for implementing an Expander
as a group for the ListView
in WPF do so by defining a new ControlTemplate
for the GroupItem
in the GroupStyle.ContainerStyle
property.
An example of this can be found here, and it works as expected.
However it does have the flaw that if you add additional GroupDescription
s (for multi-level grouping), there is no indentation for the second and subsequent levels like there would be when overriding only the header template and not the container style.
I happened to notice in passing that the GroupItem
code does appear to have special support for locating an Expander
control in its template, which (if that's a recursive search) suggests that it might be possible to replace the group style above with a simpler one like so:
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate DataType="GroupItem">
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"
FontWeight="Bold" Foreground="Gray"
FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" FontSize="22"
Foreground="Green" FontWeight="Bold"
FontStyle="Italic" Margin="10,0,0,0"
VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" FontSize="22"
Foreground="Silver" FontStyle="Italic"
VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
ie. the same core Expander
but expressed in the HeaderTemplate
rather than the ContainerStyle
.
This almost works -- visually it appears correct, and even has the expected indentation for multiple levels. But toggling the Expander
does not actually show or hide the items in the group, so clearly the visual hierarchy is wrong.
I have a few questions:
GroupItem
actually doing with Expander
detection, if not for this?ContainerStyle
, is there a way to keep the natural indentation?Upvotes: 2
Views: 5313
Reputation: 10349
First of all, if you place Expander
in the header template, GroupItem
will not recognize it (if you inspect the source code (in particular this helper method) you'll see why - it looks for a descendant Expander
, but with TemplatedParent
set to the GroupItem
in question, whereas if the Expander
is part of the header template, its TemplatedParent
will be a ContentPresenter
1.
Second of all, even if it was recognized, GroupItem
does not really handle group items visibility - this is done by the Expander
template (when it's expanded its Content
is visible, otherwise it is not). GroupItem
only handles some edge case when UI virtualization is enabled and it's recycled to represent another item (basically it boils down to invalidating measure on the owner ItemsControl
).
Lastly, the correct approach is to put the Expander
in the GroupItem
template (via GroupStyle.ContainerStyle
). If you blindly follow the referred sample then yes, there won't be any indentation. However, all you need to do is to set desired margin on the ItemsPresenter
, e.g.:
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Header="{Binding Name}">
<ItemsPresenter Margin="20,0,0,0" />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
and voilà! - you've got yourself a nice (and "recursive") indentation.
1 Assuming you do not overwrite the default GroupItem
template.
Upvotes: 3