效果

概述

最近有个小需求要用双滑块表示一个取值范围,于是就简单做了个用户控件,在此记录下.

使用矩形Rectangle表示范围,椭圆Ellipse表示滑块,使用Canvas控制滑块的左右移动.

椭圆的鼠标按下事件里强制获取鼠标事件焦点,避免移动过快或移出控件范围时,滑块就不跟着跑了.椭圆的鼠标抬起事件释放强制获取鼠标事件焦点

代码部分

需求比较简单,只定义了4个依赖属性,范围的最大值和最小值,取值的最大值和最小值.

接下来就是计算滑块和高亮矩形的位置,计算时注意减去椭圆Ellipse控件(圆)的半径,使圆心对准值,而不是左侧对准值.

鼠标移动的时候,计算当前位置对应的值,去改变依赖属性的值:

 1         private void ell_MouseMove(object sender, MouseEventArgs e)
2 {
3 if (e.LeftButton == MouseButtonState.Pressed)
4 {
5 double percentage = e.GetPosition(rect).X / rect.ActualWidth;
6 double value = (Maximum - Minimum) * percentage + Minimum;
7 if (_ellFromPressed)
8 {
9 MinValue = (int)value;
10
11 }
12 if (_ellToPressed)
13 {
14 MaxValue = (int)value;
15 }
16 }
17 }

依赖属性的值变化,引起滑块的位置变化和高亮滑块宽度和位置的变化:

 1         //最小值变化
2 private void MinValuePropertyChanged()
3 {
4 if (MinValue < Minimum)
5 {
6 MinValue = Minimum;
7 return;
8 }
9 if (MinValue > MaxValue)
10 {
11 MaxValue = MinValue;
12 }
13 //滑块位置变化
14 _offsetFrom = (MinValue - Minimum) * rect.ActualWidth / (Maximum - Minimum) - 8;
15 Canvas.SetLeft(ellFrom, _offsetFrom);
16 //高亮矩形长度和位置的变化
17 if (_offsetTo != -1)
18 {
19 double diff = _offsetTo - _offsetFrom;
20 if (diff >= 0)
21 {
22 rectHighLight.Width = diff;
23 Canvas.SetLeft(rectHighLight, _offsetFrom);
24 }
25 }
26 }
27
28 //最大值变化
29 private void MaxValuePropertyChanged()
30 {
31 if (MaxValue > Maximum)
32 {
33 MaxValue = Maximum;
34 return;
35 }
36 if (MaxValue < MinValue)
37 {
38 MinValue = MaxValue;
39 }
40 //滑块位置变化
41 _offsetTo = (MaxValue - Minimum) * rect.ActualWidth / (Maximum - Minimum) - 8;
42 Canvas.SetLeft(ellTo, _offsetTo);
43 //高亮矩形长度和位置的变化
44 if (_offsetFrom != -1)
45 {
46 double diff = _offsetTo - _offsetFrom;
47 if (diff >= 0)
48 {
49 rectHighLight.Width = diff;
50 Canvas.SetLeft(rectHighLight, _offsetFrom);
51 }
52 }
53 }

取值的TextBox没有封装在用户控件里,是单独的两个TextBox跟依赖属性双向绑定的.注意绑定的时候触发方式最好不要用PropertyChanged,没有防抖效果,不然体验不是很好.回车触发就可以了.

前台双向绑定:

1         <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
2 <local:DoubleThumbSlider x:Name="dts" Width="500" Minimum="100" Maximum="200"></local:DoubleThumbSlider>
3 <TextBox Name="tMin" Width="48" Text="{Binding ElementName=dts,Path=MinValue,UpdateSourceTrigger=Explicit,Mode=TwoWay}" PreviewKeyUp="tbox_PreviewKeyUp"></TextBox>
4 <TextBlock Margin="5,0" Text="-"></TextBlock>
5 <TextBox Name="tMax" Width="48" Text="{Binding ElementName=dts,Path=MaxValue,UpdateSourceTrigger=Explicit,Mode=TwoWay}" PreviewKeyUp="tbox_PreviewKeyUp"></TextBox>
6 </StackPanel>

后台回车触发:

1         private void tbox_PreviewKeyUp(object sender, KeyEventArgs e)
2 {
3 if (e.Key == Key.Enter)
4 {
5 TextBox tbox = (TextBox)sender;
6 var binding = tbox.GetBindingExpression(TextBox.TextProperty);
7 binding.UpdateSource();
8 }
9 }

源码下载:DoubleThumbSlider.zip

参考连接:

1.https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.uielement.capturemouse?view=windowsdesktop-6.0

