数值范围选择控件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的更多相关文章
- 通用数据水平层级选择控件v0.70升级版使其支持jQuery v1.9.1
升级原因:作者原来脚本支持的jquery版本太低了,查找了下资料,使得它能支持最新版本的jquery 备注说明:脚本代码源作者跟源文出处很难找,只能在此特感谢他的分享. 更新部分: 1.新版本不再支持 ...
- Windows Phone 8.1 新特性 - 控件之列表选择控件
本篇我们来介绍Windows Phone 8.1 新特性中的列表选择控件. 在Windows Phone 8 时代,大家都会使用 LongListSelector 来实现列表选择控件,对数据进行分组显 ...
- SNF开发平台WinForm之三-开发-单表选择控件创建-SNF快速开发平台3.3-Spring.Net.Framework
3.1运行效果: 3.2开发实现: 3.2.1 这个开发与第一个开发操作步骤是一致的,不同之处就是在生成完代码之后,留下如下圈红程序,其它删除. 第一个开发地址:开发-单表表格编辑管理页面 http: ...
- 用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)
本文为原创,如有转载,请注明出处:http://www.cnblogs.com/jackybu 前言 章节: 1.需求描述以及c/c++实现日期和月历的基本操作 2.ios实现自绘日期选择控件 3.a ...
- js 跨域的问题 (同一个主域名不同的二级域名下的跨域问题) 解决 WdatePicker.js my97日期选择控件
例如域名是 a.xx.com 和 b.xx.com 如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain. 如果iframe的时候 a包含b 为 ...
- JQuery好用的日期选择控件 DatePicker
近期发现一个很好的基于JQ的前端UI日期选择控件Jquery.DatePicker.js 下载地址:jquery.DatePIcker.js 演示地址:DatePicker - 基于jQuery 1. ...
- [Ext JS 4] 实战之 带week(星期)的日期选择控件(三)
前言 在 [Ext JS 4] 实战之 带week(星期)的日期选择控件(二) 的最后,有提到一个解决方案. 不过这方案有一个条件 ==> “2. 每年的周数从(1-52), 如果超过52 周 ...
- 使用sui实现的选择控件【性别、日期、省市级联】
使用sui mobile做的选择控件,其中sm.js有修改,增加自定义api,详情请看index.html的注释,不多说了,上代码 <!DOCTYPE html> <html> ...
- android中选择控件与选择界面自然过度效果的实现--一种新的交互设计
转载请标明出处: http://blog.csdn.net/jianghejie123/article/details/40648931 在安卓中经常遇到须要选择一个东西的功能,比方选择日期.选择文件 ...
随机推荐
- .dmp文件导出使用示例
exp导出的几种用例,先睹为快: 1 将数据库SampleDB完全导出,用户名system 密码manager 导出到E:/SampleDB.dmp中 exp system/manager@TestD ...
- 动态布局Cell的高度
1 自定义Cell, 在Cell的构造方法里面添加好所有的子控件 2 3 2 在HeightForRowAtIndexPath方法中返回每一行Cell对应的高度 4 5 3 在Cell的layoutS ...
- POJ 1952 BUY LOW, BUY LOWER DP记录数据
最长递减子序列.加记录有多少个最长递减子序列.然后须要去重. 最麻烦的就是去重了. 主要的思路就是:全面出现反复的值,然后还是同样长度的子序列.这里的DP记录的子序列是以当前值为结尾的时候,而且一定选 ...
- JDK8 直接定义接口中静态方法
JDK8前,接口只能是抽象方法. 但是在JDK8中,静态方法是可以直接定义方法体,可以直接用接口名调用.实现类和实现是不可以调用的 一.直接调用接口的静态方法 二.实现接口的子类来调用接口的静态方法 ...
- 【42.38%】【BZOJ 3196】二逼平衡树
Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1363 Solved: 579 [Submit][Status][Discuss] Descripti ...
- Finder那点事
事件是这样,我MAC PRO,关不了机了,是有什么线程在用 defaults write com.apple.Finder QuitMenuItem 1 这个命令是让Finder 有退出BTN ,co ...
- Android菜鸟的成长笔记(27)——SurfaceView的使用
前面有关自定义View中进行了绘图,但View的绘图机制存在如下缺陷: 1.View缺乏双缓冲机制. 2.当程序需要更新View上的图像时,程序必须重绘View上显示的整张图片. 3.新线程无法直接更 ...
- Java 开源博客——B3log Solo 0.6.6 正式版发布了!
Java 开源博客 -- B3log Solo 0.6.6 正式版发布了!欢迎大家下载. 该版本引入了数据库连接池:Druid. 另外,欢迎观摩 B3log 团队的新项目:Noty,也非常欢迎大家参与 ...
- JavaScript 正則表達式
一.简单介绍 1.什么是正則表達式 正則表達式本身就是一种语言,这在其他语言是通用的. 正則表達式(regular expression)描写叙述了一种字符串匹配的模式,能够用来检查一个串是否含有某种 ...
- python property属性
能够检查參数,一直没注意这个语言特性,忽略了非常多细节,感谢 vitrox class Person( object ): def __init__( self, name ): if not isi ...