search
HomeBackend DevelopmentC#.Net TutorialScreenUnLock-detailed explanation on the use of graphic unlock control

This article mainly introduces the use of WPF graphic unlocking control ScreenUnLock in detail. It has certain reference value. Interested friends can refer to

ScreenUnLock and pattern unlocking on smartphones. Functions are the same. The purpose of unlocking or memorizing graphics is achieved by drawing graphics.

I had a sudden idea to transplant the graphics unlocking function on my mobile phone to WPF. It has also been applied to the company's projects.

Before creating ScreenUnLock, let’s first analyze the implementation idea of ​​graphic unlocking.

1. Create the origin of the nine-square grid (or more grids), and define a coordinate value for each point

2. Provide extended attributes and events related to graphic unlocking to facilitate the definition of the caller. For example: color of points and lines (Color), operation mode (Check|Remember), correct color for verification (RightColor), color for failed verification (ErrorColor), unlock event OnCheckedPoint, memory event OnRememberPoint, etc.;

3. Define the MouseMove event to monitor the line drawing behavior. The line drawing part is also the core of this article. During the line drawing process. The program needs to determine from which point the line starts to be drawn and which point it passes through (excluding already recorded points). Whether the drawing is completed and so on.

4. Line drawing completion, the line drawing completion behavior is processed according to the operation mode. And call related custom events

The general idea is as above, let’s start writing ScreenUnLock step by step

Create ScreenUnLock


public partial class ScreenUnlock : UserControl

Define related properties


/// <summary>
  /// 验证正确的颜色
  /// </summary>
  private SolidColorBrush rightColor;

  /// <summary>
  /// 验证失败的颜色
  /// </summary>
  private SolidColorBrush errorColor;

  /// <summary>
  /// 图案是否在检查中
  /// </summary>
  private bool isChecking;

  public static readonly DependencyProperty PointArrayProperty = DependencyProperty.Register("PointArray", typeof(IList<string>), typeof(ScreenUnlock));
  /// <summary>
  /// 记忆的坐标点 
  /// </summary>
  public IList<string> PointArray
  {
   get { return GetValue(PointArrayProperty) as IList<string>; }
   set { SetValue(PointArrayProperty, value); }
  }

  /// <summary>
  /// 当前坐标点集合
  /// </summary>
  private IList<string> currentPointArray;

  /// <summary>
  /// 当前线集合
  /// </summary>
  private IList<Line> currentLineList;

  /// <summary>
  /// 点集合
  /// </summary>
  private IList<Ellipse> ellipseList;

  /// <summary>
  /// 当前正在绘制的线
  /// </summary>
  private Line currentLine;

  public static readonly DependencyProperty OperationPorperty = DependencyProperty.Register("Operation", typeof(ScreenUnLockOperationType), typeof(ScreenUnlock), new FrameworkPropertyMetadata(ScreenUnLockOperationType.Remember));
  /// <summary>
  /// 操作类型
  /// </summary>
  public ScreenUnLockOperationType Operation
  {
   get { return (ScreenUnLockOperationType)GetValue(OperationPorperty); }
   set { SetValue(OperationPorperty, value); }
  }

  public static readonly DependencyProperty PointSizeProperty = DependencyProperty.Register("PointSize", typeof(double), typeof(ScreenUnlock), new FrameworkPropertyMetadata(15.0));
  /// <summary>
  /// 坐标点大小 
  /// </summary>
  public double PointSize
  {
   get { return Convert.ToDouble(GetValue(PointSizeProperty)); }
   set { SetValue(PointSizeProperty, value); }
  }


  public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(SolidColorBrush), typeof(ScreenUnlock), new FrameworkPropertyMetadata(new SolidColorBrush(Colors.White), new PropertyChangedCallback((s, e) =>
  {
   (s as ScreenUnlock).Refresh();
  })));

  /// <summary>
  /// 坐标点及线条颜色
  /// </summary>
  public SolidColorBrush Color
  {
   get { return GetValue(ColorProperty) as SolidColorBrush; }
   set { SetValue(ColorProperty, value); }
  }

     /// <summary>
     /// 操作类型
     /// </summary>
     public enum ScreenUnLockOperationType
     {
      Remember = 0, Check = 1
     }

