2013-07-01 24 views
5

Trong iOS khi bạn nhập mật khẩu vào một trường, ký tự cuối cùng của trường được hiển thị nhưng sau đó sẽ bị làm mờ khi bạn nhập ký tự tiếp theo. Có cách nào để nhân bản hành vi này trong WPF?Custom Passworded Masked trong WPF

+3

Đây được coi là thực tế không tốt nếu bạn làm điều này trên ứng dụng dành cho máy tính để bàn.Hãy xem xét thực tế rằng trên một người điện thoại xem qua vai của bạn là ít hơn nhiều so với một màn hình lớn trên một máy tính để bàn. Vì vậy, trừ khi bạn có ý định cho điều này trên Windows điện thoại hoặc máy tính bảng. Nghĩ lại. – Viv

+2

@Viv có ý nghĩa, nhưng thật không may, tôi không đưa ra yêu cầu trong trường hợp này. – aceinthehole

+0

@Viv plus, ứng dụng này có thể được sử dụng bởi những người có thể có xu hướng về phía khiếm thị, có thể có ý nghĩa hơn một chút trong tình huống đó. – aceinthehole

Trả lời

11

Ok nếu việc sử dụng của bạn cho một thứ như vậy trong ứng dụng dành cho máy tính để bàn là hợp lý, thì bạn có thể thực hiện một số việc như sau.

Chúng tôi đã có một yêu cầu tương tự trước đây và đây là những gì tôi đã làm.

  • Tôi tạo ra một phong tục Passwordbox bằng cách bắt nguồn từ TextBox và thêm một DP mới của loại SecureString với nó. (Khá nhiều khái niệm tương tự như bình thường PasswordBox). Chúng tôi không mất bất kỳ lợi ích bảo mật nào theo cách này và có thể tùy chỉnh hành vi trực quan cho nội dung trái tim của chúng tôi.
  • Với điều này, chúng tôi có thể sử dụng Text của số TextBox làm chuỗi hiển thị và giữ mật khẩu thực tế ở mặt sau SecureString DP và liên kết nó với máy ảo.
  • Chúng tôi xử lý PreviewTextInputPreviewKeyDown sự kiện để quản lý tất cả những thay đổi văn bản trong việc kiểm soát, bao gồm những thứ như Key.Back, Key.Delete và gây phiền nhiễu Key.Space (mà không đi qua PreviewTextInput

iOS cảm nhận:

Một vài điều khác cần lưu ý cho hành vi iOS chính xác.

  1. ký tự cuối cùng chỉ hiển thị khi thêm các ký tự mới vào "đầu của chuỗi hiện tại" (FlowDirection độc lập)
  2. Chỉnh sửa các ký tự nằm giữa chuỗi hiện có không ảnh hưởng đến mặt nạ.
  3. Ký tự cuối cùng được hiển thị phụ thuộc vào bộ đếm thời gian (trở thành "*" sau một khoảng thời gian nhất định nếu không sử dụng)
  4. Tất cả các thao tác sao chép bị vô hiệu hóa trong điều khiển.

2 điểm đầu tiên có thể được xử lý dễ dàng khi phát hiện thay đổi văn bản, lần cuối chúng tôi có thể sử dụng DispatcherTimer để làm việc với chuỗi hiển thị tương ứng.

Vì vậy, việc đưa điều này tất cả cùng nhau chúng ta kết thúc với:

/// <summary> 
/// This class contains properties for CustomPasswordBox 
/// </summary> 
internal class CustomPasswordBox : TextBox { 
    #region Member Variables 
    /// <summary> 
    /// Dependency property to hold watermark for CustomPasswordBox 
    /// </summary> 
    public static readonly DependencyProperty PasswordProperty = 
    DependencyProperty.Register(
     "Password", typeof(SecureString), typeof(CustomPasswordBox), new UIPropertyMetadata(new SecureString())); 

    /// <summary> 
    /// Private member holding mask visibile timer 
    /// </summary> 
    private readonly DispatcherTimer _maskTimer; 
    #endregion 

    #region Constructors 
    /// <summary> 
    /// Initialises a new instance of the LifeStuffPasswordBox class. 
    /// </summary> 
    public CustomPasswordBox() { 
    PreviewTextInput += OnPreviewTextInput; 
    PreviewKeyDown += OnPreviewKeyDown; 
    CommandManager.AddPreviewExecutedHandler(this, PreviewExecutedHandler); 
    _maskTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 1) }; 
    _maskTimer.Tick += (sender, args) => MaskAllDisplayText(); 
    } 
    #endregion 

    #region Commands & Properties 
    /// <summary> 
    /// Gets or sets dependency Property implementation for Password 
    /// </summary> 
    public SecureString Password { 
    get { 
     return (SecureString)GetValue(PasswordProperty); 
    } 

    set { 
     SetValue(PasswordProperty, value); 
    } 
    } 
    #endregion 

    #region Methods 
    /// <summary> 
    /// Method to handle PreviewExecutedHandler events 
    /// </summary> 
    /// <param name="sender">Sender object</param> 
    /// <param name="executedRoutedEventArgs">Event Text Arguments</param> 
    private static void PreviewExecutedHandler(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs) { 
    if (executedRoutedEventArgs.Command == ApplicationCommands.Copy || 
     executedRoutedEventArgs.Command == ApplicationCommands.Cut || 
     executedRoutedEventArgs.Command == ApplicationCommands.Paste) { 
     executedRoutedEventArgs.Handled = true; 
    } 
    } 

    /// <summary> 
    /// Method to handle PreviewTextInput events 
    /// </summary> 
    /// <param name="sender">Sender object</param> 
    /// <param name="textCompositionEventArgs">Event Text Arguments</param> 
    private void OnPreviewTextInput(object sender, TextCompositionEventArgs textCompositionEventArgs) { 
    AddToSecureString(textCompositionEventArgs.Text); 
    textCompositionEventArgs.Handled = true; 
    } 

    /// <summary> 
    /// Method to handle PreviewKeyDown events 
    /// </summary> 
    /// <param name="sender">Sender object</param> 
    /// <param name="keyEventArgs">Event Text Arguments</param> 
    private void OnPreviewKeyDown(object sender, KeyEventArgs keyEventArgs) { 
    Key pressedKey = keyEventArgs.Key == Key.System ? keyEventArgs.SystemKey : keyEventArgs.Key; 
    switch (pressedKey) { 
     case Key.Space: 
     AddToSecureString(" "); 
     keyEventArgs.Handled = true; 
     break; 
     case Key.Back: 
     case Key.Delete: 
     if (SelectionLength > 0) { 
      RemoveFromSecureString(SelectionStart, SelectionLength); 
     } else if (pressedKey == Key.Delete && CaretIndex < Text.Length) { 
      RemoveFromSecureString(CaretIndex, 1); 
     } else if (pressedKey == Key.Back && CaretIndex > 0) { 
      int caretIndex = CaretIndex; 
      if (CaretIndex > 0 && CaretIndex < Text.Length) 
      caretIndex = caretIndex - 1; 
      RemoveFromSecureString(CaretIndex - 1, 1); 
      CaretIndex = caretIndex; 
     } 

     keyEventArgs.Handled = true; 
     break; 
    } 
    } 

    /// <summary> 
    /// Method to add new text into SecureString and process visual output 
    /// </summary> 
    /// <param name="text">Text to be added</param> 
    private void AddToSecureString(string text) { 
    if (SelectionLength > 0) { 
     RemoveFromSecureString(SelectionStart, SelectionLength); 
    } 

    foreach (char c in text) { 
     int caretIndex = CaretIndex; 
     Password.InsertAt(caretIndex, c); 
     MaskAllDisplayText(); 
     if (caretIndex == Text.Length) { 
     _maskTimer.Stop(); 
     _maskTimer.Start(); 
     Text = Text.Insert(caretIndex++, c.ToString()); 
     } else { 
     Text = Text.Insert(caretIndex++, "*"); 
     } 
     CaretIndex = caretIndex; 
    } 
    } 

    /// <summary> 
    /// Method to remove text from SecureString and process visual output 
    /// </summary> 
    /// <param name="startIndex">Start Position for Remove</param> 
    /// <param name="trimLength">Length of Text to be removed</param> 
    private void RemoveFromSecureString(int startIndex, int trimLength) { 
    int caretIndex = CaretIndex; 
    for (int i = 0; i < trimLength; ++i) { 
     Password.RemoveAt(startIndex); 
    } 

    Text = Text.Remove(startIndex, trimLength); 
    CaretIndex = caretIndex; 
    } 

    private void MaskAllDisplayText() { 
    _maskTimer.Stop(); 
    int caretIndex = CaretIndex; 
    Text = new string('*', Text.Length); 
    CaretIndex = caretIndex; 
    } 
    #endregion 
} 

