桌面程序的解锁方式一般是账号密码,互联网的可以使用扫码解锁,甚至人脸识别。但扫码需要网络,人脸识别又较复杂。所以就想把安卓常用的手势解锁移植到桌面程序上。

先来张效果图,有兴趣的往下看,没兴趣的打扰了。

WPF实现手势解锁-LMLPHP

 WPF手势解锁使用鼠标点击事件,鼠标移动事件,鼠标弹起事件实现。自定义了三个属性(初始化颜色,选中颜色,选中点的集合),一个事件(绘制完成后触发的事件)。

实现的功能:

  绘制过程中直线随鼠标移动的效果

  绘制两个连接点的连线

  绘制完成后可调用的事件

  实现初始化颜色,选中颜色,选择连接点依赖属性

源码主要说明:

1.构造函数,完成事件注册

WPF实现手势解锁-LMLPHPWPF实现手势解锁-LMLPHP
 1 /// <summary>
 2 /// 构造函数
 3 /// </summary>
 4 public ScreenUnlock()
 5 {
 6 InitializeComponent();
 7 Points = new List<int>();
 8 this.Loaded += ScreenUnlock_Loaded;
 9 this.MouseDown += ScreenUnlock_MouseDown;
10 this.MouseUp += ScreenUnlock_MouseUp;
11 this.MouseMove += ScreenUnlock_MouseMove;
12 }
View Code

2.窗体加载事件

绘制九宫格,tag用动态类型保存了实际位置(Point)和序号(Loaction)

WPF实现手势解锁-LMLPHPWPF实现手势解锁-LMLPHP
 1 /// <summary>
 2 /// Load事件,绘制九宫格
 3 /// </summary>
 4 /// <param name="sender"></param>
 5 /// <param name="e"></param>
 6 private void ScreenUnlock_Loaded(object sender, RoutedEventArgs e)
 7 {
 8 canvas.Children.Clear();
 9 //为了保证正方形
10 var distance = Math.Min(this.ActualWidth == 0 ? this.Width : this.ActualWidth, this.ActualHeight == 0 ? this.Height : this.ActualHeight) / 3;
11 double left = (distance - PointSize) / 2;
12 for (var i = 0; i < 3; i++)
13 {
14 for (var j = 0; j < 3; j++)
15 {
16 var x = j * distance + left;
17 var y = i * distance + left;
18 Ellipse ellipse = new Ellipse()
19 {
20 Width = PointSize,
21 Height = PointSize,
22 Fill = Color,
23 Tag = new
24 {
25 Point = new Point(x + PointSize / 2, y + PointSize / 2),
26 Location = i * 3 + j + 1
27 }
28 };
29 ellipse.SetValue(Canvas.LeftProperty, x);
30 ellipse.SetValue(Canvas.TopProperty, y);
31 Canvas.SetLeft(ellipse, x);
32 Canvas.SetTop(ellipse, y);
33 canvas.Children.Add(ellipse);
34 }
35 }
36 }
View Code

3.鼠标左键点击事件

3.1清空了除九宫格之外所有元素

3.2判断点击位置是否是圆点位置,如果不是则不处理,否则记录当前位置用于画线,一条是跟踪鼠标的线(currentLine),另一个是为了显示选中的圆点的连线,此处记录绘制线的第一个点(currentEllipse与后续经过的点的连线).

WPF实现手势解锁-LMLPHPWPF实现手势解锁-LMLPHP
 1 private void ScreenUnlock_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
 2 {
 3 if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
 4 {
 5 //每次点击都是重新绘制,清空除了九宫格的所有元素
 6 while (canvas.Children.Count > 9)
 7 canvas.Children.RemoveAt(canvas.Children.Count - 1);
 8 ellipseList.Clear();
 9 currentEllipse = null;
10 Points.Clear();
11
12 //再次点击时需要先把颜色修改为初始化颜色
13 foreach (Shape item in canvas.Children)
14 item.Fill = Color;
15
16 //获取当前鼠标位置
17 var point = e.GetPosition(this);
18 //鼠标所在位置是否有圆点
19 if (VisualTreeHelper.HitTest(this, point).VisualHit is Ellipse ellipse) //鼠标经过圆点
20 {
21 currentEllipse = ellipse;
22 ellipseList.Add(ellipse);
23 Points.Add((int)((dynamic)ellipse.Tag).Location);
24 var p = (Point)((dynamic)currentEllipse.Tag).Point;
25 currentLine = new Line()
26 {
27 Stroke = Color,
28 StrokeThickness = PointSize / 2,
29 X1 = p.X,
30 Y1 = p.Y,
31 X2 = p.X,
32 Y2 = p.Y
33 };
34 }
35 }
36 }
View Code

4.鼠标移动事件

4.1绘制跟随鼠标的线

4.2判断是否经过之前没经过的圆点,绘线过程中,一个点只能用一次。经过的点保存在ellipseList集合中。

4.3如果经过未曾经过的点,则与上个经过的点绘制直线,并且重新赋值当前点currentEllipse,重新定义跟随鼠标的线(currentLine)的起始点为该点。

4.4把点添加到Points集合中

