详解如何用WPF图形解锁控件ScreenUnLock

详解如何用WPF图形解锁控件ScreenUnLock,第1张

概述详解如何用WPF图形解锁控件ScreenUnLock 这篇文章主要为大家详细介绍了WPF图形解锁控件ScreenUnLock的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

ScreenUnLock 与智能手机上的图案解锁功能一样。通过绘制图形达到解锁或记忆图形的目的。

本人突发奇想,把手机上的图形解锁功能移植到WPF中。也应用到了公司的项目中。

在创建ScreenUnLock之前,先来分析一下图形解锁的实现思路。

1.创建九宫格原点(或更多格子),每个点定义一个坐标值

2.提供图形解锁相关扩展属性和事件,方便调用者定义。比如:点和线的颜色(color), *** 作模式(Check|Remember),验证正确的颜色(Rightcolor), 验证失败的颜色(Errorcolor), 解锁事件 OnCheckedPoint,记忆事件 OnRememberPoint 等;

3.定义MouseMove事件监听画线行为。 画线部分也是本文的核心。在画线过程中。程序需判断,线条从哪个点开始绘制,经过了哪个点(排除已经记录的点)。是否完成了绘制等等。

4.画线完成,根据 *** 作模式处理画线完成行为。并调用相关自定义事件

大致思路如上,下面开始一步一步编写ScreenUnLock吧

创建ScreenUnLock


public partial class ScreenUnlock : UserControl

定义相关属性


/// <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     }

初始化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();  }

创建点


/// <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);    }   }  }

创建线


private line Createline()  {   line line = new line()   {    stroke = color,    strokeThickness = 2   };   return line;  }

点和线都创建都定义好了,可以开始监听绘制事件了


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); //触发图形记忆事件      }     });    }   }  }

判断线是否经过了附近的某个点


/// <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;  }

检查点是否正确,按数组顺序逐个匹配之


/// <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;  }

记录经过点,并创建一条新的线


/// <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;  }

当绘制完成时,执行完成动画并触发响应模式的事件


/// <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();    }   });  }

图形解锁的调用


<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>

总结

以上是内存溢出为你收集整理的详解如何用WPF图形解锁控件ScreenUnLock全部内容,希望文章能够帮你解决详解如何用WPF图形解锁控件ScreenUnLock所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1211844.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-04
下一篇 2022-06-04

发表评论

登录后才能评论

评论列表(0条)

保存