具有可滚动标题的WPF ListView

时间:2018-12-14 19:32:35

标签: wpf listview header

我正在尝试自定义带有标题的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音乐应用程序->我的音乐->歌手->选择包含几首歌曲的歌手->歌曲视图->向上滚动以查看效果。

我有任何其他疑问。

0 个答案:

没有答案