Inside an ItemsControl or ListView, is it possible to make the height of the GroupStyle.HeaderTemplate stretch to match the height of the grouped items?
Regardless of what I set VerticalAlignment/VerticalContentAlignment to, the group header appears to the left of the first item from the group.
Regardless of what I set VerticalAlignment/VerticalContentAlignment to, the group header appears to the left of the first item from the group.
A group header is actually a ListViewHeaderItem
object. By looking through theViewTree
we will not see a parent container for every group (one group contains one ListViewHeaderItem
and several ListViewItem
s.). So it seems like there is no parent control for ListViewHeaderItem
to simply strength. But we can set the height of ListViewHeaderItem
to march the group items by accurate calculation.
Here we can use the ViewTreeHelper class to get the AcualHeight
of ListViewItem
firstly, and then get the items count for current group from the group resource you bind to XAML, and now the group header height can be calculated. Code as follows:
XAML Code
<Page.Resources>
<CollectionViewSource x:Name="cvsActivities" IsSourceGrouped="True" />
<CollectionViewSource
x:Name="cvsProjects"
IsSourceGrouped="True"
ItemsPath="Activities" />
<local:ListGroupStyleSelector x:Key="listGroupStyleSelector" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Padding="30">
<ListView
x:Name="CListView"
GroupStyleSelector="{StaticResource listGroupStyleSelector}"
ItemContainerStyle="{StaticResource ListViewItemExpanded}"
ItemTemplate="{StaticResource listViewItemTemplate}"
ItemsSource="{Binding Source={StaticResource cvsActivities}}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel GroupHeaderPlacement="Left" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
Code behind
public sealed partial class MainPage : Page
{
DateTime startDate;
IOrderedEnumerable<IGrouping<string, Activity>> result;
public MainPage()
{
this.InitializeComponent();
}
//Calculate the group height.
public void calculate()
{
IEnumerable<ListViewHeaderItem> headeritems = FindVisualChildren<ListViewHeaderItem>(CListView);
IEnumerable<ListViewItem> listviewitems = FindVisualChildren<ListViewItem>(CListView);
var listviewitemheight = listviewitems.FirstOrDefault().ActualHeight;
for (int i = 0; i < headeritems.Count(); i++)
{
ListViewHeaderItem headeritem = headeritems.ElementAt<ListViewHeaderItem>(i);
var currentgroup = result.ElementAt<IGrouping<string, Activity>>(i);
var groupcount = currentgroup.Count();
headeritem.Height = listviewitemheight * groupcount;
System.Diagnostics.Debug.WriteLine(headeritem.ActualHeight);
}
}
private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
DateTime.TryParse("1/1/2014", out startDate);
PopulateActivities();
}
private void PopulateActivities()
{
List<Activity> Activities = new List<Activity>();
Activities.Add(new Activity()
{
Name = "Activity 1",
Complete = true,
DueDate = startDate.AddDays(4),
Project = "Project 1"
});
...
Activities.Add(new Activity()
{
Name = "Activity A",
Complete = true,
DueDate = startDate.AddDays(2),
Project = "Project 2"
});
...
result = from act in Activities group act by act.Project into grp orderby grp.Key select grp;
cvsActivities.Source = result;
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
calculate();
}
}
public class ListGroupStyleSelector : GroupStyleSelector
{
protected override GroupStyle SelectGroupStyleCore(object group, uint level)
{
return (GroupStyle)App.Current.Resources["listViewGroupStyle"];
}
}
public class Activity
{
public string Name { get; set; }
public DateTime DueDate { get; set; }
public bool Complete { get; set; }
public string Project { get; set; }
}
Update the HeaderContainerStyle for group style to make the layout look tidy.
<GroupStyle x:Key="listViewGroupStyle">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock
VerticalAlignment="Center"
Foreground="Black"
Text="{Binding Key}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.HeaderContainerStyle>
<Style TargetType="ListViewHeaderItem">
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ListViewHeaderItemThemeFontSize}" />
<Setter Property="Background" Value="Azure" />
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="Padding" Value="0,0,0,0" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="MinHeight" Value="{ThemeResource ListViewHeaderItemMinHeight}" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewHeaderItem">
<Grid
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter
x:Name="ContentPresenter"
Margin="0"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="Stretch"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}" />
<Rectangle
Height="1"
Margin="0,0,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Stroke="{ThemeResource SystemControlForegroundBaseLowBrush}"
StrokeThickness="0.5" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.HeaderContainerStyle>
</GroupStyle>
And the result.