This article is mainly for you to introduce the WPF graphics unlock control screenunlock use method, with a certain reference value, interested in small partners can refer to
The Screenunlock is the same as the pattern unlock feature on your smartphone. Achieve the purpose of unlocking or remembering graphics by drawing graphics.
I had a whim to port the graphics unlock feature on my phone to WPF. are also applied to the company's projects.
Before creating the Screenunlock, we first analyze the implementation of the one-shape unlocking method.
1. Create nine Gongge origin (or more squares) with each point defining a coordinate value
2. Provide graphics unlock related extended properties and events for easy caller definition. For example: color of dots and lines, operating mode (check| Remember), verify the correct color (rightcolor), validate the failed color (errorcolor), unlock event Oncheckedpoint, memory event onrememberpoint, etc.;
3. Define the MouseMove event listener draw line behavior. The drawing line part is also the core of this paper. In the process of drawing lines. The program needs to determine which point the line is drawn from and what point it passed (excluding the points that have been recorded). Whether you have finished drawing, and so on.
4. Draw the line to finish, according to the mode of operation to handle the line completion behavior. and invoke related custom events
General ideas as above, the following start to write Screenunlock step by step
Create Screenunlock
public partial class Screenunlock:usercontrol
Defining related properties
/// <summary>
/// verify the correct color
/// </ summary>
private SolidColorBrush rightColor;
/// <summary>
/// color of validation failure
/// </ summary>
private SolidColorBrush errorColor;
/// <summary>
/// Whether the pattern is being checked
/// </ summary>
private bool isChecking;
public static readonly DependencyProperty PointArrayProperty = DependencyProperty.Register ("PointArray", typeof (IList <string>), typeof (ScreenUnlock));
/// <summary>
/// memorized coordinate points
/// </ summary>
public IList <string> PointArray
{
get {return GetValue (PointArrayProperty) as IList <string>;}
set {SetValue (PointArrayProperty, value);}
}
/// <summary>
/// current coordinate point set
/// </ summary>
private IList <string> currentPointArray;
/// <summary>
/// current line collection
/// </ summary>
private IList <Line> currentLineList;
/// <summary>
/// point collection
/// </ summary>
private IList <Ellipse> ellipseList;
/// <summary>
/// the line currently being drawn
/// </ summary>
private Line currentLine;
public static readonly DependencyProperty OperationPorperty = DependencyProperty.Register ("Operation", typeof (ScreenUnLockOperationType), typeof (ScreenUnlock), new FrameworkPropertyMetadata (ScreenUnLockOperationType.Remember));
/// <summary>
/// operation type
/// </ 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>
/// coordinate point size
/// </ 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>
/// coordinate point and line color
/// </ summary>
public SolidColorBrush Color
{
get {return GetValue (ColorProperty) as SolidColorBrush;}
set {SetValue (ColorProperty, value);}
}
/// <summary>
/// operation type
/// </ 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; // Listen to draw events
}
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>
/// create points
/// </ summary>
private void CreatePoint ()
{
canvasRoot.Children.Clear ();
int row = 3, column = 3; // Three rows and three columns, Jiugongge
double oneColumnWidth = (this.ActualWidth == 0? this.Width: this.ActualWidth) / 3; // The width of a single column
double oneRowHeight = (this.ActualHeight == 0? this.Height: this.ActualHeight) / 3; // Height of a single column
double leftDistance = (oneColumnWidth-PointSize) / 2; // Single column left margin
double topDistance = (oneRowHeight-PointSize) / 2; // Single column top margin
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 lines
private Line CreateLine()
{
Line line = new Line()
{
Stroke = Color,
StrokeThickness = 2
};
return line;
}
Both point and line creation are defined so that you can start listening for drawing events.
private void ScreenUnlock_MouseMove (object sender, System.Windows.Input.MouseEventArgs e)
{
if (isChecking) // If the graphics are being checked, do not respond to subsequent processing
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)
{
// Draw from scratch
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
{
// Meet the next point, exclude points that have passed
if (currentPointArray.Contains (ellipse.Tag.ToString ()))
return;
OnAfterByPoint (ellipse);
}
}
else if (currentLine! = null)
{
// During the drawing process
currentLine.X2 = point.X;
currentLine.Y2 = point.Y;
// Judge whether the current Line passes through the point
ellipse = IsOnLine ();
if (ellipse! = null)
OnAfterByPoint (ellipse);
}
}
else
{
if (currentPointArray.Count == 0)
return;
isChecking = true;
if (currentLineList.Count + 1! = currentPointArray.Count)
{
// The end of the last line is not on the point
// Two points and one line, the number of points -1 is equal to the number of lines
currentLineList.Remove (currentLine); // Remove the last extra line from the set of recorded lines
canvasRoot.Children.Remove (currentLine); // Remove the last extra line from the interface
currentLine = null;
}
if (Operation == ScreenUnLockOperationType.Check)
{
Console.WriteLine ("playAnimation Check");
var result = CheckPoint (); // Perform graphic check
// Execute completion animation and trigger check event
PlayAnimation (result, () =>
{
if (OnCheckedPoint! = null)
{
this.Dispatcher.BeginInvoke (OnCheckedPoint, this, new CheckPointArgs () {Result = result}); // Trigger check completion event
}
});
}
else if (Operation == ScreenUnLockOperationType.Remember)
{
Console.WriteLine ("playAnimation Remember");
RememberPoint (); // Remember drawing coordinates
var args = new RememberPointArgs () {PointArray = this.PointArray};
// Execute completion animation and trigger memory events
PlayAnimation (true, () =>
{
if (OnRememberPoint! = null)
{
this.Dispatcher.BeginInvoke (OnRememberPoint, this, args); // Trigger the graphic memory event
}
});
}
}
}
Determine if a line has passed a nearby point
/// <summary>
/// calculate the length of a line at two points
/// </ 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)); // Calculate the line length formula based on two points√ ((x1-x2) ²x (y1- y2) ²)
}
/// <summary>
/// determine whether the line has passed a certain point
/// </ summary>
/// <param name = "ellipse"> </ param>
/// <returns> </ returns>
private Ellipse IsOnLine ()
{
double lineAB = 0; // length of the current line
double lineCA = 0; // distance between current point and point A
double lineCB = 0; // distance between current point and point B
double dis = 0;
double deciation = 1; // allowable deviation distance
lineAB = GetLineLength (currentLine.X1, currentLine.Y1, currentLine.X2, currentLine.Y2); // Calculate the length of the currently drawn line
foreach (Ellipse ellipse in ellipseList)
{
if (currentPointArray.Contains (ellipse.Tag.ToString ())) // Exclude points that have passed
continue;
var ellipseCenterPoint = GetCenterPoint (ellipse); // Get the center point of the current point
lineCA = GetLineLength (currentLine.X1, currentLine.Y1, ellipseCenterPoint.X, ellipseCenterPoint.Y); // Calculate the length from the current point to the A end of the line
lineCB = GetLineLength (currentLine.X2, currentLine.Y2, ellipseCenterPoint.X, ellipseCenterPoint.Y); // Calculate the length from the current point to the B end of the line
dis = Math.Abs (lineAB-(lineCA + lineCB)); // length of line CA + length of line CB> length of current line AB
if (dis <= deciation) // Because the drawn point has a width and height, you need to set an allowable deviation range, and let the line hit the point and hit it (adsorption effect)
{
return ellipse;
}
}
return null;
}
Check points are correct, matching them in array order
/// <summary>
/// check if the coordinate points are correct
/// </ summary>
/// <returns> </ returns>
private bool CheckPoint ()
{
/// PointArray: the correct array of coordinate values
Current // currentPointArray: array of coordinate values currently drawn
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 past points and create a new line
/// <summary>
/// record the points passed
/// </ 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>
/// Get the coordinates of the center point of the origin
/// </ 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 complete, an event that finishes the animation and triggers the response mode is executed
/// <summary>
/// execute the animation
/// </ 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 ();
}
});
}
Call to unlock Graphics
<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>