Главная » Разработка для Windows Phone 7 » Вариации на тему Slider

0

Как можно ожидать, один из самых сложных стандартных шаблонов Silverlight – это шаблон для Slider. Поэтому важно изучить его, особенно если вам не очень нравится шаблон Slider по умолчанию, реализованный в Windows Phone 7.

На первый взгляд Slider, кажется, не подходит под схему шаблонов, главным образом потому что он включает движущиеся части. Как же именно он реализуется?

Если посмотреть на документацию Slider, мы найдем в ней привычные теги TemplateVisualStateAttribute, а также коллекцию тегов TemplatePartAttribute (Атрибут части шаблона) (здесь я привожу их немного в другом порядке, чем они представлены в документации):

[TemplateVisualStateAttribute(Name = "Normal", GroupName = "CommonStates")] [TemplateVisualStateAttribute(Name = "Mouse0ver", GroupName = "CommonStates")] [TemplateVisualStateAttribute(Name = "Disabled", GroupName = "CommonStates")] [TemplateVisualStateAttribute(Name = "Focused", GroupName = "FocusStates")] [TemplateVisualStateAttribute(Name = "Unfocused", GroupName = "FocusStates")] [TemplatePartAttribute(Name = "HorizontalTemplate", Type = typeof(FrameworkElement))] [TemplatePartAttribute(Name = "HorizontalTrackLargeChangeDecreaseRepeatButton",

Type = typeof(RepeatButton))] [TemplatePartAttribute(Name = "HorizontalTrackLargeChangelncreaseRepeatButton",

Type = typeof(RepeatButton))] [TemplatePartAttribute(Name = "HorizontalThumb", Type = typeof(Thumb))] [TemplatePartAttribute(Name = "VerticalTemplate", Type = typeof(FrameworkElement))] [TemplatePartAttribute(Name = "VerticalTrackLargeChangeDecreaseRepeatButton",

Type = typeof(RepeatButton))] [TemplatePartAttribute(Name = "VerticalTrackLargeChangelncreaseRepeatButton",

Type = typeof(RepeatButton))] [TemplatePartAttribute(Name = "VerticalThumb", Type = typeof(Thumb))] public class Slider : RangeBase

Это означает, что шаблон для Slider должен включать восемь элементов с именами «HorizontalTemplate» (Горизонтальный шаблон) и т.д. Эти элементы называют «частями» шаблона. Части «HorizontalTemplate» и «VerticalTemplate» (Вертикальный шаблон) могут быть только типа FrameworkElement (или производным от FrameworkElement), но остальные части должны быть типа RepeatButton или Thumb (Бегунок).

RepeatButton и Thumb – это пара элементов управления, использовать которые в данной книге до сих пор не было повода. (Оба этих класса располагаются в пространстве имен System.Windows.Controls.Primitives – тонкий намек на то, что данные элементы управления предполагается использовать для построения других элементов управления.) RepeatButton аналогичен обычному Button, за исключением того что при удержании пальца на нем он формирует повторяющиеся события Click. Он идеально подходит для ScrollBar или Slider и, вероятно, создавался именно для этих целей.

Thumb – довольно специализированный элемент управления, который сообщает о том, каким образом пользователь перемещает его. Но Thumb сложно найти в стандартном Slider в Windows Phone 7, потому что он довольно хорошо спрятан в шаблоне темы. Одна из моих задач здесь – вернуть Thumb в Slider.

Элемент управления с частями (такой как Slider) перегружает метод OnApplyTemplate (При применении шаблона), чтобы получать уведомления при задании шаблона его свойству Template. После этого с помощью метода GetTemplateChild (Получить дочерний элемент шаблона) он находит элементы с заданными именами. Он может задавать обработчики событий для этих элементов и манипулировать ими в ходе работы. (Реализация этого процесса в коде будет показана ближе к концу данной главы.)

Стандартный Slider поддерживает горизонтальную и вертикальную ориентации, и шаблон включает два отдельных (и довольно независимых) шаблона для обеспечения этих ориентаций. Эти два шаблона представлены элементами «HorizontalTemplate» и «VerticalTemplate». Если свойство Orientation объекта Slider имеет значение Horizontal, Slider задает свойству Visibility элемента «HorizontalTemplate» значение Visible и свойству Visibility элемента «VerticalTemplate» значение Collapsed; и противоположные значения для вертикальной ориентации.

