2013-07-01 65 views
5

在iOS中,當您在字段中輸入密碼時,會顯示該字段的最後一個字母,但在鍵入下一個字符時會被混淆。有沒有辦法在WPF中複製這種行爲?WPF中的自定義屏蔽密碼框

+3

如果你這樣做,這被認爲是不好的做法在桌面應用上。考慮一下這樣一個事實,在手機上,人們看着你的肩膀遠遠不及桌面上的大型顯示器。所以除非你打算在Windows手機或平板電腦上使用。再想一想。 – Viv

+2

@Viv是有道理的,但不幸的是,我不是在這種情況下規定要求。 – aceinthehole

+0

@Viv plus,這個應用程序可能會被那些傾向於視覺受損的人使用,在這種情況下可能會更有意義。 – aceinthehole

回答

11

好的,如果你在桌面應用程序中使用這樣的東西是合理的,那麼你可以做類似下面的事情。

我們之前有過類似的要求,這就是我所做的。

  • 我從TextBox派生並添加SecureString類型,它的一個新的DP創建自定義Passwordbox。(幾乎相同的概念正常PasswordBox)。我們不以這種方式放棄任何安全利益,並且可以根據我們心中的內容定製視覺行爲。
  • 現在,我們可以使用TextBoxText,因爲它的顯示字符串並在後端SecureString DP中保存實際密碼並將其綁定到VM。
  • 我們處理PreviewTextInputPreviewKeyDown事件來管理控件中的所有文本的變化,包括像Key.BackKey.Delete和惱人的Key.Space東西(不通過PreviewTextInput

iOS的感覺來了:

夫婦有更多的事情要注意一個確切的iOS行爲

  1. 僅在向「當前字符串的末尾」添加新字符時顯示最後一個字符(FlowDirection獨立)
  2. 編輯現有字符串之間的字符對掩碼沒有影響。
  3. 顯示的最後一個字符與定時器有關(如果空閒,在一段時間後變爲「*」)
  4. 控件中禁用的所有複製粘貼操作。

首先2分能檢測文本發生變化時很容易進行處理,最後一個我們可以使用一個DispatcherTimer與顯示字符串相應工作。

所以把所有這一切我們一起結束:

/// <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 
} 

工作示例:

Download Link

您可以輸入一些東西到控制和檢查顯示的儲值在它下面。

在本示例中,我添加了一個新的DP類型string,以表明控件正常工作。你會不想在你的實時代碼中使用DP(HiddenText),但我希望這個示例有助於分析這個類是否真正起作用:)

+0

哇,謝謝Viv!這正是我尋找的東西的類型!感謝您幫助WPF新手。 – aceinthehole

+0

出於某種原因,我不能綁定''Password''-DP。它根本不會更新。 SecureString是不是可綁定的?我在我的應用程序的每個地方都使用SecureString,直到我必須將其存儲到文件中。然後我得到臨時字符串,並立即使用定義的密鑰對其進行加密。同樣的,當我讀到價值。所以SecureString對我認爲的這種設計非常有用。但我無法使用Bindings = / – ecth