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

互联网 17-8-6
这篇文章主要为大家详细介绍了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的详细内容,更多内容请关注技术你好其它相关文章!

来源链接:
免责声明:
1.资讯内容不构成投资建议,投资者应独立决策并自行承担风险
2.本文版权归属原作所有,仅代表作者本人观点,不代表本站的观点或立场
标签: 图形
上一篇:php获取远程图片并下载保存到本地的方法分析 下一篇:ASP.NET中怎样用MVC5的MiniProfiler对MVC进行性能监控

相关资讯