手机版

WPF图形解锁控件屏幕解锁使用详解

时间:2021-09-04 来源:互联网 编辑:宝哥软件园 浏览:

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

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

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

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

2.提供图形解锁相关扩展属性和事件,方便调用者定义。比如:点和线的颜色(颜色),操作模式(检查|记住),验证正确的颜色(右颜色),验证失败的颜色(错误颜色),解锁事件在检查点上,记忆事件OnRememberPoint等;

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

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

大致思路如上,下面开始一步一步编写屏幕解锁吧

创建屏幕解锁

公共部分类屏幕解锁:用户控制定义相关属性

///摘要///验证正确的颜色////摘要私有SolidColorBrush rightColor///摘要///验证失败的颜色////摘要私有SolidColorBrush errorColor///摘要///图案是否在检查中////摘要私有bool isCheckingpublic静态只读依赖属性PointArrayProperty=依赖属性.Register('PointArray ',typeof(IListstring),type of(ScreenUnlock));///摘要///记忆的坐标点////summary public IListstring PointArray { get { return GetValue(PointArrayProperty)作为IListstring} set { SetValue(PointArrayProperty,value);} } ///摘要///当前坐标点集合////摘要私有ilist string CurrentPointArray///摘要///当前线集合////摘要私有ilist line CurrentLinist///摘要///点集合////摘要私有ilistellipselist///摘要///当前正在绘制的线////摘要专线currentLinepublic静态只读依赖属性操作porperty=依赖属性.注册('操作'),类型为(屏幕解锁操作类型),类型为(屏幕解锁),新框架工作属性元数据(屏幕解锁操作类型。记住));///摘要///操作类型////摘要公共screenunlock Operation type Operation { get { return(screenunlock Operation type)GetValue(Operation property);} set { SetValue(OperationPoperty,value);} }公共静态只读依赖属性PointSizeProperty=依赖属性.注册(' PointSize '、typeof(double)、typeof(ScreenUnlock)、新框架属性元数据(15.0));///摘要///坐标点大小////摘要公共双点大小{ get { return Convert .到double(GetValue(PointSizeProperty));} set { SetValue(PointSizeProperty,value);} }公共静态只读依赖属性color property=依赖属性.注册('颜色,类型为(SolidColorBrush),类型为(屏幕解锁),新框架工作属性元数据(新SolidColorBrush(颜色.白色),新的PropertyChangedCallback((s,e)={ (s作为屏幕解锁).刷新();})));///摘要///坐标点及线条颜色////摘要公共SolidColorBrush Color { get { return GetValue(颜色属性)为SolidColorBrush} set { SetValue(ColorProperty,value);} } ///摘要///操作类型////摘要公共枚举屏幕解锁操作类型{记住=0,检查=1 }初始化屏幕解锁

public ScreenUnlock(){ InitializeComponent();这个。已加载=屏幕解锁_已加载;这个。卸载=屏幕解锁_卸载;这个MouseMove=ScreenUnlock _ MouseMove;//监听绘制事件}私人void ScreenUnlock _ Loaded(对象发送方,RoutedEventArgs e){ isChecking=false;rightColor=新的SolidColorBrush(颜色。绿色);错误颜色=新的SolidColorBrush(颜色。红色);currentPointArray=new Liststring();currentline list=new ListLine();ellipseList=new list elliplex();创建点();}私人void ScreenUnlock _ Unloaded(对象发送方,Routedeventargs e){ right color=null;error color=null if(currentPointArray!=null)这个。currentpointarray。clear();if(CurrentLiST!=null)这个。currentlinelist。clear();if (ellipseList!=null) ellipseList .clear();这个。canvasroot。孩子们。clear();}创建点

///摘要///创建点////总结私有void create point(){ canvasRoot .儿童。clear();int row=3,column=3;//三行三列,九宫格double oneColumnWidth=(这个.实际宽度==0?这个。宽度:这个。实际宽度)/3;//单列的宽度double oneRowHeight=(这个.实际高度==0?这个。身高:这个。实际高度)/3;//单列的高度双左距离=(OneColumnWidth-PointSize)/2;//单列左边距double TopDistance=(OneRowHeight-PointSize)/2;//单列上边距for(var I=0;我划船;I){ for(var j=0;j栏;j ) {椭圆椭圆=新椭圆(){宽度=点大小,高度=点大小,填充=颜色,标记=字符串。格式(' {0}{1} ',I,j)};帆布SetLeft(椭圆,j *一列宽的左距离);帆布SetTop(椭圆,I *一行高度顶距);canvasRoot .儿童。添加(椭圆);ellipseList .添加(椭圆);} } }创建线

专线创建线路(){线路线路=新线路()}描边=颜色,描边厚度=2 };返回线;}点和线都创建都定义好了,可以开始监听绘制事件了