Initialize ScreenUnLock


public ScreenUnlock()
  {
   InitializeComponent();
   this.Loaded += ScreenUnlock_Loaded;
   this.Unloaded += ScreenUnlock_Unloaded;
   this.MouseMove += ScreenUnlock_MouseMove; //监听绘制事件
  }
 private void ScreenUnlock_Loaded(object sender, RoutedEventArgs e)
  {
   isChecking = false;
   rightColor = new SolidColorBrush(Colors.Green);
   errorColor = new SolidColorBrush(Colors.Red);
   currentPointArray = new List<string>();
   currentLineList = new List<Line>();
   ellipseList = new List<Ellipse>();
   CreatePoint();
  }


  private void ScreenUnlock_Unloaded(object sender, RoutedEventArgs e)
  {
   rightColor = null;
   errorColor = null;
   if (currentPointArray != null)
    this.currentPointArray.Clear();
   if (currentLineList != null)
    this.currentLineList.Clear();
   if (ellipseList != null)
    ellipseList.Clear();
   this.canvasRoot.Children.Clear();
  }

Create point


/// <summary>
  /// 创建点
  /// </summary>
  private void CreatePoint()
  {
   canvasRoot.Children.Clear();
   int row = 3, column = 3; //三行三列,九宫格
   double oneColumnWidth = (this.ActualWidth == 0 ? this.Width : this.ActualWidth) / 3; //单列的宽度
   double oneRowHeight = (this.ActualHeight == 0 ? this.Height : this.ActualHeight) / 3; //单列的高度
   double leftDistance = (oneColumnWidth - PointSize) / 2; //单列左边距
   double topDistance = (oneRowHeight - PointSize) / 2; //单列上边距
   for (var i = 0; i < row; i++)
   {
    for (var j = 0; j < column; j++)
    {
     Ellipse ellipse = new Ellipse()
     {
      Width = PointSize,
      Height = PointSize,
      Fill = Color,
      Tag = string.Format("{0}{1}", i, j)
     };
     Canvas.SetLeft(ellipse, j * oneColumnWidth + leftDistance);
     Canvas.SetTop(ellipse, i * oneRowHeight + topDistance);
     canvasRoot.Children.Add(ellipse);
     ellipseList.Add(ellipse);
    }
   }
  }

Create line


private Line CreateLine()
  {
   Line line = new Line()
   {
    Stroke = Color,
    StrokeThickness = 2
   };
   return line;
  }

The points and lines have been created and defined, and you can start listening for drawing events


