通用多滑块





5.00/5 (9投票s)
功能全面的多滑块(范围),可以添加、删除和移动箭头(滑块柄)。
(Visual Studio 2017)
注意:仅需进行少量修改即可转换为早期版本。
引言
顾名思义,MultiSlider
是一个 UserControl
,能够在一个滑轨控件上显示多个箭头。我保留了大部分标准 Slider
控件的行为和外观,并添加了一些小的增强功能。
- 用户可以通过双击滑轨来添加新箭头(可选)。
- 用户可以通过 Alt+右键单击来删除箭头(可选)。
- 箭头具有
IsMoveable
属性;如果设置为false
,则箭头既不能移动也不能删除(在视觉上用箭头上的彩色装饰圆圈表示)。 - 箭头具有
IsUser
属性;如果设置为true
,则用于为普通箭头着色的画笔将有所不同。用户箭头和标准箭头在其他方面处理方式相同。 - 支持
AutoToolTipPlacement
。
注释
使用 Tab 键或 Modifier+光标键在主窗口控件之间进行 Tab 切换时
- 如果选中了一个箭头,可以使用 Shift+光标键在任何相邻的箭头之间进行 Tab 切换。
- Tab 键不会在任何相邻的箭头之间切换,只会切换到控件。
- 当 Tab 键切换到
MultiSlider
控件时,按下空格键将激活先前选中的箭头。 - Control+光标键在 Tab 切换控件时按正常方式工作。
市面上有一些多滑块工具
- https://codeproject.org.cn/Articles/626132/WPF-MultiRangeSlider-Control
此文章发布于 2013 年,使用的是 Windows 7 箭头样式,因此不适用于 W10 风格。功能有限,但可以添加新箭头。
- http://avaloncontrolslib.codeplex.com/
这个更老(2007 年)。
- http://www.telerik.com/products/wpf/slider.aspx?utm_source=google&utm_medium=cpc&gclid=CjwKEAjwl9DIBRCG_e3DwsKsizsSJADMmJ11IH1yTbk4LCKPBdn0Q9rsFBB0G-P1IpyNywq5hAi4_BoCpYfw_wcB&gclsrc=aw.ds&dclid=CKqYk9fy59MCFcKg7QodaLcK5g
看起来是为 W7 设计的。我没有深入研究过这个。
- http://wpftoolkit.codeplex.com/wikipage?title=RangeSlider&referringTitle=Home
这个只固定为 2 个箭头,不够灵活,但对于其功能来说看起来相当不错。
它们都不够全面以满足我的需求,所以我自己开发了一个。
- 为程序员提供的事件处理功能已全面考虑(见下文规格)。
- 我没有包含“刻度”图形和功能(可能以后会添加),但支持 AutoToolTip 功能。
用法
解决方案中有两个项目将演示 MultiSlider
的实际应用。第一个项目 TestSimple
仅显示所有箭头类型、覆盖颜色用法和基本功能。第二个项目 TestMultiSlider
更为全面,包含:
- 供用户以各种方式操作箭头的设施
- 用于更改多滑块状态的开关
- 一个垂直和一个水平方向的多滑块
- 在
TextBlock
中显示用户操作的读出
属性
AdornerColor
- (get
/set
) - 用于箭头装饰圆圈的颜色。如果传递null
,则颜色将被重置。AdornerRatio
- (get
/set
) - 设置/获取装饰圆圈直径与向上箭头宽度之比。如果传递null
,则比率将被重置。ArrowAtIndex(int index)
- 返回位置“index
”(base=0
,从最小值端开始)的ArrowData
。ArrowCount
- (get
) - 滑轨中的箭头数量。ArrowType
- (get
/set
) -MultiSlider.ArrowTypes enum
。值:LeftRight
、UpDown
或Rect
。AutoToolTipPlacement
- (get
/set
) - 自动工具提示相对于箭头的位置。AutoToolTipPrecision
- (get
/set
) - 将箭头的 Value 打印为 N 位小数。AutoToolTipSigFigs
- (get
/set
) - 如果N > 0
,则将箭头的 Value 打印为 N 位有效数字,并且优先级高于AutoToolTipPrecision
。CanDelAddArrows
- (get
/set
) - 用户是否可以使用鼠标添加/删除箭头。HelpText
- (get
) - 一个string
,包含基本的格式化文本,为用户提供说明。适合添加到MessageBox
中显示。最低
- (g
et
/set
) - 滑轨中箭头的最小值。设置器将删除所有现有箭头,并且不会触发ArrowDeleted
事件。Maximum
- (get
/set
) - 滑轨中箭头的最大值。设置器将删除所有现有箭头,并且不会触发ArrowDeleted
事件。Orientation
- (get
/set
) - 指定滑轨的垂直或水平轴。ReverseDirection
- (get
/set
) -Maximum
是否位于滑轨的最大端或最小端(反之亦然Minimum
)。SchemeOverlayColor
- (get
,set
) - 用于滑轨和普通箭头之上的覆盖颜色(建议 alpha=10%)。SmallChange
- (get
/set
) - 使用键盘光标键移动时,添加到当前箭头值或从中减去的小增量 Value。LargeChange
- (get
/set
) - 使用键盘光标键(+Alt 键)移动时,添加到当前箭头值或从中减去的大增量 Value。Value
- (get
/set
) - 滑轨上第一个箭头的 Value。如果滑轨为空,则得到DoubleNaN
;或者在设置时忽略滑轨。这对于不可删除的单箭头滑块很有用。
方法
CreateArrow(double value, bool isUser = false, bool isMoveable = true)
- 创建并显示一个新箭头。返回一个ArrowData
。- "
value
">TrackBar
的Minimum
和Maximum
属性之间的值。 - "
isUser
">true
:使用箭头的 User 状态画笔。 - "
isMoveable
">false
:箭头既不能移动也不能删除。
- "
DeleteArrow(int index, bool fireEvent)
- 从滑轨画布中删除索引为“index
”的箭头(base=0
,从最小值端开始)。如果'fireEvent' = true
,则触发ArrowDeleted
事件。DeleteAllArrows(bool fireEvent)
- 从滑轨画布中删除所有箭头。如果'fireEvent' = true
,则触发ArrowDeleted
事件。IsValidValue(value)
- 参数是否落在多滑块的Minimum
/Maximum
值范围内。ValueAtIndex(int index)
- 返回滑轨上有效箭头索引(base=0
,从最小值端开始)的值。
对于箭头(由事件处理程序发送的 ArrowData
类型)
Index
- (get
) - Base 0。滑轨上从最小值端开始的第 n 个箭头。IsMoveable
- (get
/set
) - 箭头是否可以移动/删除。可以动态更改,例如在下面的事件处理程序中。IsUser
- (get
/set
) - 仅当设置“普通”箭头画笔时使用的画笔不同。可以动态更改,例如在下面的事件处理程序中。Value
- (get
/set
) - {Minimum
到Maximum
} 之间的值。
事件
ArrowCreated
ArrowRightClicked
- 如果使用 Alt 键删除箭头,则此事件不会触发。ArrowScrolled
ArrowDeleted
- 如果焦点更改为相邻箭头,ArrowSelected
也会在之后触发。ArrowLeftClicked
ArrowSelected
- 当箭头获得键盘焦点时触发(例如,被单击或 Tab 键切换到)。所有发送者arg
都作为(object)ArrowData
传递。
实现说明
如果 Minimum==Maximum => 行为
有点奇怪但可接受。
在 MultiSlider
属性窗口中有一个条件编译符号 MULTISLIDER
;在调试版本不在调试器外部运行时,更改此符号以阻止控制台输出。
MultiSlider 控件的构建
控件首先有一个 Canvas
,代表滑轨(作为 Border
),其大小设置为用户控件的大小。然后在此处添加一个 trackbar Border
控件。此 Border
的大小相对于 MultiSlider
的 width
或 height
属性设置。然后,将已创建和初始化的复合箭头添加到主 trackbar Canvas
。
箭头由一个 Canvas
组成,该 Canvas
被添加到主 trackbar
;在此 Canvas
中,首先添加一个初始化的 Polygon
来描述箭头,然后添加一个 Ellipse
(充当装饰器),最后添加一个 Label
(显示 AutoToolTip
文本)。每当需要更改箭头的视觉位置时,都是将 Canvas
相对于主 trackbar Canvas
进行放置;因此,绝不会访问或更改 Polygon
的点。
使用 Shift+光标键进行箭头键盘焦点
在方法 MultiSlider.Arrow_KeyDown()
中实现了使用 Shift+光标键在控件内的箭头之间进行 Tab 切换。
通过在滑轨上按住鼠标左键进行箭头移动
所有这些都在 trackbar
的 MouseLeftButtonDown
/Up
处理程序中完成。在 TrackBar_MouseLeftButtonDown()
中。谓词 if(Mouse.LeftButton == MouseButtonState.Released)
不能在循环中使用,因为当鼠标按钮释放时,报告的 Mouse
按钮状态保持不变。唯一有效的方法是以下方法:
- 部署一个
iVar 标志 _mouseUp
。 - 在
TrackBar_MouseLeftButtonDown()
中将_mouseUp
设置为false
。 - 编写事件处理程序
TrackBar_MouseLeftButtonUp()
- 将_mouseUp
设置为true
。
处理连续按住鼠标按钮
编写一个 BackgroundWorker.DoWork
事件处理程序 ProcessHoldingMouseDown()
,并设置 args 为 TrackBar_MouseLeftButtonDown()
传递的所需类型;这将进行鼠标按钮状态检测并执行箭头移动操作。
在 TrackBar_MouseLeftButtonDown()
中,在要执行箭头移动的地方编写以下代码
BackgroundWorker threadBW = new BackgroundWorker();
threadBW.DoWork += (obj, e2) => ProcessHoldingMouseDown(mouseCoords, arrow);
threadBW.RunWorkerAsync();
在 ProcessHoldingMouseDown()
中,GUI 主线程调用器部署了多次,例如 Dispatcher.Invoke(() => TrackBar.InitBounds(arrow));
示意图连接
MultiSlider
的行为需要对内部事件处理程序进行相当精细的“调优”。自行修改,后果自负!
一个 Shape.Polygon
RenderBounds()
不考虑任何变换(例如,缩放、旋转)在该形状上。仅包含 StrokeThickness
等,因此当箭头的 Polygon
渲染时,它正好适合其包含的 Canvas
。