WPF實現手勢解鎖

yiyecao發表於2020-09-07

桌面程式的解鎖方式一般是賬號密碼,網際網路的可以使用掃碼解鎖,甚至人臉識別。但掃碼需要網路,人臉識別又較複雜。所以就想把安卓常用的手勢解鎖移植到桌面程式上。

先來張效果圖,有興趣的往下看,沒興趣的打擾了。

 

 

 WPF手勢解鎖使用滑鼠點選事件,滑鼠移動事件,滑鼠彈起事件實現。自定義了三個屬性(初始化顏色,選中顏色,選中點的集合),一個事件(繪製完成後觸發的事件)。

實現的功能:

  繪製過程中直線隨滑鼠移動的效果

  繪製兩個連線點的連線

  繪製完成後可呼叫的事件

  實現初始化顏色,選中顏色,選擇連線點依賴屬性

原始碼主要說明:

1.建構函式,完成事件註冊

WPF實現手勢解鎖
 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實現手勢解鎖
 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實現手勢解鎖
 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實現手勢解鎖
 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實現手勢解鎖
 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實現手勢解鎖
 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

 

相關文章