private void ScreenUnlock_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
  {
   if (isChecking) //如果图形正在检查中,不响应后续处理
    return;
   if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
   {
    var point = e.GetPosition(this);
    HitTestResult result = VisualTreeHelper.HitTest(this, point);
    Ellipse ellipse = result.VisualHit as Ellipse;
    if (ellipse != null)
    {
     if (currentLine == null)
     {
      //从头开始绘制                  
      currentLine = CreateLine();
      var ellipseCenterPoint = GetCenterPoint(ellipse);
      currentLine.X1 = currentLine.X2 = ellipseCenterPoint.X;
      currentLine.Y1 = currentLine.Y2 = ellipseCenterPoint.Y;

      currentPointArray.Add(ellipse.Tag.ToString());
      Console.WriteLine(string.Join(",", currentPointArray));
      currentLineList.Add(currentLine);
      canvasRoot.Children.Add(currentLine);
     }
     else
     {
      //遇到下一个点,排除已经经过的点
      if (currentPointArray.Contains(ellipse.Tag.ToString()))
       return;
      OnAfterByPoint(ellipse);
     }
    }
    else if (currentLine != null)
    {
     //绘制过程中
     currentLine.X2 = point.X;
     currentLine.Y2 = point.Y;

     //判断当前Line是否经过点
     ellipse = IsOnLine();
     if (ellipse != null)
      OnAfterByPoint(ellipse);
    }
   }
   else
   {
    if (currentPointArray.Count == 0)
     return;
    isChecking = true;
    if (currentLineList.Count + 1 != currentPointArray.Count)
    {
     //最后一条线的终点不在点上
     //两点一线,点的个数-1等于线的条数
     currentLineList.Remove(currentLine); //从已记录的线集合中删除最后一条多余的线
     canvasRoot.Children.Remove(currentLine); //从界面上删除最后一条多余的线
     currentLine = null;
    }

    if (Operation == ScreenUnLockOperationType.Check)
    {
     Console.WriteLine("playAnimation Check");
     var result = CheckPoint(); //执行图形检查
              //执行完成动画并触发检查事件
     PlayAnimation(result, () =>
     {
      if (OnCheckedPoint != null)
      {
       this.Dispatcher.BeginInvoke(OnCheckedPoint, this, new CheckPointArgs() { Result = result }); //触发检查完成事件
      }
     });

    }
    else if (Operation == ScreenUnLockOperationType.Remember)
    {
     Console.WriteLine("playAnimation Remember");
     RememberPoint(); //记忆绘制的坐标
     var args = new RememberPointArgs() { PointArray = this.PointArray };
             //执行完成动画并触发记忆事件
     PlayAnimation(true, () =>
     {
      if (OnRememberPoint != null)
      {
       this.Dispatcher.BeginInvoke(OnRememberPoint, this, args); //触发图形记忆事件
      }
     });
    }
   }
  }

Judge whether the line passes through a nearby point


/// <summary>
  /// 两点计算一线的长度
  /// </summary>
  /// <param name="pt1"></param>
  /// <param name="pt2"></param>
  /// <returns></returns>
  private double GetLineLength(double x1, double y1, double x2, double y2)
  {
   return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); //根据两点计算线段长度公式 √((x1-x2)²x(y1-y2)²)
  }

  /// <summary>
  /// 判断线是否经过了某个点
  /// </summary>
  /// <param name="ellipse"></param>
  /// <returns></returns>
  private Ellipse IsOnLine()
  {
   double lineAB = 0; //当前画线的长度
   double lineCA = 0; //当前点和A点的距离 
   double lineCB = 0; //当前点和B点的距离
   double dis = 0;
   double deciation = 1; //允许的偏差距离
   lineAB = GetLineLength(currentLine.X1, currentLine.Y1, currentLine.X2, currentLine.Y2); //计算当前画线的长度

   foreach (Ellipse ellipse in ellipseList)
   {
    if (currentPointArray.Contains(ellipse.Tag.ToString())) //排除已经经过的点
     continue;
    var ellipseCenterPoint = GetCenterPoint(ellipse); //取当前点的中心点
    lineCA = GetLineLength(currentLine.X1, currentLine.Y1, ellipseCenterPoint.X, ellipseCenterPoint.Y); //计算当前点到线A端的长度
    lineCB = GetLineLength(currentLine.X2, currentLine.Y2, ellipseCenterPoint.X, ellipseCenterPoint.Y); //计算当前点到线B端的长度
    dis = Math.Abs(lineAB - (lineCA + lineCB)); //线CA的长度+线CB的长度>当前线AB的长度 说明点不在线上
    if (dis <= deciation) //因为绘制的点具有一个宽度和高度,所以需设定一个允许的偏差范围,让线靠近点就命中之(吸附效果)
    {
     return ellipse;
    }
   }
   return null;
  }

Check whether the points are correct and match them one by one in array order


/// <summary>
  /// 检查坐标点是否正确
  /// </summary>
  /// <returns></returns>
  private bool CheckPoint()
  { 
         //PointArray:正确的坐标值数组
         //currentPointArray:当前绘制的坐标值数组
   if (currentPointArray.Count != PointArray.Count)
    return false;
   for (var i = 0; i < currentPointArray.Count; i++)
   {
    if (currentPointArray[i] != PointArray[i])
     return false;
   }
   return true;
  }