私有void ScreenUnlock_MouseMove(对象发送者,系统窗户。输入。mouseeventargs e){ if(isChecking)//如果图形正在检查中,不响应后续处理返回;如果(例如左键==系统.窗户。输入。mousebuttonstate。按下){ var point=e . GetPosition(this);HitTestResult结果=VisualTreeHelper .HitTest(这个,点);椭圆椭圆=结果。可视化点击为椭圆;如果(椭圆!=null){ if(CurrentLine==null){//从头开始绘制currentLine=create line();var ellipseccenter point=get center point(椭圆);当前线路.X1=电流线X2=埃利普斯中心点x;当前线路.Y1=电流线Y2=椭圆中心点.y;currentPointArray .添加(椭圆标签。ToString());控制台WriteLine(字符串Join(',',currentPointArray));当前列表.添加(CurrentLine);canvasRoot .子代。添加(CurrentLine);} else { //遇到下一个点,排除已经经过的点if (currentPointArray .包含(椭圆标签。ToString()))返回;OnAfterByPoint(椭圆);} } else if (currentLine!=null) { //绘制过程中当前线路.X2=点x;当前线路.Y2=点y。//判断当前线条是否经过点椭圆=等值线();如果(椭圆!=null) OnAfterByPoint(椭圆);} } else { if (currentPointArray).计数==0)返回;isChecking=trueif(当前列表.数到1!=currentPointArray .计数){ //最后一条线的终点不在点上//两点一线,点的个数-1等于线的条数当前列表.移除(CurrentLine);//从已记录的线集合中删除最后一条多余的线canvasRoot .子级。删除(当前行);//从界面上删除最后一条多余的线currentLine=null}如果(操作==屏幕解锁操作类型.检查){控制台。写线('播放动画检查');定义变量结果=CheCk();//执行图形检查//执行完成动画并触发检查事件播放动画(结果,()={ if (OnCheckedPoint!=null) {这个.调度员。BeginInvoke(onchecked point,this,new CheckPointArgs(){ Result=Result });//触发检查完成事件} });}否则如果(操作==屏幕解锁操作类型.记住){控制台写行(‘playAnimation memory’);RememberPoint();//记忆绘制的坐标var args=new RememberPointArgs(){ PointArray=this .PointArray };//执行完成动画并触发记忆事件PlayAnimation(true,()={ if (OnRememberPoint!=null) {这个.调度员。BeginInvoke(on rememberpoint,this,args);//触发图形记忆事件} });} } }判断线是否经过了附近的某个点

///摘要///两点计算一线的长度////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 isoline(){ double LineB=0;//当前画线的长度double LineCA=0;//当前点和A点的距离double LineCB=0;//当前点和B点的距离double dis=0;双重决策=1;//允许的偏差距离line b=GetLineLength(currentLine .X1,电流线Y1,电流线X2,当前在线.Y2);//计算当前画线的长度foreach(ellipseList中的椭圆椭圆){ if (currentPointArray).包含(椭圆。标记。ToString()))))//排除已经经过的点继续;var ellipseccenter point=get center point(椭圆);//取当前点的中心点行ca=GetLineLength(CurrentLine .X1,电流线Y1,ellipseCenterPoint .x,ellipseCenterPoint .y);//计算当前点到线A端的长度行CB=GetLineLength(CurrentLine .X2,currentLine .Y2,ellipseCenterPoint .x,ellipseCenterPoint .y);//计算当前点到线B端的长度dis=数学ABS(LineB-(LineCA LineCB));//线加利福尼亚的长度线民用波段的长度当前线AB型血型血的长度说明点不在线上if (dis=决策)//因为绘制的点具有一个宽度和高度,所以需设定一个允许的偏差范围,让线靠近点就命中之(吸附效果){返回椭圆;} }返回null}检查点是否正确,按数组顺序逐个匹配之

///摘要///检查坐标点是否正确////summary////returns/returns private bool CheckPoint(){//Pointarray 3360正确的坐标值数组//currentPointArray:当前绘制的坐标值数组if (currentPointArray .数数!=点数组.计数)返回false for(var I=0;i currentPointArray .计数;i ) { if (currentPointArray[i]!=PointArray[i])返回false}返回真}记录经过点,并创建一条新的线

///摘要///记录经过的点////summary///param name=' Ellipse '/param private void on fterbypoint(ellipseclie){ var ellipsecenter point=get center point(ellipseclie);当前线路.X2=埃利普斯中心点x;当前线路.Y2=椭圆中心点.y;currentLine=create line();当前线路.X1=电流线X2=埃利普斯中心点x;当前线路.Y1=电流线Y2=椭圆中心点.y;currentPointArray .添加(椭圆标签。ToString());控制台WriteLine(字符串Join(',',currentPointArray));当前列表.添加(CurrentLine);canvasRoot .子代。添加(CurrentLine);}///摘要///获取原点的中心点坐标////summary////param name=' Ellipse '/param///returns/returns private Point GetCenterPoint(椭圆椭圆){ Point p=new Point(画布.获取左(椭圆)椭圆。宽度/2,帆布GetTop(椭圆)椭圆。身高/2);返回p;}当绘制完成时,执行完成动画并触发响应模式的事件

///摘要///执行动画////summary////param name=' result '/param private void PlayAnimation(bool result,Action callback=null) { Task .工厂。StartNew(()={ this .调度程序。调用((动作)委托{ foreach(当前列表中的第l行)l .笔画=结果?right color : errorColorforeach(椭圆列表中的椭圆e)if(currentPointArray).包含(e .标签。tostring()))e . Fill=结果?right color : error color });线程。睡眠(1500);这个调度员。调用((操作)委托{ foreach(CurrentLiST中的第l行)这个。canvasroot。孩子们。移除(l);填充=颜色;});currentLine=nullthis。currentpointarray。clear();这个。CurrentLiST。clear();isChecking=false})。继续(t={ try { if(callback!=null)回调();} catch(异常例如){控制台.WriteLine(例如。消息);}最后{ t . Dispose();} });}图形解锁的调用

local :屏幕解锁宽度='500 '高度='500 '点数组=' {绑定点数组,模式=双向,UpdateSourceTrigger=属性已更改}”操作='检查!-或记住-i:交互.触发器I :事件触发器事件名称=' onchecked point '自定义:事件到命令=' { Binding on checked point } ' PassEventArgsToCommand=' True '/I :事件触发器事件名称=' OnRememberPoint '自定义:事件到命令=' { Binding OnRememberPoint } ' PassEventArgsToCommand=' True '/I :事件触发器/I :交互.触发器/本地:屏幕解锁

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

版权声明:WPF图形解锁控件屏幕解锁使用详解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。