数值范围选择控件RangeSlider

原文: 数值范围选择控件RangeSlider

RangeSlider控件用于在一个指定上下限的范围中选择一个数值范围,因此该控件的MaximumMinimum属性用于指定上下限;而SelectionStartSelectionEnd用于指定选择的范围,还有一个Change属性用于指定SelectionStartSelectionEnd的最小变化值。运行效果如下图所示。默认样式很难看,不过定制一个漂亮的样式很简单。

  

 

以下是控件的默认样式:

<Style TargetType="{x:Type local:RangeSlider}">

        <Setter Property="Template">

            <Setter.Value>

                <ControlTemplate TargetType="{x:Type local:RangeSlider}">

                    <Border BorderBrush="{TemplateBinding BorderBrush}"

                            BorderThickness="{TemplateBinding BorderThickness}"

                            Background="{TemplateBinding Background}"

                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">

                        <Grid Name="PART_GridContainerName">

                            <Grid.ColumnDefinitions>

                                <ColumnDefinition/>

                                <ColumnDefinition/>

                                <ColumnDefinition/>

                            </Grid.ColumnDefinitions>

                            <RepeatButton Grid.Column="0" Command="{x:Static local:RangeSlider.MoveBackward}"/>

                            <RepeatButton Grid.Column="2" Command="{x:Static local:RangeSlider.MoveForward}"/>

                            <Grid Grid.Column="1">

                                <Thumb Name="PART_LeftThumb" IsTabStop="False" Cursor="SizeWE" Width="5" Margin="-5 0 00" HorizontalAlignment="Left"/>

                                <Thumb Name="PART_CenterThumb" IsTabStop="False" Cursor="ScrollAll" MinWidth="5"/>

                                <Thumb Name="PART_RightThumb" IsTabStop="False" Cursor="SizeWE" Width="5" Margin="0 0 -50" HorizontalAlignment="Right"/>

                            </Grid>

                        </Grid>

                    </Border>

                </ControlTemplate>

            </Setter.Value>

        </Setter>

    </Style>

从控件的样式中可以看出,三个Thumb分别支持拖动改变开始值,结束值和同时改变两个值。两个RepeatButton支持按最小变化值同时改变开始值和结束值;按下Ctl键和左右箭头可达到相同的目的。

RangeSlider控件还实现了SelectedRangeChanged路由事件,这样就可以在SelectionStartSelectionEnd改变的时候发出事件通知。

RangeSlider控件还支持命令;也就是说它实现了ICommandSource接口,这样就可以和MVVM模式很好的结合起来。剩下也没什么好说的了,代码贴上:

using System;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Text;

usingSystem.Windows.Controls;

usingSystem.Windows;

usingSystem.Windows.Input;

usingSystem.Windows.Controls.Primitives;

usingSystem.ComponentModel;

 

namespace MySolution.Controls

{

    [TemplatePart(Name = GridContainerName, Type = typeof(Grid))]

    [TemplatePart(Name = LeftThumbName, Type = typeof(Thumb))]

    [TemplatePart(Name = CenterThumbName, Type = typeof(Thumb))]

    [TemplatePart(Name = RightThumbName, Type = typeof(Thumb))]

    public class RangeSlider : Control,ICommandSource

    {

       static RangeSlider()

       {

           DefaultStyleKeyProperty.OverrideMetadata(typeof(RangeSlider), new FrameworkPropertyMetadata(typeof(RangeSlider)));

       }

 

        #region PartNames

 

       private conststring LeftThumbName = "PART_LeftThumb";

       private conststring CenterThumbName = "PART_CenterThumb";

       private conststring RightThumbName = "PART_RightThumb";

       private conststring GridContainerName = "PART_GridContainerName";

 

        #endregion

 

        #region Minimum

 

        public long Minimum

       {

           get { return(long)GetValue(MinimumProperty); }

           set { SetValue(MinimumProperty, value); }

       }

 

       public staticreadonly DependencyPropertyMinimumProperty =

         DependencyProperty.Register("Minimum", typeof(long), typeof(RangeSlider),

         new FrameworkPropertyMetadata(0L,OnMinimumChanged));

 

       private staticvoid OnMinimumChanged(DependencyObjectsender, DependencyPropertyChangedEventArgse)

       {

           RangeSlider slider = sender as RangeSlider;

           slider.CoerceValue(MaximumProperty);

           slider.CoerceValue(SelectionStartProperty);

           slider.CoerceValue(SelectionEndProperty);

           slider.Relayout();

       }

 

        #endregion

 

        #region Maximum

 

       public longMaximum

       {

           get { return(long)GetValue(MaximumProperty); }

           set { SetValue(MaximumProperty, value); }

       }

 