Record passing points and create a new line


/// <summary>
  /// 记录经过的点
  /// </summary>
  /// <param name="ellipse"></param>
  private void OnAfterByPoint(Ellipse ellipse)
  {
   var ellipseCenterPoint = GetCenterPoint(ellipse);
   currentLine.X2 = ellipseCenterPoint.X;
   currentLine.Y2 = ellipseCenterPoint.Y;
   currentLine = CreateLine();
   currentLine.X1 = currentLine.X2 = ellipseCenterPoint.X;
   currentLine.Y1 = currentLine.Y2 = ellipseCenterPoint.Y;
   currentPointArray.Add(ellipse.Tag.ToString());
   Console.WriteLine(string.Join(",", currentPointArray));
   currentLineList.Add(currentLine);
   canvasRoot.Children.Add(currentLine);
  }


/// <summary>
  /// 获取原点的中心点坐标
  /// </summary>
  /// <param name="ellipse"></param>
  /// <returns></returns>
  private Point GetCenterPoint(Ellipse ellipse)
  {
   Point p = new Point(Canvas.GetLeft(ellipse) + ellipse.Width / 2, Canvas.GetTop(ellipse) + ellipse.Height / 2);
   return p;
  }

When the drawing is completed, the completion animation is executed and Events that trigger response mode


/// <summary>
  /// 执行动画
  /// </summary>
  /// <param name="result"></param>
  private void PlayAnimation(bool result, Action callback = null)
  {
   Task.Factory.StartNew(() =>
   {
    this.Dispatcher.Invoke((Action)delegate
    {
     foreach (Line l in currentLineList)
      l.Stroke = result ? rightColor : errorColor;
     foreach (Ellipse e in ellipseList)
      if (currentPointArray.Contains(e.Tag.ToString()))
       e.Fill = result ? rightColor : errorColor;
    });
    Thread.Sleep(1500);
    this.Dispatcher.Invoke((Action)delegate
    {
     foreach (Line l in currentLineList)
      this.canvasRoot.Children.Remove(l);
     foreach (Ellipse e in ellipseList)
      e.Fill = Color;
    });
    currentLine = null;
    this.currentPointArray.Clear();
    this.currentLineList.Clear();
    isChecking = false;
   }).ContinueWith(t =>
   {
    try
    {
     if (callback != null)
      callback();
    }
    catch (Exception ex)
    {
     Console.WriteLine(ex.Message);
    }
    finally
    {
     t.Dispose();
    }
   });
  }

Calls for graphics unlock


<local:ScreenUnlock Width="500" Height="500"
      PointArray="{Binding PointArray, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
      Operation="Check"> <!--或Remember-->
      <i:Interaction.Triggers>
       <i:EventTrigger EventName="OnCheckedPoint">
        <Custom:EventToCommand Command="{Binding OnCheckedPoint}" PassEventArgsToCommand="True"/>
       </i:EventTrigger>
       <i:EventTrigger EventName="OnRememberPoint">
        <Custom:EventToCommand Command="{Binding OnRememberPoint}" PassEventArgsToCommand="True"/>
       </i:EventTrigger>
      </i:Interaction.Triggers>
     </local:ScreenUnlock>

The above is the detailed content of ScreenUnLock-detailed explanation on the use of graphic unlock control. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
The Continued Relevance of C# .NET: A Look at Current UsageThe Continued Relevance of C# .NET: A Look at Current UsageApr 16, 2025 am 12:07 AM

C#.NET is still important because it provides powerful tools and libraries that support multiple application development. 1) C# combines .NET framework to make development efficient and convenient. 2) C#'s type safety and garbage collection mechanism enhance its advantages. 3) .NET provides a cross-platform running environment and rich APIs, improving development flexibility.

