65.9K
CodeProject 正在变化。 阅读更多。
Home

Android风格的登录屏幕模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (12投票s)

2013年7月12日

CPOL

3分钟阅读

viewsIcon

53110

downloadIcon

3285

绘制图案代替密码用于登录屏幕。

引言

本文解释了如何创建智能手机风格的密码屏幕。身份验证基于用户名和屏幕上绘制的屏幕代码。

背景

这个基本想法来自基于 Android 的智能手机,它们提供基于屏幕上绘制的图案的屏幕锁定/解锁功能。

使用代码

登录过程

在登录屏幕中,将有两个身份验证过程。 将对用户名进行身份验证,并显示图案画布。 绘制图案并在完成后,将对用户进行身份验证。 完成用户名身份验证是为了检索输入用户的屏幕图案信息。

屏幕要求

在用户名身份验证后显示屏幕图案画布。 通过选择隐身模式,可以在可见/不可见模式下绘制图案。 如果输入无效,屏幕代码将以红色显示一秒钟,然后自动擦除。 成功后,将显示已通过身份验证的消息,并且屏幕代码将被擦除。 允许在身份验证成功后编辑屏幕代码。 屏幕代码可以保存,并且内部绘制的图像将通过邮件发送给用户,绘制的图案将在图像区域中供用户参考。

如何实现以上要求

第一部分

  1. 创建一个 LockView 用户控件。
  2. 创建图案 Canvas
  3. 创建一个带有标签值设置(1,2..9)的椭圆 3x3 矩阵。
  4. 注册椭圆 Mouse Down 事件。
  5. 注册椭圆 Mouse Move 事件。
  6. 注册 Canvas Mouse up 事件。
  7. 创建隐身模式复选框。
  8. 注册保存和清除按钮事件。

第二部分

  1. 创建验证委托,参数包括屏幕数据和验证标志。
  2. 创建保存委托,参数包括屏幕数据。
  3. 创建屏幕数据,包括验证委托对象、保存委托对象、屏幕代码、目标屏幕代码、绘制的图像源。

第三部分

  1. 创建锁基类,其中屏幕数据和屏幕模式作为依赖属性,以及绘制图案、插入图案数据、验证图案、保存图案、将屏幕图案捕获为图像源的方法。
  2. 使用 MD5Hash 加密屏幕图案并设置屏幕代码值。

如何绘制图案

在椭圆鼠标按下时创建一个折线形状,并在鼠标在有效椭圆上移动时继续添加点。 添加带有箭头的线条以显示图案的流动方向。 箭头线是根据折线点集合中的当前点和前一个点创建的。 应用 theta 来计算箭头角度。

NewLine = new Polyline();
NewLine.Name = CodeLine;
NewLine.Stroke = color;
NewLine.StrokeThickness = 10.0;  
  
/// <summary>
/// Draw arrow lines for the selected points
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="arrowName"></param>
/// <returns></returns>
protected static Shape DrawLinkArrow(Point p1, Point p2, string arrowName)
{
    GeometryGroup lineGroup = new GeometryGroup();
    double theta = Math.Atan2((p2.Y - p1.Y), (p2.X - p1.X)) * 180 / Math.PI;

    PathGeometry pathGeometry = new PathGeometry();
    PathFigure pathFigure = new PathFigure();
    Point p = new Point(p1.X + ((p2.X - p1.X) / 1.35), p1.Y + ((p2.Y - p1.Y) / 1.35));
    pathFigure.StartPoint = p;

    Point lpoint = new Point(p.X + 6, p.Y + 15);
    Point rpoint = new Point(p.X - 6, p.Y + 15);
    LineSegment seg1 = new LineSegment();
    seg1.Point = lpoint;
    pathFigure.Segments.Add(seg1);

    LineSegment seg2 = new LineSegment();
    seg2.Point = rpoint;
    pathFigure.Segments.Add(seg2);

    LineSegment seg3 = new LineSegment();
    seg3.Point = p;
    pathFigure.Segments.Add(seg3);

    pathGeometry.Figures.Add(pathFigure);
    RotateTransform transform = new RotateTransform();
    transform.Angle = theta + 90;
    transform.CenterX = p.X;
    transform.CenterY = p.Y;
    pathGeometry.Transform = transform;
    lineGroup.Children.Add(pathGeometry);

    LineGeometry connectorGeometry = new LineGeometry();
    connectorGeometry.StartPoint = p1;
    connectorGeometry.EndPoint = p2;
    lineGroup.Children.Add(connectorGeometry);
    System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();
    path.Data = lineGroup;
    path.StrokeThickness = 2;
    path.Stroke = path.Fill = Brushes.Black;
    path.Name = string.Format("ArrowLine{0}", arrowName);
    return path;
}

检查重复条目并将每个椭圆的标签值插入堆栈。 在鼠标抬起时,通过将其加密为 MD5hash 字符串来验证图案与目标屏幕代码是否匹配。 使用验证标志和屏幕数据作为参数调用 onvalidate 委托。 启动一个时间间隔为 1 秒的计时器,以清除屏幕上的所有代码和图案。 如果屏幕图案无效,请用红色重新绘制图案并再次启动计时器以清除屏幕。

在画布中创建一个标签值从 (1,2,3...9) 开始的椭圆 3x3 矩阵

XAML 代码

