桌面程式的解鎖方式一般是賬號密碼,網際網路的可以使用掃碼解鎖,甚至人臉識別。但掃碼需要網路,人臉識別又較複雜。所以就想把安卓常用的手勢解鎖移植到桌面程式上。
先來張效果圖,有興趣的往下看,沒興趣的打擾了。
WPF手勢解鎖使用滑鼠點選事件,滑鼠移動事件,滑鼠彈起事件實現。自定義了三個屬性(初始化顏色,選中顏色,選中點的集合),一個事件(繪製完成後觸發的事件)。
實現的功能:
繪製過程中直線隨滑鼠移動的效果
繪製兩個連線點的連線
繪製完成後可呼叫的事件
實現初始化顏色,選中顏色,選擇連線點依賴屬性
原始碼主要說明:
1.建構函式,完成事件註冊
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 }
2.窗體載入事件
繪製九宮格,tag用動態型別儲存了實際位置(Point)和序號(Loaction)
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 }
3.滑鼠左鍵點選事件
3.1清空了除九宮格之外所有元素
3.2判斷點選位置是否是圓點位置,如果不是則不處理,否則記錄當前位置用於畫線,一條是跟蹤滑鼠的線(currentLine),另一個是為了顯示選中的圓點的連線,此處記錄繪製線的第一個點(currentEllipse與後續經過的點的連線).
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 }
4.滑鼠移動事件
4.1繪製跟隨滑鼠的線
4.2判斷是否經過之前沒經過的圓點,繪線過程中,一個點只能用一次。經過的點儲存在ellipseList集合中。
4.3如果經過未曾經過的點,則與上個經過的點繪製直線,並且重新賦值當前點currentEllipse,重新定義跟隨滑鼠的線(currentLine)的起始點為該點。
4.4把點新增到Points集合中
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 }
5.滑鼠左鍵彈起事件
5.1滑鼠彈起時,修改所有經過的點以及點之間的連線顏色。
5.2清空所有使用的臨時的變數,currentEllipse,currentLine
5.3觸發繪製後觸發的事件(AfterDraw)
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 }
6.選中點和線的顏色,SelectedColor屬性
設定該顏色時,需要同時修改選中的點和線的顏色。
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 }
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