uwpuwp-xamlgroupstyle

UWP - Stretch height of HeaderTemplate in ItemsStackPanel with GroupHeaderPlacement=Left


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.


Solution

  • 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 ListViewItems.). 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.

    enter image description here