       public staticreadonly DependencyPropertyMaximumProperty =

         DependencyProperty.Register("Maximum", typeof(long), typeof(RangeSlider),

         new FrameworkPropertyMetadata(100L,OnMaximumChanged, CoerceMaximum));

 

       private staticvoid OnMaximumChanged(DependencyObjectsender, DependencyPropertyChangedEventArgse)

       {

           RangeSlider slider = sender as RangeSlider;

           slider.CoerceValue(SelectionStartProperty);

           slider.CoerceValue(SelectionEndProperty);

           slider.Relayout();

       }

 

       private staticobject CoerceMaximum(DependencyObjectsender, object baseValue)

       {

           RangeSlider slider = sender as RangeSlider;

           long value = (long)baseValue;

 

           if (value < slider.Minimum + 1)

           {

                returnslider.Minimum + 1;

           }

 

           return baseValue;

       }

 

        #endregion

 

        #regionSelectionStart

 

       public longSelectionStart

       {

           get { return(long)GetValue(SelectionStartProperty); }

           set { SetValue(SelectionStartProperty, value); }

       }

 

       public staticreadonly DependencyPropertySelectionStartProperty =

         DependencyProperty.Register("SelectionStart", typeof(long), typeof(RangeSlider),

         new FrameworkPropertyMetadata(0L,OnSelectionStartChanged, CoerceSelectionStart));

 

       private staticvoid OnSelectionStartChanged(DependencyObject sender, DependencyPropertyChangedEventArgse)

       {

           RangeSlider slider = sender as RangeSlider;

           slider.CoerceValue(SelectionEndProperty);

           slider.Relayout();

       }

 

       private staticobject CoerceSelectionStart(DependencyObject sender, objectbaseValue)

       {

           RangeSlider slider = sender as RangeSlider;

           long value = (long)baseValue;

 

           if (slider.SelectionEnd >=slider.Minimum && slider.SelectionEnd <= slider.Maximum)

           {

                if(value > slider.SelectionEnd)

                {

                    returnslider.SelectionEnd;

                }

           }

 

           if (value < slider.Minimum)

           {

                returnslider.Minimum;

           }

 

           if (value > slider.Maximum)

           {

                returnslider.Maximum;

           }

 

           return baseValue;

       }

 

        #endregion

 

        #regionSelectionEnd

 

       public longSelectionEnd

       {

           get { return(long)GetValue(SelectionEndProperty); }

           set { SetValue(SelectionEndProperty, value); }

       }

 

       public staticreadonly DependencyPropertySelectionEndProperty =

         DependencyProperty.Register("SelectionEnd", typeof(long), typeof(RangeSlider),

         new FrameworkPropertyMetadata(10L,OnSelectionEndChanged, CoerceSelectionEnd));

 

       private staticvoid OnSelectionEndChanged(DependencyObject sender, DependencyPropertyChangedEventArgse)

       {

           RangeSlider slider = sender as RangeSlider;

           slider.CoerceValue(SelectionStartProperty);

           slider.Relayout();

       }

 

       private staticobject CoerceSelectionEnd(DependencyObject sender, objectbaseValue)

       {

           RangeSlider slider = sender as RangeSlider;

           long value = (long)baseValue;

 

           if (slider.SelectionStart >=slider.Minimum && slider.SelectionStart <= slider.Maximum)

           {

                if(value < slider.SelectionStart)

                {

                    returnslider.SelectionStart;

                }

           }

 

           if (value < slider.Minimum)

           {

                returnslider.Minimum;

           }

 

           if (value > slider.Maximum)

           {

                returnslider.Maximum;

           }

 

           returnbaseValue;

       }

 

        #endregion

 

        #region Change

 

       public longChange

       {

           get { return(long)GetValue(ChangeProperty); }

           set { SetValue(ChangeProperty, value); }

       }

 

       public staticreadonly DependencyPropertyChangeProperty =

         DependencyProperty.Register("Change", typeof(long), typeof(RangeSlider),

         new FrameworkPropertyMetadata(1L),ValidateChange);

 

       private staticbool ValidateChange(objectvalue)

        {

           return (long)value!= 0;

       }

 

        #endregion

 

        #region RoutedEvents

 

       ///<summary>

       /// Eventraised whenever the selected range is changed

       ///</summary>

       public staticreadonly RoutedEventSelectionChangedEvent = EventManager.RegisterRoutedEvent(

           "SelectionChangedEvent",

           RoutingStrategy.Bubble,

           typeof(EventHandler<RangeSelectionChangedEventArgs>),

           typeof(RangeSlider));

 

       ///<summary>

       /// Eventraised whenever the selected range is changed