WPF双滑块控件以及强制捕获鼠标事件焦点的更多相关文章

  1. 【WPF】监听WPF的WebBrowser控件弹出新窗口的事件

    原文:[WPF]监听WPF的WebBrowser控件弹出新窗口的事件 WPF中自带一个WebBrowser控件,当我们使用它打开一个网页,例如百度,然后点击它其中的链接时,如果这个链接是会弹出一个新窗 ...

  2. WPF,强制捕获鼠标事件,鼠标移出控件外依然可以执行强制捕获的鼠标事件

    在WPF中,只有鼠标位置在某个控件上的时候才会触发该控件的鼠标事件.例如,有两个控件都注册了MouseDown和MouseUp事件,在控件1上按下鼠标,不要放开,移动到控件2上再放开.在这个过程中,控 ...

  3. WPF滑块控件(Slider)的自定义样式

    前言 每次开发滑块控件的样式都要花很久去读样式代码,感觉有点记不牢,所以特此备忘. 自定义滑块样式 首先创建项目,添加Slider控件. 然后获取Slider的Window样式,如下图操作. 然后弹出 ...

  4. 深入理解MVC C#+HtmlAgilityPack+Dapper走一波爬虫 StackExchange.Redis 二次封装 C# WPF 用MediaElement控件实现视频循环播放 net 异步与同步

    深入理解MVC   MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性 ...

  5. 抛砖引玉 【镜像控件】 WPF实现毛玻璃控件不要太简单

    原文:抛砖引玉 [镜像控件] WPF实现毛玻璃控件不要太简单 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Vblegend_2013/articl ...

  6. WPF范围选择控件(RangeSelector)

    原文:WPF范围选择控件(RangeSelector) 版权声明:本文为博主原创文章,转载请注明作者和出处 https://blog.csdn.net/ZZZWWWPPP11199988899/art ...

  7. WPF中Ribbon控件的使用

    这篇博客将分享如何在WPF程序中使用Ribbon控件.Ribbon可以很大的提高软件的便捷性. 上面截图使Outlook 2010的界面,在Home标签页中,将所属的Menu都平铺的布局,非常容易的可 ...

  8. WPF 调用WinForm控件

    WPF可以使用WindowsFormsHost控件做为容器去显示WinForm控件,类似的用法网上到处都是,就是拖一个WindowsFormsHost控件winHost1到WPF页面上,让后设置win ...

  9. InteropBitmap指定内存,绑定WPF的Imag控件时刷新问题。

    1.InteropBitmap指定内存,绑定WPF的Imag控件的Source属性 创建InteropBitmap的时候,像素的格式必须为PixelFormats.Bgr32, 如果不是的话在绑定到I ...

随机推荐

  1. jsp第三次作业

    1.在jsp页面中使用include动态标记加载音频 <%@ page language="java" import="java.util.*" page ...

  2. transition 动画过渡

    1. html 结构 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  3. nginx服务优化大全

    第18章 nginx服务优化 18.1 复习以前的nginx知识 18.1.1 复习nginx编译安装的3部曲 ./configure        配置(开启/关闭功能),指定安装目录 make   ...

  4. 2022最新IntellJ IDEA的zheng开发部署文档

    目录 前景提示 一.环境整合 构建工具(参考工具部署方式) 二.git 导入编译器 三.模块描述浅析 四.配置文档 1.总配置 2.数据库配置 3.密码设置 4.配置建议 五.在IDEA中执行MySQ ...

  5. rocketmq消息及流程

    1.为什么用mq 优势 主要有3个: 应用解耦(降低微服务之间的关联). 异步提速(微服务拿到mq消息后同时工作). 削峰填谷(可以消息堆积) 劣势 系统可用性降低(MQ一旦宕机整个系统不可用) 复杂 ...

  6. 聊聊Lock接口的lock()和lockInterruptible()有什么区别?

    lock()和lockInterruptible()都表示获取锁,唯一区别是,当A线程调用lock()或lockInterruptible()方法获取锁没有成功而进入等待锁的状态时,若接着调用该A线程 ...

  7. 动态SQL常用标签

    动态 SQL 目的:为了摆脱在不同条件拼接 SQL 语句的痛苦 在不同条件在生成不同的SQL语句 本质上仍然是SQL语句,不过是多了逻辑代码去拼接SQL,只要保证SQL的正确性按照格式去排列组合 可以 ...

  8. MySQL 高频面试题,都在这了

    点击上方"开源Linux",选择"设为星标"回复"学习"获取独家整理的学习资料! 前言 本文主要受众为开发人员,所以不涉及到MySQL的服务 ...

  9. 2020年DevOps工程师入门指南

    DevOps兴起于2010年代,到现在DevOps已经在行业中拥有了一席之地,并在继续发展壮大. 有兴趣成为一名DevOps工程师吗?如果想要成为一名DevOps工程师,需要做到以下五点: 要有开发者 ...

  10. 百度3D离线地图开发,3D离线地图开发,百度地图离线开发

    3D离线地图介绍(3D离线采用矢量数据作为地图基础,可保持地图数据最新) 一.开发中引用3D离线地图(可独立部署通过内外IP+端口进行访问,也可拷贝js库文件到项目中通过绝对路径访问) 1).离线AP ...