Модификация полосы прокрутки

66

Существует один аспект окна списка, который пока остался нетронутым — полоса прокрутки, расположенная справа. Это часть элемента ScrollViewer, который является частью шаблона ListBox. Несмотря на то что в данном примере переопределяется шаблон ListBox, элемент ScrollViewer в ScrollBar не изменяется.

Чтобы настроить эту деталь, можно было бы создать шаблон ScrollViewer для использования с ListBox и затем указать этот специальный шаблон ScrollBar в качестве шаблона ScrollViewer. Однако доступен более простой способ — создать типизированный в отношении элементов стиль, который изменяет шаблон всех обнаруженных элементов управления ScrollBar. Это позволит избежать дополнительной работы по созданию шаблона ScrollViewer.

Конечно, также следует подумать о том, как это проектное решение затронет остальную часть приложения. Если создать типизированный в отношении элементов стиль ScrollBar и добавить его в коллекцию Resources окна, то все элементы управления в этом окне получат новые стилизованные полосы прокрутки, если они используют ScrollBar, что может быть именно тем, что и требуется. С другой стороны, если нужно изменить только полосу прокрутки в ListBox, понадобится добавить типизированный в отношении элементов стиль ScrollBar в коллекцию ресурсов самого ListBox. И, наконец, чтобы изменить вид полос прокрутки во всем приложении, необходимо добавить стиль в коллекцию ресурсов внутри файла App.xaml.

Элемент управления ScrollBar неожиданно сложен. В действительности он состоит из целой коллекции меньших частей.

Фон полосы прокрутки представлен классом Track — обычно это текстурированный прямоугольник, простирающийся на всю длину полосы. На концах полосы прокрутки находятся кнопки, которые позволяют перемещать на один инкремент вверх или вниз (либо влево или вправо). Это экземпляры класса RepeatButton, унаследованного от ButtonBase. Ключевое отличие между RepeatButton и обычным классом Button состоит в том, что удержание кнопки мыши нажатой на RepeatButton приводит к повторению события Click (это удобно для прокрутки).

Посередине полосы прокрутки расположен элемент Thumb, представляющий текущую позицию прокручиваемого содержимого. И что самое интересное, пустое пространство с каждой стороны от ползунка — это на самом деле еще два объекта RepeatButton, являющиеся прозрачными. При щелчке на любом из них полоса прокрутки прокручивает содержимое на целую страницу (страница определена как часть прокручиваемого содержимого, умещающаяся в окно). Это дает хорошо знакомую возможность быстро перемещаться по прокручиваемому содержимому, щелкая на полосе по обе стороны от ползунка.

Есть несколько ключевых моментов, которые следовало бы отметить:

Ниже показан шаблон скроллбара:

<Style x:Key="ScrollBarLineButtonStyle" TargetType="{x:Type RepeatButton}">
        <Setter Property="Focusable" Value="False"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RepeatButton}">
                    <Canvas Height="18">
                        <Polygon Fill="LightBlue" Points="3,15 15,15 9,3"></Polygon>
                    </Canvas>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="ScrollBarLineButtonBottomStyle" TargetType="{x:Type RepeatButton}">
        <Setter Property="Focusable" Value="False"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RepeatButton}">
                    <Canvas Height="18">
                        <Polygon Fill="LightBlue" Points="3,3 9,15 15,3"></Polygon>
                    </Canvas>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="ScrollBarPageButtonStyle" TargetType="{x:Type RepeatButton}">
        <Setter Property="IsTabStop" Value="False"/>
        <Setter Property="Focusable" Value="False"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RepeatButton}">
                    <Border BorderBrush="Transparent"></Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="ScrollBarThumbStyle" TargetType="{x:Type Thumb}">
        <Setter Property="IsTabStop" Value="False"/>
        <Setter Property="Focusable" Value="False"/>
        <Setter Property="Margin" Value="1,0,1,0" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Thumb}">
                    <Rectangle Fill="LightBlue" Margin="2"></Rectangle>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition MaxHeight="18"/>
                <RowDefinition Height="*"/>
                <RowDefinition MaxHeight="18"/>
            </Grid.RowDefinitions>

            <RepeatButton Grid.Row="0" Height="18"
                          Style="{StaticResource ScrollBarLineButtonStyle}"
                          Command="ScrollBar.LineUpCommand" >
            </RepeatButton>
            <Track Name="PART_Track" Grid.Row="1" 
                   IsDirectionReversed="True">
                <Track.DecreaseRepeatButton>
                    <RepeatButton Command="ScrollBar.PageUpCommand" Style="{StaticResource ScrollBarPageButtonStyle}">
                    </RepeatButton>
                </Track.DecreaseRepeatButton>
                <Track.Thumb>
                    <Thumb Style="{StaticResource ScrollBarThumbStyle}">
                    </Thumb>
                </Track.Thumb>
                <Track.IncreaseRepeatButton>
                    <RepeatButton Command="ScrollBar.PageDownCommand" Style="{StaticResource ScrollBarPageButtonStyle}">
                    </RepeatButton>
                </Track.IncreaseRepeatButton>
            </Track>
            <RepeatButton Grid.Row="3" Height="18"
                          Style="{StaticResource ScrollBarLineButtonBottomStyle}"
                          Command="ScrollBar.LineDownCommand">
            </RepeatButton>
        </Grid>
    </ControlTemplate>

   <Style TargetType="{x:Type ScrollBar}">
                <Setter Property="Template" Value="{StaticResource VerticalScrollBar}"/>
    </Style>
Окно списка с настроенной полосой прокрутки

Также можно заметить, что шаблон использует ключевое имя, которое указывает на то, что это — вертикальная полоса прокрутки. Как известно, установка ключевого имени стиля гарантирует, что оно не применяется автоматически, даже если также установлено свойство TargetType. Причина использования этого подхода в данном примере связана с тем, что шаблон подходит только к полосам прокрутки с вертикальной ориентацией.

Пройди тесты
Лучший чат для C# программистов