       ///</summary>

       public eventEventHandler<RangeSelectionChangedEventArgs>SelectedRangeChanged

       {

           add { AddHandler(SelectionChangedEvent, value); }

           remove {RemoveHandler(SelectionChangedEvent, value); }

       }

 

       private voidOnSelectedRangeChanged(RangeSelectionChangedEventArgse)

       {

           e.RoutedEvent = SelectionChangedEvent;

           RaiseEvent(e);

 

           ExecuteCommand();

       }

 

       private voidExecuteCommand()

       {

           if (Command != null)

           {

                RoutedCommandcommand = Command as RoutedCommand;

 

                if(command != null)

                {

                   command.Execute(CommandParameter,CommandTarget);

                }

                else

                {

                   Command.Execute(CommandParameter);

                }

           }

       }

 

        #endregion

 

        #regionCommands

 

        #region Commanddeclaration

 

       ///<summary>

       /// Command tomove back the selection

       ///</summary>

       public staticRoutedUICommand MoveBackward =

         new RoutedUICommand("Move Backward", "MoveBackward", typeof(RangeSlider),

           new InputGestureCollection(new InputGesture[]{ new KeyGesture(Key.Left, ModifierKeys.Control)}));

 

       ///<summary>

       /// Command tomove forward the selection

       ///</summary>

       public staticRoutedUICommand MoveForward =

         new RoutedUICommand("Move Forward", "MoveForward",typeof(RangeSlider),

           new InputGestureCollection(new InputGesture[]{ new KeyGesture(Key.Right, ModifierKeys.Control)}));

 

        #endregion

 

        #region CommandHandlers

 

       private voidMoveBackwardHandler(object sender, ExecutedRoutedEventArgs e)

       {

           MoveSelection(-Change);

       }

 

       private voidMoveForwardHandler(object sender, ExecutedRoutedEventArgs e)

       {

           MoveSelection(Change);

       }

 

        #endregion

 

        #endregion

 

        #regionConstructor

 

       public RangeSlider()

       {

           CommandBindings.Add(new CommandBinding(MoveBackward,MoveBackwardHandler));

           CommandBindings.Add(new CommandBinding(MoveForward, MoveForwardHandler));

       }

 

        #endregion

 

        #region Fields

 

       private Thumb_centerThumb;

       private Thumb_leftThumb;

       private Thumb_rightThumb;

       private Grid_gridContainer;

 

        #endregion

 

       public overridevoid OnApplyTemplate()

       {

           base.OnApplyTemplate();

 

           _centerThumb = Template.FindName(CenterThumbName, this) as Thumb;

           _leftThumb = Template.FindName(LeftThumbName, this)as Thumb;

           _rightThumb = Template.FindName(RightThumbName, this)as Thumb;

           _gridContainer = Template.FindName(GridContainerName, this) as Grid;

 

           _leftThumb.DragDelta += DragSelectionStart;

           _centerThumb.DragDelta += DragSelection;

           _rightThumb.DragDelta += DragSelectionEnd;

 

           Relayout();

       }

 

       private voidRelayout()

       {

           if (_gridContainer == null || SelectionEnd - SelectionStart < 0)

           {

                return;

           }

 

           double colWidth1 = SelectionStart -Minimum;

           double colWidth2 = SelectionEnd -SelectionStart;

           double colWidth3 = Maximum -SelectionEnd;

 

           _gridContainer.ColumnDefinitions[0].Width = newGridLength(colWidth1, GridUnitType.Star);

           _gridContainer.ColumnDefinitions[1].Width = newGridLength(colWidth2, GridUnitType.Star);

           _gridContainer.ColumnDefinitions[2].Width = newGridLength(colWidth3, GridUnitType.Star);

 

           OnSelectedRangeChanged(new RangeSelectionChangedEventArgs(SelectionStart,SelectionEnd));

       }

 

        #regionDragHandlers

 

       private voidDragSelection(object sender, DragDeltaEventArgs e)

       {

           long offsetValue =GetNormalizedValue(e.HorizontalChange);

           MoveSelection(offsetValue);

       }

 

       private voidMoveSelection(long offsetValue)

       {

           if (offsetValue < 0)

           {

                if(SelectionStart == Minimum)

                {

                    return;

                }

 

                longrange = SelectionEnd - SelectionStart;

                longnewValue = SelectionStart + offsetValue;

                SelectionStart = Math.Max(Minimum, newValue);

                SelectionEnd = SelectionStart +range;

           }

           else if(offsetValue > 0)

           {

                if(SelectionEnd == Maximum)

                {

                    return;

                }

 

                longrange = SelectionEnd - SelectionStart;

                longnewValue = SelectionEnd + offsetValue;

                SelectionEnd = Math.Min(Maximum, newValue);

                SelectionStart = SelectionEnd -range;

           }

       }

 

