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

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

  

 

以下是控件的默认样式:

<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路由事件,这样就可以在SelectionStart和SelectionEnd改变的时候发出事件通知。

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

    }

}

 

 

 

数值范围选择控件RangeSlider的更多相关文章

  1. 通用数据水平层级选择控件v0.70升级版使其支持jQuery v1.9.1

    升级原因:作者原来脚本支持的jquery版本太低了,查找了下资料,使得它能支持最新版本的jquery 备注说明:脚本代码源作者跟源文出处很难找,只能在此特感谢他的分享. 更新部分: 1.新版本不再支持 ...

  2. Windows Phone 8.1 新特性 - 控件之列表选择控件

    本篇我们来介绍Windows Phone 8.1 新特性中的列表选择控件. 在Windows Phone 8 时代,大家都会使用 LongListSelector 来实现列表选择控件,对数据进行分组显 ...

  3. SNF开发平台WinForm之三-开发-单表选择控件创建-SNF快速开发平台3.3-Spring.Net.Framework

    3.1运行效果: 3.2开发实现: 3.2.1 这个开发与第一个开发操作步骤是一致的,不同之处就是在生成完代码之后,留下如下圈红程序,其它删除. 第一个开发地址:开发-单表表格编辑管理页面 http: ...

  4. 用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)

    本文为原创,如有转载,请注明出处:http://www.cnblogs.com/jackybu 前言 章节: 1.需求描述以及c/c++实现日期和月历的基本操作 2.ios实现自绘日期选择控件 3.a ...

  5. js 跨域的问题 (同一个主域名不同的二级域名下的跨域问题) 解决 WdatePicker.js my97日期选择控件

    例如域名是  a.xx.com  和 b.xx.com    如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain. 如果iframe的时候  a包含b  为 ...

  6. JQuery好用的日期选择控件 DatePicker

    近期发现一个很好的基于JQ的前端UI日期选择控件Jquery.DatePicker.js 下载地址:jquery.DatePIcker.js 演示地址:DatePicker - 基于jQuery 1. ...

  7. [Ext JS 4] 实战之 带week(星期)的日期选择控件(三)

    前言 在 [Ext JS 4] 实战之 带week(星期)的日期选择控件(二) 的最后,有提到一个解决方案. 不过这方案有一个条件  ==> “2. 每年的周数从(1-52), 如果超过52 周 ...

  8. 使用sui实现的选择控件【性别、日期、省市级联】

    使用sui mobile做的选择控件,其中sm.js有修改,增加自定义api,详情请看index.html的注释,不多说了,上代码 <!DOCTYPE html> <html> ...

  9. android中选择控件与选择界面自然过度效果的实现--一种新的交互设计

    转载请标明出处: http://blog.csdn.net/jianghejie123/article/details/40648931 在安卓中经常遇到须要选择一个东西的功能,比方选择日期.选择文件 ...

随机推荐

  1. linux下查看动态链接库依赖关系的命令 x86: ldd *.so arm: arm-linux-readelf -d *.so 实际例子: 以项目中用到的库librtsp.so分析: lijun@ubuntu:~/workspace$ arm-hisiv100nptl-linux-ld -d librtsp.so arm-hisiv100nptl-linux-ld:

    linux下查看动态链接库依赖关系的命令 x86:ldd    *.so arm:arm-linux-readelf    -d    *.so 实际例子:以项目中用到的库librtsp.so分析:l ...

  2. fatal error C1859的有效解决办法

    作者:朱金灿来源:http://blog.csdn.net/clever101 在服务器(操作系统为Widows Server2008)上使用VS C++2008编译工程,总是出现这样一个错误:fat ...

  3. 如何使stm32程序更好移植, 结构体相当于define

    原创:转载请标注引用地址 如何定义 led1对应于PA8  呢 :对于我一开始学习的方法:: #include "main.h" #define led1      GPIO_Pi ...

  4. [CSS] Dynamically Size Elements with Pure CSS

    Learn how to size elements based on the dimensions of the viewport, even when the browser is resized ...

  5. jquery-11 如何制作鼠标右键菜单

    jquery-11 如何制作鼠标右键菜单 一.总结 一句话总结:核心原理:找到右键菜单事件contextmenu,return false去掉默认事件,然后判断用户是否点的右键,然后在鼠标的位置显示菜 ...

  6. Python 标准库和第三方库的安装位置、Python 第三方库安装的各种问题及解决

    首先使用 sys 下的 path 变量查看所有的 python 路径: import sys sys.path 标准库 lib 目录下(home 目录/pythonXX.XX/lib) 第三方库 在 ...

  7. java并发api总结

    开发十年,就只剩下这套架构体系了! >>>   1.java.util.concurrent包 1.1 Executors Executor:接口,仅有一个方法为execute(Ru ...

  8. Shuttle ESB(三)——架构模型介绍(2)

    上一篇文章中,介绍了Shuttle ESB架构模型中的三个重要部分. 今天,我们继续介绍剩余的三个内容:模式和消息路由. 四.模式 Request/Response(请求/响应模式) 对基于Reque ...

  9. 【机器学习实战】第3章 决策树(Decision Tree)

    第3章 决策树 <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/ ...

  10. [tmux] Copy and paste text from a tmux session

    One non-obvious, but extremely useful, feature in tmux is copy-pasting text between panes. This also ...