WPF ListView с прокручиваемым заголовком - PullRequest
0 голосов
/ 14 декабря 2018

Я пытаюсь настроить ListBox / ListView с заголовком, чтобы при прокрутке вниз заголовок уменьшался соответственно до некоторой минимальной высоты и оставался там, пока содержимое все еще прокручивается вверх.И затем, когда я прокручиваю содержимое вниз, в некоторой точке рядом с первым элементом, заголовок также будет расширен до его максимальной высоты, когда список прокручивается до позиции 1. Я мог бы добиться этого, изменив ScrollViewer внутри ListView.

Вот стиль 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>

В блоке кода выше вы можете видеть, что у меня есть Margin = "0,144,0,0 "для Контента внутри ScrollContentPresenter, который должен сначала оставаться прямо над элементами в ListView, а затем прокручиваться вверх по мере прокрутки элементов вверх.Но это работает только тогда, когда виртуализация пользовательского интерфейса отключена.Когда он включен, поле остается там постоянно, и только элементы увеличиваются, поэтому сверху есть пробел.

Вот код C # для ScrollViewerWithHeader:

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 должна быть включена виртуализация пользовательского интерфейса.Кроме того, решение помещать первый фиктивный элемент в ItemsSource и связывать высоту ListViewItem не требуется, так как это не элегантно.

Если вы хотите увидеть это в действии, вы можете открыть приложение Groove Music -> MyМузыка -> Исполнители -> Выберите исполнителя, у которого есть несколько песен -> Просмотр композиций -> Прокрутите вверх, чтобы увидеть эффект.

Я открыт для любых дальнейших вопросов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...