WPF实现手势解锁-LMLPHPWPF实现手势解锁-LMLPHP
 1 /// <summary>
 2 /// 鼠标移动
 3 /// </summary>
 4 /// <param name="sender"></param>
 5 /// <param name="e"></param>
 6 private void ScreenUnlock_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
 7 {
 8 //鼠标左键处于点击状态
 9 if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
10 {
11 //获取当前鼠标位置
12 var point = e.GetPosition(this);
13
14 ///当没有遇到圆点之前绘制跟随鼠标的线
15 if (currentLine != null)
16 {
17 canvas.Children.Remove(currentLine);
18 currentLine.X2 = point.X;
19 currentLine.Y2 = point.Y;
20 canvas.Children.Add(currentLine);
21 }
22
23 //线跟着移动
24 if (VisualTreeHelper.HitTest(this, point).VisualHit is Ellipse ellipse && currentEllipse != null)
25 {
26 var p1 = (Point)((dynamic)currentEllipse.Tag).Point;
27 var p = (Point)((dynamic)ellipse.Tag).Point;
28 if (p1 != p) //鼠标经过圆点
29 {
30 //如果不包含该圆点,一个点只能用一次
31 if (!ellipseList.Contains(ellipse))
32 {
33 //绘制当前点和上个点之间的连线
34 var t = new Line()
35 {
36 Stroke = Color,
37 StrokeThickness = PointSize / 2,
38 X1 = p1.X,
39 Y1 = p1.Y,
40 X2 = p.X,
41 Y2 = p.Y
42 };
43 //修改当前点
44 currentEllipse = ellipse;
45 ellipseList.Add(ellipse);
46 canvas.Children.Add(t);
47 Points.Add((int)((dynamic)ellipse.Tag).Location);
48 if (currentLine != null)
49 {
50 canvas.Children.Remove(currentLine);
51 currentLine.X1 = p.X;
52 currentLine.Y1 = p.Y;
53 currentLine.X2 = p.X;
54 currentLine.Y2 = p.Y;
55 canvas.Children.Add(currentLine);
56 }
57 }
58 }
59 }
60 }
61 }
View Code

5.鼠标左键弹起事件

5.1鼠标弹起时,修改所有经过的点以及点之间的连线颜色。

5.2清空所有使用的临时的变量,currentEllipse,currentLine

5.3触发绘制后触发的事件(AfterDraw)

WPF实现手势解锁-LMLPHPWPF实现手势解锁-LMLPHP
 1 /// <summary>
 2 /// 鼠标左键弹起
 3 /// </summary>
 4 /// <param name="sender"></param>
 5 /// <param name="e"></param>
 6 private void ScreenUnlock_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
 7 {
 8 if (canvas.Children.Count > 9)
 9 foreach (Shape item in canvas.Children)
10 if (item is Line)
11 item.Stroke = SelectedColor;
12 else if (item is Ellipse ellipse && ellipseList.Contains(ellipse))
13 item.Fill = SelectedColor;
14 currentEllipse = null;
15 currentLine = null;
16 canvas.Children.Remove(currentLine);
17 RaiseEvent(new RoutedEventArgs(AfterDrawEvent));
18 }
View Code

6.选中点和线的颜色,SelectedColor属性

设置该颜色时,需要同时修改选中的点和线的颜色。

WPF实现手势解锁-LMLPHPWPF实现手势解锁-LMLPHP
 1 /// <summary>
 2 /// 选中的颜色
 3 /// </summary>
 4 public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor", typeof(SolidColorBrush), typeof(ScreenUnlock), new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Green), new PropertyChangedCallback((s, e) =>
 5 {
 6 var t = s as ScreenUnlock;
 7 if (t.canvas.Children.Count > 9)
 8 for (int i = 9; i < t.canvas.Children.Count; i++)
 9 {
10 Shape item = t.canvas.Children[i] as Shape;
11 if (item is Line)
12 item.Stroke = e.NewValue as SolidColorBrush;
13 else if (item is Ellipse ellipse)
14 item.Fill = e.NewValue as SolidColorBrush;
15 }
16 })));
17
18 /// <summary>
19 /// 选中的颜色
20 /// </summary>
21 public SolidColorBrush SelectedColor
22 {
23 get { return GetValue(SelectedColorProperty) as SolidColorBrush; }
24 set { SetValue(SelectedColorProperty, value); }
25 }
View Code

7.绘制用的点的集合Points

绑定Points属性,后台就可以获取到绘制图案经历的点的集合。

8.其他代码

其他包含初始颜色,绘制完成以后触发的事件,以及使用到的变量。见源码。

9.利用该控件实现解锁

9.1绑定三个属性一个事件,分别是初始化颜色(Color),选中的颜色(SelectedColor),经过的点(Points)

9.2SelectedColor绑定方式

需要一个转换器(验证正确与否与颜色的转换),如果验证正确,则显示绿色,否则显示红色。

9.3如果连接的点太少时,则需进行提示,并且恢复原来的状态(即把选中的颜色设置为初始化的颜色)

参考:

https://www.cnblogs.com/ShenNan/p/5587009.html

源码:

没找到上传附件,附上码云地址。

https://gitee.com/yiyecao/temporary-components

09-08 00:10