我正在尝试自定义带有标题的ListBox / ListView,以便当我向下滚动时,标题将相应缩小到某个最小高度并停留在该位置,而内容仍会向上滚动。然后,当我向下滚动内容时,在第一个元素附近的某个位置,当列表滚动到位置1时,标题也会被扩展到其最大高度。我可以通过修改ListView内的ScrollViewer来实现。
这是ListView样式:
<Style x:Key="CustomListView" TargetType="ListView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListView">
<Border Name="Border">
<local:ScrollViewerWithHeader>
<ItemsPresenter/>
</local:ScrollViewerWithHeader>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
这是ScrollViewerWithHeader样式:
<Style TargetType="{x:Type local:ScrollViewerWithHeader}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="HeaderHeight" Value="192"/>
<Setter Property="MinHeaderHeight" Value="48"/>
<Setter Property="MarginTopOfScrolledContent" Value="144"/>
<Setter Property="Header" Value="{StaticResource SampleGrid}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ScrollViewerWithHeader}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Column="1" BorderThickness="0,1,1,1">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BorderMediumColor}" />
</Border.BorderBrush>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Height="{TemplateBinding MinHeaderHeight}"/>
<Border Grid.Row="1" Height="{TemplateBinding MarginTopOfScrolledContent}"/>
<ScrollContentPresenter CanContentScroll="{TemplateBinding CanContentScroll}" Grid.Row="1" Grid.RowSpan="2">
<ScrollContentPresenter.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{TemplateBinding ScrollViewer.Content}" Margin="0,144,0,0"/>
</DataTemplate>
</ScrollContentPresenter.ContentTemplate>
</ScrollContentPresenter>
<ContentControl Grid.Row="0" VerticalAlignment="Top" Grid.RowSpan="2" Template="{TemplateBinding Header}" Height="{TemplateBinding HeaderHeight}"/>
</Grid>
</Border>
<ScrollBar x:Name="PART_VerticalScrollBar"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Grid.Column="2"/>
<ScrollBar x:Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Grid.Column="1"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
在上面的代码块中,您可以看到ScrollContentPresenter中的Content具有Margin =“ 0,144,0,0”,该内容应首先位于ListView中项目的正上方,然后随着项目向上滚动而向上滚动。但是它仅在禁用UI虚拟化时有效。启用该功能后,边距将永久保留在此处,并且只有项目上升,因此顶部会有空白。
这是ScrollViewerWithHeader的C#代码:
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace TestListAutoResizingHeader
{
public class ScrollViewerWithHeader : ScrollViewer
{
public double MaxHeaderHeight
{
get { return (double)GetValue(MaxHeaderHeightProperty); }
set { SetValue(MaxHeaderHeightProperty, value); }
}
public static readonly DependencyProperty MaxHeaderHeightProperty =
DependencyProperty.RegisterAttached(
"MaxHeaderHeight",
typeof(double),
typeof(ScrollViewerWithHeader));
public double MinHeaderHeight
{
get { return (double)GetValue(MinHeaderHeightProperty); }
set { SetValue(MinHeaderHeightProperty, value); }
}
public static readonly DependencyProperty MinHeaderHeightProperty =
DependencyProperty.RegisterAttached(
"MinHeaderHeight",
typeof(double),
typeof(ScrollViewerWithHeader));
public object Header
{
get { return GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register(
"Header",
typeof(object),
typeof(ScrollViewerWithHeader),
new FrameworkPropertyMetadata((object)null));
public double HeaderHeight
{
get { return (double)GetValue(HeaderHeightProperty); }
set { SetValue(HeaderHeightProperty, value); }
}
public static readonly DependencyProperty HeaderHeightProperty =
DependencyProperty.Register(
"HeaderHeight",
typeof(double),
typeof(ScrollViewerWithHeader));
public double MarginTopOfScrolledContent
{
get { return (double)GetValue(MarginTopOfScrolledContentProperty); }
set { SetValue(MarginTopOfScrolledContentProperty, value); }
}
public static readonly DependencyProperty MarginTopOfScrolledContentProperty =
DependencyProperty.Register(
"MarginTopOfScrolledContent",
typeof(double),
typeof(ScrollViewerWithHeader));
protected override void OnScrollChanged(ScrollChangedEventArgs e)
{
Console.WriteLine("Header height before: " + HeaderHeight);
if (192 - e.VerticalOffset > 48)
HeaderHeight = 192 - e.VerticalOffset;
else
HeaderHeight = 48;
base.OnScrollChanged(e);
}
}
}
这段代码看起来非常混乱,但是我想要实现的是:在ListView的第一项上方放置一些空间,当我滚动时,它会与项目保持一致。此ListView必须已启用UI虚拟化。此外,由于其不美观,因此不希望将第一个虚拟项目放入ItemsSource并绑定ListViewItem的高度的解决方案。
如果您想在运行中看到它,可以打开Groove音乐应用程序->我的音乐->歌手->选择包含几首歌曲的歌手->歌曲视图->向上滚动以查看效果。
我有任何其他疑问。