Содержание
При ограниченном пространстве окна программы нам периодически необходимо обеспечить прокрутку содержимого этого окна. Например, когда в окне программы размещается большое количество элементов управления или загружен текст большого объема. Для таких случаев в WPF используется элемент ScrollViewer, который и обеспечивает прокрутку содержимого.
Элемент ScrollViewer
Элемент ScrollViewer представляет собой контейнер содержимого с двумя полосами прокрутки — горизонтальной и вертикальной. ScrollViewer может содержать только один контейнер содержимого и, при этом, в качестве такого контейнера обычно выступает какой-либо предок элемента Panel (WrapPanel, StackPanel и т.д.). Рассмотрим следующий пример:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ScrollViewer>
<StackPanel>
<Border Height="50" Background="Blue"></Border>
<Border Height="50" Background="Green"></Border>
<Border Height="50" Background="Red"></Border>
<Border Height="50" Background="Yellow"></Border>
<Border Height="50" Background="Blue"></Border>
<Border Height="50" Background="Green"></Border>
<Border Height="50" Background="Yellow"></Border>
<Border Height="50" Background="Blue"></Border>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
Здесь мы используем ScrollViewer с настройками по умолчанию, поэтому в запущенном приложении вы увидите следующее окно приложения:
Несмотря на то, что элементам Border внутри контейнера вполне хватает места. чтобы разместиться в нем и быть полностью видимыми, у ScrollViewer мы видим вертикальную полосу прокрутки, которая в данный момент неактивна. Измените высоту окна и вы увидите, что полоса прокрутки становится доступной и можно прокручивать содержимое контейнера. 
При этом, сколько бы мы не изменяли ширину окна — горизонтальная полоса прокрутки не появляется, так как все элементы внутри ScrollViewer автоматически меняют свою ширину. Рассмотрим основные свойства и методы ScrollViewer.
Свойства и события ScrollViewer
Среди основных свойств элемента ScrollViewer можно выделить следующие:
| Свойство | Тип | Описание |
HorizontalScrollBarVisibility |
ScrollBarVisibility |
Определяет видимость горизонтальной полосы прокрутки и может принимать следующие значения:
|
VerticalScrollBarVisibility |
ScrollBarVisibility |
Определяет видимость вертикальной полосы прокрутки. Возможные принимаемые значения см. выше. |
HorizontalOffset |
double |
Возвращает горизонтальное смещение прокручиваемого содержимого. Свойство только для чтения. |
VerticalOffset |
double |
Возвращает вертикальное смещение прокручиваемого содержимого. Свойство только для чтения. |
Также, элемент ScrollViewer предоставляет нам следующее событие
| Событие | Описание |
public eventScrollChangedEventHandler ScrollChanged; |
Генерируется при обнаружении изменений положения полосы прокрутки или размера области просмотра. |
Изменим код нашего приложения следующим образом:
1. Перепишем разметку XAML окна:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<ScrollViewer x:Name="scroll"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
ScrollChanged="scroll_ScrollChanged">
<StackPanel Width="700" Height="400">
<Border Height="50" Background="Blue"></Border>
<Border Height="50" Background="Green"></Border>
<Border Height="50" Background="Red"></Border>
<Border Height="50" Background="Yellow">
<TextBlock x:Name="info"/>
</Border>
<Border Height="50" Background="Blue"></Border>
<Border Height="50" Background="Green"></Border>
<Border Height="50" Background="Yellow"></Border>
<Border Height="50" Background="Blue"></Border>
</StackPanel>
</ScrollViewer>
</Window>
Во-первых, мы определили размеры контейнера StackPanel внутри ScrollViewer:
<StackPanel Width="700" Height="400">
Во-вторых, мы добавили текстовый блок с именем info для вывода информации о значении свойств ScrollViewer:
<Border Height="50" Background="Yellow"> <TextBlock x:Name="info"/> </Border>
И, в-третьих, определили значение свойств HorizontalScrollBarVisibility, VerticalScrollBarVisibility и обработчик события ScrollChanged:
<ScrollViewer x:Name="scroll"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
ScrollChanged="scroll_ScrollChanged">
Теперь полосы прокрутки будут показываться только, когда содержимому не будет хватать места в окне просмотра, а обработчик события будет следующим:
private void scroll_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
info.Text = $"HorizontalOffset {scroll.HorizontalOffset}; VerticalOffset {scroll.VerticalOffset}";
}
Запустим приложение и посмотрим на результат. При запуске приложения, всё содержимое полностью умещается и видимо в окне просмотра, поэтому полос прокрутки мы не видим:
Изменим размер окна таким образом, чтобы увидеть полосы прокрутки и изменим положение «бегунков»:
При необходимости, мы можем управлять прокруткой в ScrollViewer автоматически, используя методы элемента.
Методы для программной прокрутки ScrollViewer
Для программной прокрутки ScrollViewer можно использовать следующие методы (в таблице представлена только часть методов):
| Метод | Описание |
public void LineDown(); |
Прокрутка вниз на одну строку (обычно, на 16 единиц) |
public void LineUp(); |
Прокрутка вверх на одну строку |
public void LineLeft(); |
Прокрутка влево на одну строку |
public void LineRight(); |
Прокрутка вправо на одну строку |
public void PageDown(); |
Прокрутка на страницу вниз |
public void PageLeft(); |
Прокрутка на страницу влево |
public void PageRight(); |
Прокрутка на страницу вправо |
public void PageUp(); |
Прокрутка на страницу вверх |
public void ScrollToHorizontalOffset(double offset); |
Прокрутка влево или вправо на определенное значение |
public void ScrollToVerticalOffset(double offset); |
Прокрутка вверх или вниз на определенное значение |
ScrollToEnd() |
Прокрутка в конец окна |
ScrollToHome() |
Прокрутка в самый вверх окна |
Все эти методы можно сгруппировать по трем группам:
- Прокрутка на элемент (методы вида
Line[Up, Down, Left, Right]()) - Прокрутка на страницу (методы вида
Page[Up, Down, Left, Right]()) - Прокрутка на определенное значение (методы вида
ScrollTo)
Чтобы продемонстрировать работу этих методов, перепишем код нашего приложения следующим образом:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Orientation="Vertical">
<ScrollViewer x:Name="scroll"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
ScrollChanged="scroll_ScrollChanged"
Height="400">
<StackPanel Width="700" Height="400">
<Border Height="50" Background="Blue"></Border>
<Border Height="50" Background="Green"></Border>
<Border Height="50" Background="Red"></Border>
<Border Height="50" Background="Yellow">
<TextBlock x:Name="info"/>
</Border>
<Border Height="50" Background="Blue"></Border>
<Border Height="50" Background="Green"></Border>
<Border Height="50" Background="Yellow"></Border>
<Border Height="50" Background="Blue"></Border>
</StackPanel>
</ScrollViewer>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Content="Вверх" Grid.Column="0" Margin="4" Click="Button_Click" />
<Button Content="Вниз" Grid.Column="1" Margin="4" Click="Button_Click_1" />
<Button Content="Влево" Grid.Column="2" Margin="4" Click="Button_Click_2" />
<Button Content="Вправо" Grid.Column="3" Margin="4" Click="Button_Click_3"/>
</Grid>
</StackPanel>
</Window>
Здесь мы добавили контейнер Grid с четырьмя кнопками для программной прокрутки ScrollViewer вверх, вниз, вправо и влево:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Content="Вверх" Grid.Column="0" Margin="4" Click="Button_Click" />
<Button Content="Вниз" Grid.Column="1" Margin="4" Click="Button_Click_1" />
<Button Content="Влево" Grid.Column="2" Margin="4" Click="Button_Click_2" />
<Button Content="Вправо" Grid.Column="3" Margin="4" Click="Button_Click_3"/>
</Grid>
У каждой из кнопок мы определили обработчики события Click. В этих обработчиках мы будем использовать методы из той или иной группы для прокрутки. Например,
private void Button_Click(object sender, RoutedEventArgs e)
{
scroll.LineUp();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
scroll.LineDown();
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
scroll.LineLeft();
}
private void Button_Click_3(object sender, RoutedEventArgs e)
{
scroll.LineRight();
}
Запустим приложение и попробуем, управляя кнопками выполнить прокрутку:
Прокрутка с помощью методов типа Line...() осуществляет переход к следующему элементу в логическом дереве. Обычно, смещение идет на 16 единиц в заданном направлении. Немного по другому работают методы прокрутки на страницу.
Изменим обработчики кнопок следующим образом:
private void Button_Click(object sender, RoutedEventArgs e)
{
scroll.PageUp();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
scroll.PageDown();
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
scroll.PageLeft();
}
private void Button_Click_3(object sender, RoutedEventArgs e)
{
scroll.PageRight();
}
А также изменим высоту ScrollViewer, установив её равной 100:
<ScrollViewer x:Name="scroll"
...
Height="100">
...
</ScrollViewer>
Теперь запустите приложение и убедитесь, что ScrollViewer пытает прокрутить содержимое на размер своей высоты (или ширины) или же, если оставшееся расстояние прокрутки меньше этого значения, то прокрутка идёт до конца:
И, наконец, продемонстрируем прокрутку на произвольное значение. Изменим обработчики кнопок следующим образом:
private void Button_Click(object sender, RoutedEventArgs e)
{
scroll.ScrollToVerticalOffset(-25);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
scroll.ScrollToVerticalOffset(25);
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
scroll.ScrollToHorizontalOffset(25);
}
private void Button_Click_3(object sender, RoutedEventArgs e)
{
scroll.ScrollToHorizontalOffset(-25);
}
Обратите внимание, что для прокрутки вверх и влево мы используем отрицательные значения смещения, а для прокрутки вниз и влево — положительные. При этом, прокрутка производится один раз, то есть, сколько бы раз мы не нажимали на кнопку — величина прокрутки не изменится после первого нажатия. Поэтому, если вам необходимо программно прокручивать содержимое ScrollViewer на произвольное значение, то необходимо изменить код обработчиков следующим образом (на примере прокрутки вверх и вниз)
private void Button_Click(object sender, RoutedEventArgs e)
{
scroll.ScrollToVerticalOffset(scroll.ContentVerticalOffset - 25);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
scroll.ScrollToVerticalOffset(scroll.ContentVerticalOffset + 25);
}
Теперь текущее значение прокрутки будет браться из свойства scroll.ContentVerticalOffset и можно программно пролистывать ScrollViewer на 25 единиц в обоих направлениях до конца.
Итого
Элемент ScrollViewer позволяет организовать прокрутку содержимого, когда оно полностью не умещается в доступную область просмотра. Прокрутка может осуществляться как с использование полос прокрутки, так и программно на один элемент, на страницу или на произвольное значение.