From Web to Desktop: The Versatility of C# .NETFrom Web to Desktop: The Versatility of C# .NETApr 15, 2025 am 12:07 AM

C#.NETisversatileforbothwebanddesktopdevelopment.1)Forweb,useASP.NETfordynamicapplications.2)Fordesktop,employWindowsFormsorWPFforrichinterfaces.3)UseXamarinforcross-platformdevelopment,enablingcodesharingacrossWindows,macOS,Linux,andmobiledevices.

C# .NET and the Future: Adapting to New TechnologiesC# .NET and the Future: Adapting to New TechnologiesApr 14, 2025 am 12:06 AM

C# and .NET adapt to the needs of emerging technologies through continuous updates and optimizations. 1) C# 9.0 and .NET5 introduce record type and performance optimization. 2) .NETCore enhances cloud native and containerized support. 3) ASP.NETCore integrates with modern web technologies. 4) ML.NET supports machine learning and artificial intelligence. 5) Asynchronous programming and best practices improve performance.

Is C# .NET Right for You? Evaluating its ApplicabilityIs C# .NET Right for You? Evaluating its ApplicabilityApr 13, 2025 am 12:03 AM

C#.NETissuitableforenterprise-levelapplicationswithintheMicrosoftecosystemduetoitsstrongtyping,richlibraries,androbustperformance.However,itmaynotbeidealforcross-platformdevelopmentorwhenrawspeediscritical,wherelanguageslikeRustorGomightbepreferable.

C# Code within .NET: Exploring the Programming ProcessC# Code within .NET: Exploring the Programming ProcessApr 12, 2025 am 12:02 AM

The programming process of C# in .NET includes the following steps: 1) writing C# code, 2) compiling into an intermediate language (IL), and 3) executing by the .NET runtime (CLR). The advantages of C# in .NET are its modern syntax, powerful type system and tight integration with the .NET framework, suitable for various development scenarios from desktop applications to web services.

C# .NET: Exploring Core Concepts and Programming FundamentalsC# .NET: Exploring Core Concepts and Programming FundamentalsApr 10, 2025 am 09:32 AM

C# is a modern, object-oriented programming language developed by Microsoft and as part of the .NET framework. 1.C# supports object-oriented programming (OOP), including encapsulation, inheritance and polymorphism. 2. Asynchronous programming in C# is implemented through async and await keywords to improve application responsiveness. 3. Use LINQ to process data collections concisely. 4. Common errors include null reference exceptions and index out-of-range exceptions. Debugging skills include using a debugger and exception handling. 5. Performance optimization includes using StringBuilder and avoiding unnecessary packing and unboxing.

Testing C# .NET Applications: Unit, Integration, and End-to-End TestingTesting C# .NET Applications: Unit, Integration, and End-to-End TestingApr 09, 2025 am 12:04 AM

Testing strategies for C#.NET applications include unit testing, integration testing, and end-to-end testing. 1. Unit testing ensures that the minimum unit of the code works independently, using the MSTest, NUnit or xUnit framework. 2. Integrated tests verify the functions of multiple units combined, commonly used simulated data and external services. 3. End-to-end testing simulates the user's complete operation process, and Selenium is usually used for automated testing.

Advanced C# .NET Tutorial: Ace Your Next Senior Developer InterviewAdvanced C# .NET Tutorial: Ace Your Next Senior Developer InterviewApr 08, 2025 am 12:06 AM

Interview with C# senior developer requires mastering core knowledge such as asynchronous programming, LINQ, and internal working principles of .NET frameworks. 1. Asynchronous programming simplifies operations through async and await to improve application responsiveness. 2.LINQ operates data in SQL style and pay attention to performance. 3. The CLR of the NET framework manages memory, and garbage collection needs to be used with caution.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
1 months agoBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Chat Commands and How to Use Them
1 months agoBy尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Atom editor mac version download

Atom editor mac version download

The most popular open source editor

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function

Dreamweaver Mac version

Dreamweaver Mac version

Visual web development tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor