Search
Write a publication
Pull to refresh

WPF: NumericUpDown своими руками из стандарного Slider.

Уважаемые хабровчане!

Многие кто работал с WPF ощущает необходимость в стандартном компоненте DomainUpDown, который присутствует в WinForms или NumericUpDown из Delphi. В сети существует много примеров и готовых контролов реализующих функционал NumericUpDown, но используя стандартный класс Slider вы можете создать свой собственный NumericUpDown.

По сути принципы работы Slider и NumericUpDown очень похожи, есть минимальное значение и максимальное значение с помощью UI котрола осуществляеться премещение от минимума к максимуму, все разница лишь том какой UI мы будем использовать: ползунок для Slider-а или две кнопки «больше» и «меньше». Задача проста: меняем UI класса Slider и получаем краивую обертку NumericUpDown с сердцем Slider-а.

Создадим простой проект WPF Custom Control Library, далее длянашего контрола пропишем простую обертку:

using System.Windows;
using System.Windows.Controls;

namespace MyControls
{
  public class UpDownNumeric : Slider
  {
    static UpDownNumeric()
    {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(UpDownNumeric), new FrameworkPropertyMetadata(typeof(UpDownNumeric)));
    }
  }
}

* This source code was highlighted with Source Code Highlighter.

Теперь спокойно под этот контрол пишем XAML код для реализации UI NumericUpDown.

Сначала добавим пару кистей для нашего контрола:

<LinearGradientBrush x:Key="ButtonBackgroundBrush" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFF1F3F3" Offset="0"/>
    <GradientStop Color="#FFF0F2F2" Offset="0.5"/>
    <GradientStop Color="#FFE7EAEE" Offset="0.5"/>
    <GradientStop Color="#FFF6F7F8" Offset="1"/>
  </LinearGradientBrush>

  <LinearGradientBrush x:Key="HighlightedPressedButtonBrush" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFFDD4A8" Offset="0"/>
    <GradientStop Color="#FFFBAE60" Offset="0.4"/>
    <GradientStop Color="#FFF9972F" Offset="0.4"/>
    <GradientStop Color="#FFFDE499" Offset="1"/>
  </LinearGradientBrush>

  <LinearGradientBrush x:Key="HighlightedMouseOverButtonBrush" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFFFFDDC" Offset="0"/>
    <GradientStop Color="#FFFFE797" Offset="0.4"/>
    <GradientStop Color="#FFFFD754" Offset="0.4"/>
    <GradientStop Color="#FFFFE89F" Offset="1"/>
  </LinearGradientBrush>

  <SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />

* This source code was highlighted with Source Code Highlighter.

Создадим шаблоны для кнопок Up и Down:

<Style x:Key="UpButtonStyle" TargetType="{x:Type RepeatButton}">
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="IsTabStop" Value="false"/>
    <Setter Property="Focusable" Value="true"/>
    <Setter Property="Height" Value="11"/>
    <Setter Property="VerticalAlignment" Value="Top"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type RepeatButton}">
          <Border Name="Border" Background="{StaticResource ButtonBackgroundBrush}" BorderBrush="{StaticResource SolidBorderBrush}" BorderThickness="1">
          <Path
            x:Name="Arrow"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Data="M0,3 L3,0 6,3 z" Fill="Black"/>
          </Border>
          <ControlTemplate.Triggers>
          <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="Border" Property="Background" Value="{StaticResource HighlightedMouseOverButtonBrush}"/>
          </Trigger>
            <Trigger Property="IsPressed" Value="true">
              <Setter TargetName="Border" Property="Background" Value="{StaticResource HighlightedPressedButtonBrush}" />
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

  
  <Style x:Key="DownButtonStyle" TargetType="{x:Type RepeatButton}">
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="IsTabStop" Value="false"/>
    <Setter Property="Focusable" Value="true"/>
    <Setter Property="Height" Value="11"/>
    <Setter Property="VerticalAlignment" Value="Bottom"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type RepeatButton}">
          <Border Name="Border" Background="{StaticResource ButtonBackgroundBrush}" BorderBrush="{StaticResource SolidBorderBrush}" BorderThickness="1">
        <Path
        x:Name="Arrow"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Data="M0,0 L3,3 6,0 z" Fill="Black"/>
          </Border>
          <ControlTemplate.Triggers>
          <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="Border" Property="Background" Value="{StaticResource HighlightedMouseOverButtonBrush}"/>
          </Trigger>
            <Trigger Property="IsPressed" Value="true">
              <Setter TargetName="Border" Property="Background" Value="{StaticResource HighlightedPressedButtonBrush}" />
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

* This source code was highlighted with Source Code Highlighter.

Тут стоит пояснить зачем нужны эти две кнопки. В обычном Slider-е эти кнопки не видны пользователю, они прозрачные и располагаються на Track-е Slider-а, нажимая на слева или справа от ползунка Slider-а пользователь перемещает ползунок влева или вправо, эту функцию выполняют эти кнопки. Если их сделать видимыми и расположить там где нужно нам, то получим нужный нам визуальный функционал!

Теперь составляем стиль слайдера так что бы он был похож на NumericUpDown: нам нужен TextBox для вывода и ввода значения Value нашего контрола и Track для реализации механики, к кнопкам Track-а IncreaseRepeatButton и DecreaseRepeatButton привязываем стили UpButtonStyle и DownButtonStyle; перед кнопками распологаем TextBox и привязываем к свойству Text свойство Value нашего NumericUpDown:

<Style TargetType="{x:Type btc:BlackThemeUpDownNumeric}">
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="Height" Value="20"/>
    <Setter Property="MinWidth" Value="50"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type Slider}">
          <Grid>
            <Border BorderThickness="1,1,1,1" CornerRadius="2" BorderBrush="{StaticResource SolidBorderBrush}" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"          
            Focusable="False">
              <Grid>
                <Grid.ColumnDefinitions>
                  <ColumnDefinition Width="*"/>
                  <ColumnDefinition Width="17"/>
                </Grid.ColumnDefinitions>
       <TextBox x:Name="PART_EditableTextBox" Grid.Column="0"
       Text="{Binding Path=Value, RelativeSource={RelativeSource TemplatedParent}}"
       IsHitTestVisible="True"
       HorizontalAlignment="Stretch"
       VerticalAlignment="Stretch"
       Focusable="True"
       Background="White"
       Visibility="Visible"
       Foreground="Black" BorderThickness="0" Margin="-1,-1,-1,-1"/>
                <Grid Grid.Column="1" Margin="0,-1,-1,-1">
                  <Track Name="PART_Track" Orientation="Vertical" Height="0">
                    <Track.IncreaseRepeatButton>
                      <RepeatButton
            Style="{StaticResource UpButtonStyle}"
            Command="Slider.IncreaseLarge" Margin="0,-10,0,0"/>
                    </Track.IncreaseRepeatButton>
                    <Track.Thumb>
                      <Thumb Height="0" />
                    </Track.Thumb>
                    <Track.DecreaseRepeatButton>
                      <RepeatButton Margin="0,0,0,-10"
            Style="{StaticResource DownButtonStyle}"
            Command="Slider.DecreaseLarge" />
                    </Track.DecreaseRepeatButton>
                  </Track>
                </Grid>
              </Grid>
            </Border>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

* This source code was highlighted with Source Code Highlighter.

У вас должно получиться что то вроде этого:

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.