       private voidMoveSelectionStart(long offsetValue)

       {

           if (offsetValue < 0)

           {

                if(SelectionStart == Minimum)

               {

                    return;

                }

 

                longnewValue = SelectionStart + offsetValue;

                SelectionStart = Math.Max(Minimum, newValue);

           }

           else if(offsetValue > 0)

           {

                longmax = SelectionEnd - Change;

                if(SelectionStart == max)

                {

                    return;

                }

 

                longnewValue = SelectionStart + offsetValue;

                SelectionStart = Math.Min(max, newValue);

           }

       }

 

       private voidMoveSelectionEnd(long offsetValue)

       {

           if (offsetValue < 0)

           {

                longmin = SelectionStart + Change;

                if(SelectionEnd == min)

                {

                    return;

                }

 

                longnewValue = SelectionEnd + offsetValue;

                SelectionEnd = Math.Max(min, newValue);

           }

           else if(offsetValue > 0)

           {

                if(SelectionEnd == Maximum)

                {

                    return;

                }

 

                longnewValue = SelectionEnd + offsetValue;

                SelectionEnd = Math.Min(Maximum, newValue);

           }

       }

 

        private void DragSelectionEnd(objectsender, DragDeltaEventArgs e)

       {

           long offsetValue =GetNormalizedValue(e.HorizontalChange);

           MoveSelectionEnd(offsetValue);

       }

 

       private voidDragSelectionStart(object sender, DragDeltaEventArgs e)

       {

           long offsetValue =GetNormalizedValue(e.HorizontalChange);

           MoveSelectionStart(offsetValue);

       }

 

        #endregion

 

        #region HelperMethod

 

       private longGetNormalizedValue(double pixelLength)

       {

           long offsetValue = (long)((Maximum - Minimum) * (pixelLength /RenderSize.Width));

           long mod = offsetValue % Change;

 

           if (Math.Abs(mod)< Change / 2)

           {

               offsetValue = Math.Sign(offsetValue) * ((long)(Math.Abs(offsetValue) /Change)) * Change;

           }

           else

           {

                offsetValue = Math.Sign(offsetValue) * ((long)(Math.Abs(offsetValue) /Change) + 1) * Change;

            }

 

           return offsetValue;

       }

 

        #endregion

 

        #regionICommandSource Members

 

       public staticreadonly DependencyPropertyCommandProperty =

           DependencyProperty.Register("Command", typeof(ICommand), typeof(RangeSlider), new PropertyMetadata(null,OnCommandChanged));

 

       public staticreadonly DependencyPropertyCommandParameterProperty =

           DependencyProperty.Register("CommandParameter", typeof(object), typeof(RangeSlider),new FrameworkPropertyMetadata(null));

 

       public staticreadonly DependencyPropertyCommandTargetProperty =

           DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(RangeSlider),new FrameworkPropertyMetadata(null));

 

       public objectCommandParameter

       {

           get { return(object)GetValue(CommandParameterProperty); }

           set { SetValue(CommandParameterProperty,value); }

       }

 

       public ICommandCommand

       {

           get { return(ICommand)GetValue(CommandProperty); }

           set { SetValue(CommandProperty, value); }

       }

 

       public IInputElementCommandTarget

       {

           get { return(IInputElement)GetValue(CommandTargetProperty);}

           set { SetValue(CommandTargetProperty, value); }

       }

 

       private staticvoid OnCommandChanged(DependencyObjectsender, DependencyPropertyChangedEventArgse)

       {

           RangeSlider slider = sender as RangeSlider;

           slider.OnCommandChanged(e.OldValue as ICommand, e.NewValue asICommand);

       }

 

       private voidOnCommandChanged(ICommand oldCommand, ICommand newCommand)

       {

           if (oldCommand != null)

           {

                oldCommand.CanExecuteChanged -=CanExecuteChanged;

           }

 

           if (newCommand != null)

           {

                newCommand.CanExecuteChanged +=CanExecuteChanged;

           }

       }

 

       private voidCanExecuteChanged(object sender, EventArgs e)

       {

           if (Command != null)

           {

                RoutedCommandcommand = Command as RoutedCommand;

 

                if(command != null)

                {

                    IsEnabled = command.CanExecute(CommandParameter,CommandTarget);

                }

                else

                {

                    IsEnabled =Command.CanExecute(CommandParameter);

                }

           }

       }

 

        #endregion

    }

}

 

 

猜你喜欢

转载自www.cnblogs.com/lonelyxmas/p/9919927.html
今日推荐