<Canvas x:Name="LockCanvas" MouseUp="Canvas_MouseUp" Margin="110,20,80,10"
                    HorizontalAlignment="Center"  Height="180" Width="200" 
                    Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
    <Ellipse   x:Name="Code1" Tag="1" Margin="5" Canvas.Left="0"
      Canvas.Top="0" Fill="Silver" Height="35"
      Width="35"
      StrokeThickness="5"
      Stroke="Black"  MouseMove="Canvas_MouseMove"  
      MouseDown="Canvas_MouseDown"/>
        :
    :
</Canvas>

C# 代码

//Draw pattern on mouse down
 CodeSequence = new Stack<int>();
 ClearAll();
 CreatePolyLine(Brushes.Green);
 FillCodeSequence(sender as Ellipse);
 if (!chkStealthMode.IsChecked.Value)
 {
   FillCodeColor(sender as Ellipse, Brushes.Green);
   LockCanvas.Children.Add(NewLine);
 }


//start drawing the screen code on mouse move checking duplicity and starting and end points
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        Point currentPoint = e.GetPosition(LockCanvas);
        var ellipse = sender as Ellipse;
        bool firstEntry = IsItFirstEntry(ellipse);
        Point point = GetPointFromCodeEllipse(ellipse);
        if (!CheckSequenceExists(ellipse))
        {
            Shape arrow = null;
            if (NewLine.Points.Count - 1 > 0)
            {
                arrow = DrawLinkArrow(NewLine.Points[NewLine.Points.Count - 1], point,
                        Convert.ToString(ellipse.Tag));
            }
            NewLine.Points.Add(point);
            if (!chkStealthMode.IsChecked.Value)
            {
                FillCodeColor(sender as Ellipse, Brushes.Green);
                if (arrow != null)
                {
                    LockCanvas.Children.Add(arrow);
                }
            }
            FillCodeSequence(ellipse);
        }
        if (firstEntry)
        {
            NewLine.Points.Add(point);
        }
    }
}

创建/编辑屏幕图案

保存时,图案将保存为 MD5Hash 加密代码,该代码将保存在数据库中。 保存图案后,捕获的屏幕图案将通过电子邮件发送给用户以供参考。

C#

private static BitmapSource CaptureScreen(Visual target, double dpiX, double dpiY)
{
    if (target == null)
    {
        return null;
    }
    Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
    RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
                                                    (int)(bounds.Height * dpiY / 96.0),
                                                    dpiX,
                                                    dpiY,
                                                    PixelFormats.Pbgra32);
    DrawingVisual dv = new DrawingVisual();
    using (DrawingContext ctx = dv.RenderOpen())
    {
        VisualBrush vb = new VisualBrush(target);
        ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
    }
    rtb.Render(dv);
    return rtb;
}

/// <summary>
/// Encrypt passcode and raise the event to continue using the encrypted passcode
/// </summary>
protected virtual void SavePassCode()
{
    if (ScreenLockData != null && ScreenLockData.OnSave != null)
    {
        var encryptedCode = Encryptor.MD5Hash(CodeSequence.ToStackString());
        ScreenLockData.ScreenCode = encryptedCode;
        ScreenLockData.TargetScreenCode = encryptedCode;
        ScreenLockData.SreenCodeImage = CaptureScreen(LockCanvas, 100, 100);
        ScreenLockData.OnSave(ScreenLockData);
        timer.Start();
    }
}

登录窗口

由于我们已经准备好使用锁屏用户控件,让我们在登录页面中使用它。

ScreenLockdata 与类型为 IScreenLockViewModel 的视图模型绑定。 使用处理程序方法注册视图模型委托。

XAML:

<lock:LockView ScreenLockMode="{Binding ScreenLockMode,Mode=TwoWay,
    UpdateSourceTrigger=PropertyChanged}" 
    HorizontalAlignment="Stretch" 
    Visibility="{Binding ScreenVisibility,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" 
    ScreenLockData="{Binding ScreenLockViewModel, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
</lock:LockView>
/// <summary>
/// Screen lock data
/// </summary>
public IScreenLockViewModel ScreenLockViewModel
{
    get { return screenViewModel; }
    set
    {
        screenViewModel = value;
        RaisePropertyChanged("ScreenLockViewModel ");
    }
}

ScreenLockViewModel = new ScreenLockViewModel();
ScreenLockViewModel.OnScreenCodeValidated = OnScreenCodeValidation;
ScreenLockViewModel.OnSave = OnSave;
ScreenLockViewModel.TargetScreenCode = GetUserCode(user);// get from database;
ScreenLockMode = Extensions.ScreenLockMode.Login;
this.DataContext = this;

/// <summary>
/// Authenticate the user and proceed
/// </summary>
/// <param name="obj"></param>
/// <param name="isValidated"></param>
private void OnScreenCodeValidation(IScreenLockViewModel obj, bool isValidated)
{
    if (isValidated)
        MessageBox.Show(string.Format("Authenticated Password : {0}", obj.ScreenCode));
}

/// <summary>
/// Create or edit an existing password and save the target screen code 
/// </summary>
/// <param name="obj"></param>
private void OnSave(IScreenLockViewModel obj)
{
    MessageBox.Show("Saved Successfully!");
    ScreenLockViewModel.TargetScreenCode = obj.ScreenCode;
    ScreenLockMode = Extensions.ScreenLockMode.Login;
    SavedImage = obj.SreenCodeImage;
  //Send an email with screen code attachment
}

关注点

  1. 用于绑定屏幕数据的依赖属性。
  2. 绘制诸如折线和线段之类的形状。
  3. 使用几何组来绘制带有箭头的线条。
  4. 委托。
  5. 使用 MD5Hash 加密。
  6. 图案控制可用于锁定屏幕或用于初始身份验证过程。
© . All rights reserved.