mẫu làm việc:

Download Link

Bạn có thể gõ một cái gì đó vào kiểm soát và kiểm tra giá trị được lưu trữ thể hiện bên dưới nó.

Trong mẫu này, tôi đã thêm một DP mới loại string để chỉ ra rằng điều khiển hoạt động tốt. Bạn sẽ không muốn có DP (HiddenText) trong mã trực tiếp của mình, nhưng tôi hy vọng mẫu sẽ giúp phân tích xem lớp học có thực sự hoạt động hay không :)

+0

Wow, cảm ơn Viv! Đó là chính xác loại điều tôi đang tìm kiếm! Cảm ơn bạn đã giúp đỡ một newbie WPF. – aceinthehole

+0

Vì lý do nào đó, tôi không thể Liên kết '' Mật khẩu''-DP. Nó chỉ đơn giản là không bao giờ cập nhật. SecureString có thể không ràng buộc được không? Tôi sử dụng SecureString ở khắp mọi nơi trong ứng dụng của mình cho đến khi tôi phải lưu trữ nó vào một tệp. Sau đó, tôi nhận được chuỗi tạm thời và ngay lập tức mã hóa nó với một khóa được xác định. Tương tự, khi tôi đọc giá trị. Vì vậy, SecureString là tốt cho loại thiết kế tôi nghĩ. Nhưng tôi không thể cập nhật nó với Bindings = / – ecth