UWP - ItemsStackPanel 中 HeaderTemplate 的拉伸高度,GroupHeaderPlacement=Left
UWP - Stretch height of HeaderTemplate in ItemsStackPanel with GroupHeaderPlacement=Left
在 ItemsControl 或 ListView 中,是否可以使 GroupStyle.HeaderTemplate 的高度拉伸以匹配分组项目的高度?
无论我将 VerticalAlignment/VerticalContentAlignment 设置为什么,组 header 都会出现在组中第一项的左侧。
Regardless of what I set VerticalAlignment/VerticalContentAlignment to, the group header appears to the left of the first item from the group.
一组header实际上是一个ListViewHeaderItem
object. By looking through theViewTree
我们不会看到每个组都有一个parent容器(一个组包含一个ListViewHeaderItem
和几个ListViewItem
s。)。所以似乎没有 parent 控制 ListViewHeaderItem
来简单地加强。但是我们可以通过精确计算设置ListViewHeaderItem
的高度来进行分组项。
这里可以先用ViewTreeHelperclass获取ListViewItem
的AcualHeight
,然后从你的群资源中获取当前群的item数绑定到XAML,现在可以计算组header的高度了。代码如下:
XAML代码
<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>
代码隐藏
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; }
}
更新 HeaderContainerStyle 组样式,使布局看起来整洁。
<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>
结果。
在 ItemsControl 或 ListView 中,是否可以使 GroupStyle.HeaderTemplate 的高度拉伸以匹配分组项目的高度?
无论我将 VerticalAlignment/VerticalContentAlignment 设置为什么,组 header 都会出现在组中第一项的左侧。
Regardless of what I set VerticalAlignment/VerticalContentAlignment to, the group header appears to the left of the first item from the group.
一组header实际上是一个ListViewHeaderItem
object. By looking through theViewTree
我们不会看到每个组都有一个parent容器(一个组包含一个ListViewHeaderItem
和几个ListViewItem
s。)。所以似乎没有 parent 控制 ListViewHeaderItem
来简单地加强。但是我们可以通过精确计算设置ListViewHeaderItem
的高度来进行分组项。
这里可以先用ViewTreeHelperclass获取ListViewItem
的AcualHeight
,然后从你的群资源中获取当前群的item数绑定到XAML,现在可以计算组header的高度了。代码如下:
XAML代码
<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>
代码隐藏
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; }
}
更新 HeaderContainerStyle 组样式,使布局看起来整洁。
<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>
结果。