Самый простой подход при проектировании нового шаблона для Slider – использовать Grid с одной ячейкой, который будет включать оба этих шаблона. Вложенный Grid под именем «HorizontalTemplate» имеет три столбца с двумя элементами управления RepeatButton и одним Thumb. У другого вложенного Grid под именем «VerticalTemplate» – три строки.

Рассмотри то, что я называю «скелетом» шаблона для Slider, который определен как ресурс: Проект Silverlight: BareBonesSlider Файл: MainPage.xaml (фрагмент)

<phone:PhoneApplicationPage.Resources>

<ControlTemplate x:Key="bareBonesSliderTemplate" TargetType="Slider">

<Grid>

<Grid Name="HorizontalTemplate"> <Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions>

<RepeatButton Name="HorizontalTrackLargeChangeDecreaseRepeatButton" Grid.Column="0" Content="-" />

<Thumb Name="HorizontalThumb" Grid.Column="1" />

<RepeatButton Name="HorizontalTrackLargeChangeIncreaseRepeatButton" Grid.Column="2" Content="+" />

</Grid>

<Grid Name="VerticalTemplate"> <Grid.RowDefinitions>

<RowDefinition Height="*" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions>

<RepeatButton Name="VerticalTrackLargeChangeDecreaseRepeatButton" Grid.Row="0" Content="-" />

<Thumb Name="VerticalThumb" Grid.Row="1" />

<RepeatButton Name="VerticalTrackLargeChangeIncreaseRepeatButton" Grid.Row="2" Content="+" />

</Grid> </Grid> </ControlTemplate> </phone:PhoneApplicationPage.Resources>

Вовсе не обязательно, чтобы в шаблоне Slider элементы управления RepeatButton и Thumb располагались в Grid с тремя строками или тремя столбцами, но, безусловно, это самое простое решение. Обратите внимание, я присвоил свойствам Content элементов управления RepeatButton знаки плюс и минус в зависимости от их роли, что выглядит довольно необычным.

Сосредоточимся на ориентации Horizontal. RepeatButton для уменьшения значений располагается в первой ячейке Grid с шириной Auto, Thumb помещен во вторую ячейку Grid, для Width которой также задано значение Auto. Сам Thumb имеет фиксированную ширину, а вот ширина уменьшающегося RepeatButton меняется напрямую логикой Slider для отражения значения его свойства Value. Когда Value получает значение Minimum, RepeatButton достигает нулевой ширины. Если Value задано значение Maximum, ширина RepeatButton соответствует разности между шириной всего элемента управления и шириной Thumb.

Slider меняет значение свойства Value (и, следовательно, относительный размер двух элементов управления RepeatButton), когда пользователь касается RepeatButton или физически перемещает Thumb. (Более подробно элемент управления Thumb мы рассмотрим несколько позже.)

Далее в проекте BareBonesSlider (Простой ползунок) в области содержимого создаются два элемента управления Slider и применяется шаблон:

Проект Silverlight: BareBonesSlider Файл: MainPage.xaml (фрагмент)

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions>

<RowDefinition Height="Auto"/> <RowDefinition Height="*" /> </Grid.RowDefinitions>

<Slider Grid.Row="0"

0rientation="Horizontal"

Template="{StaticResource bareBonesSliderTemplate}" />

<Slider Grid.Row="1"

0rientation="Vertical"

Template="{StaticResource bareBonesSliderTemplate}" HorizontalAlignment="Center" />

</Grid>

Вот как выглядят все эти элементы управления при небольшом смещении ползунка относительно его исходного положения:

RepeatButton выглядит, как обычный Button, и Thumb – как прямоугольник, окруженный прозрачной областью.

Теперь, когда мы знаем, как создать пользовательский шаблон для Slider, можем ли мы придать ему немного более привлекательный вид? Да, и ключом к реализации этого является осознание того, что RepeatButton и Thumb наследуются от Control, т.е. у обоих классов есть свойство Template, и в рамках шаблона Slider мы можем описывать новые шаблоны для RepeatButton и Thumb специально для использования в шаблоне Slider.

Рассмотрим более замысловатый Slider, который также включает привязки шаблонов для свойств Background и Foreground. Для этих свойств в Style заданы значения по умолчанию. Также этот Style включает ControlTemplate. Здесь я привожу только внешний Grid объекта ControlTemplate, у которого есть собственный раздел Resources для описания очень простого ControlTemplate для RepeatButton и довольно развернутых шаблонов для горизонтального и вертикального Thumb:

Проект Silverlight: AlternativeSlider Файл: MainPage.xaml (фрагмент)

<phone:PhoneApplicationPage.Resources>

<Style x:Key="alternativeSliderStyle" TargetType="Slider"> <Setter Property="Background"

Value="{StaticResource PhoneBackgroundBrush}" /> <Setter Property="Foreground"

Value="{StaticResource PhoneForegroundBrush}" /> <Setter Property="Template"> <Setter.Value>

<ControlTemplate TargetType="Slider">

<Grid Background="{TemplateBinding Background}">

<Grid.Resources>

<ControlTemplate x:Key="repeatButtonTemplate" TargetType="RepeatButton"> <Rectangle Fill="Transparent" /> </ControlTemplate>

<Style x:Key="horizontalThumbStyle" TargetType="Thumb"> <Setter Property="Width" Value="72" /> <Setter Property="Height" Value="72" />

<Setter Property="Template"> <Setter.Value>

<ControlTemplate TargetType="Thumb"> <Border Background="Transparent"> <Rectangle Margin="18 0" RadiusX="6" RadiusY="6"

Stroke="{StaticResource PhoneAccentBrush}" Fill="{TemplateBinding Foreground}" />

</Border> </ControlTemplate> </Setter.Value> </Setter> </Style>

<Style x:Key="verticalThumbStyle" TargetType="Thumb"> <Setter Property="Width" Value="72" /> <Setter Property="Height" Value="72" /> <Setter Property="Template"> <Setter.Value>

<ControlTemplate TargetType="Thumb"> <Border Background="Transparent"> <Rectangle Margin="0 18" RadiusX="6" RadiusY="6"

Stroke="{StaticResource PhoneAccentBrush}" Fill="{TemplateBinding Foreground}" />

</Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </Grid.Resources>

</Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>

</phone:PhoneApplicationPage.Resources>

Шаблон RepeatButton – это всего лишь прозрачный Rectangle. (Rectangle должен иметь прозрачный, а не нулевой Fill, тогда он сможет принимать сенсорный ввод.) Однако для стилей Thumb мне пришлось переопределить свойства Width и Height. В стиле темы им задано значение 48, что мне показалось слишком большим. Я задал Border с прозрачным фоном для создания большей мишени касания, но видимую часть сделал несколько поменьше, чтобы она больше была похожей на обычный бегунок Slider.

Описания обоих элементов Grid для горизонтальной и вертикальной ориентации начинаются с Rectangle, который обеспечивает своего рода визуальную обратную связь. Каждый RepeatButton и Thumb используют ControlTemplate либо Style, описанный для этого элемента управления ранее:

Проект Silverlight: AlternativeSlider Файл: MainPage.xaml (фрагмент)

<Grid Name="HorizontalTemplate"> <Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions>

<Rectangle Grid.Column="0" Grid.ColumnSpan="3" Height="8" Margin="12 0"

Stroke="{TemplateBinding Foreground}" Fill="{StaticResource PhoneAccentBrush}" />

<RepeatButton Name="HorizontalTrackLargeChangeDecreaseRepeatButton" Grid.Column="0"

Template="{StaticResource repeatButtonTemplate}" />

<Thumb Name="HorizontalThumb" Grid.Column="1"

Style="{StaticResource horizontalThumbStyle}" />

<RepeatButton Name="HorizontalTrackLargeChangeIncreaseRepeatButton" Grid.Column="2"

Template="{StaticResource repeatButtonTemplate}" />

</Grid>

<Grid Name="VerticalTemplate"> <Grid.RowDefinitions>

<RowDefinition Height="*" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions>

<Rectangle Grid.Row="0" Grid.RowSpan="3" Width="8" Margin="0 12"

Stroke="{TemplateBinding Foreground}" Fill="{StaticResource PhoneAccentBrush}" />

<RepeatButton Name="VerticalTrackLargeChangeDecreaseRepeatButton" Grid.Row="0"

Template="{StaticResource repeatButtonTemplate}" />

<Thumb Name="VerticalThumb" Grid.Row="1"

Style="{StaticResource verticalThumbStyle}" />

<RepeatButton Name="VerticalTrackLargeChangeIncreaseRepeatButton" Grid.Row="2"

Template="{StaticResource repeatButtonTemplate}" />

</Grid>

Область содержимого в данном приложении очень похожа на предыдущее, только элементы управления Slider используют этот новый стиль:

Проект Silverlight: AlternativeSlider Файл: MainPage.xaml (фрагмент)

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions>

<RowDefinition Height="Auto"/> <RowDefinition Height="*" /> </Grid.RowDefinitions>

<Slider Grid.Row="0"

Orientation="Horizontal"

Style="{StaticResource alternativeSliderStyle}" /> <Slider Grid.Row="1"

И вот что мы получаем:

Я решил перенести эти Style с ControlTemplate в библиотеку Petzold.Phone.Silverlight как Style по умолчанию для класса AltSlider. У этого класса нет дополнительных свойств, поэтому в файле кода необходимо лишь указать класс, от которого наследуется AltSlider, и класс, используемый для обнаружения местонахождения Style по умолчанию:

Проект Silverlight: Petzold.Phone.Silverlight Файл: AltSlider.cs

using System.Windows.Controls;

namespace Petzold.Phone.Silverlight {

public class AltSlider : Slider {

public AltSlider() {

this.DefaultStyleKey = typeof(AltSlider);

}

}

}

Этот Style по умолчанию (включающий ControlTemplate) описывается в файле generic.xaml. Я не буду приводить здесь этот файл полностью, потому что в нем преимущественно повторяется определение Style из проекта AlternativeSlider (Альтернативный слайдер):

Проект Silverlight: Petzold.Phone.Silverlight Файл: Themes/generic.xaml (фрагмент)

<ResourceDictionary

0rientation="Vertical"

Style="{StaticResource alternativeSliderStyle}" HorizontalAlignment="Center" />

</Grid>

xmlns="http://schemas.microsoft.com/winfx/2 0 0 6/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2 0 0 6/xaml" xmlns:local="clr-namespace:Petzold.Phone.Silverlight">

<Style TargetType="local:AltSlider"> <Setter Property="Background"

Value="{StaticResource PhoneBackgroundBrush}" /> <Setter Property="Foreground"

Value="{StaticResource PhoneForegroundBrush}" /> <Setter Property="Template"> <Setter.Value>

<ControlTemplate TargetType="local:AltSlider">

</ControlTemplate> </Setter.Value> </Setter> </Style>

</ResourceDictionary>

Безусловно, теперь необходимо выполнить тестирование. В проекте AltSliderDemo имеется ссылка на проект Petzold.Phone.Silverlight и объявление пространства имен XML для него. Область содержимого такая же, как и в двух предыдущих приложениях:

Проект Silverlight: AltSliderDemo Файл: MainPage.xaml (фрагмент)

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions>

<RowDefinition Height="Auto"/> <RowDefinition Height="*" /> </Grid.RowDefinitions>

<petzold:AltSlider Grid.Row="0"

Orientation="Horizontal"

Style="{StaticResource altSliderStyle}"/>

<petzold:AltSlider Grid.Row="1"

Orientation="Vertical" HorizontalAlignment="Center" Style="{StaticResource altSliderStyle}" />

</Grid>

Как видим, свойствам Style этих элементов управления AltSlider заданы некоторые значения. Что это? Это ссылки на Style, определенный в коллекции Resources страницы:

Проект Silverlight: AltSliderDemo Файл: MainPage.xaml (фрагмент)

<phone:PhoneApplicationPage.Resources> <Style x:Key="altSliderStyle"

TargetType="petzold:AltSlider"> <Setter Property="Margin" Value="12" />

<Setter Property="Background" Value="{StaticResource PhoneChromeBrush}" /> </Style>

</phone:PhoneApplicationPage.Resources>

Единственным назначением этого Style является демонстрация того, что определение Style по умолчанию для класса AltSlider не устраняет возможности задания впоследствии другого Style, даже такого, который будет переопределять одно из свойств исходного Style. Вот что мы видим на экране при выполнении данного приложения:

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

  • Комментарии