1. 概述
微軟官方有提供自己的密碼控制元件,但是控制元件預設的行為是輸入密碼,會立即顯示掩碼,比如 *。如果像檢視真實的文字,需要按檢視按鈕。
而我現在自定義的密碼控制元件是先顯示你輸入的字元2s,然後再顯示成掩碼。當然這種場景並不一定適用於密碼,也可以用在Pin碼。
a. 微軟官方的密碼框
b. 自定義的效果
2. 密碼框控制元件實現
要想實現自定義的密碼框,我們當然要繼承微軟的TextBox控制元件,然後加入我們自己需要的東西。
a. 這裡新增了一個加密型別的密碼 Password 欄位,真實密碼文字 RealPassword 欄位,一個Timer定時器來控制顯示掩碼
internal class CustomPasswordBox : TextBox { #region Member Variables public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(SecureString), typeof(CustomPasswordBox), new PropertyMetadata(new SecureString())); public static readonly DependencyProperty RealPasswordProperty = DependencyProperty.Register("RealPassword", typeof(string), typeof(CustomPasswordBox), new PropertyMetadata(string.Empty)); private DispatcherTimer maskTimer; #endregion }
b. 公開控制元件屬性欄位
public SecureString Password { get { return (SecureString)GetValue(PasswordProperty); } set { SetValue(PasswordProperty, value); } } public string RealPassword { get { return (string)GetValue(RealPasswordProperty); } set { SetValue(RealPasswordProperty, value); } }
c. 然後再建構函式裡新增一個需要相應的事件,還有初始化Timer
public CustomPasswordBox() { PreviewKeyDown += CustomPasswordBox_PreviewKeyDown; CharacterReceived += CustomPasswordBox_CharacterReceived; BeforeTextChanging += CustomPasswordBox_BeforeTextChanging; SelectionChanged += CustomPasswordBox_SelectionChanged; ContextMenuOpening += CustomPasswordBox_ContextMenuOpening; TextCompositionStarted += CustomPasswordBox_TextCompositionStarted; maskTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 2) }; maskTimer.Tick += MaskTimer_Tick; }
在PreviewKeyDown、CharacterReceived、BeforeTextChanging這三個事件裡,主要處理字元的輸入刪除等邏輯。
在SelectionChanged事件裡,我們主要處理不讓使用者選中文字
在ContextMenuOpening,主要是遮蔽右鍵選單,比如複製剪下貼上
在TextCompositionStarted主要遮蔽中文等組合輸入法
另外,在我的場景裡面,我們只讓使用者輸入數字,所以加入了數字驗證,以及在Xbox上開啟了InputScope=NumbericPin模式。
d. 新增事件響應程式碼
private void CustomPasswordBox_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { switch (e.OriginalKey) { case VirtualKey.Back: case VirtualKey.Delete: if (SelectionLength > 0) { RemoveFromSecureString(SelectionStart, SelectionLength); } else if (e.OriginalKey == VirtualKey.Delete && SelectionStart < Text.Length) { RemoveFromSecureString(SelectionStart, 1); } else if (e.OriginalKey == VirtualKey.Back && SelectionStart > 0) { int caretIndex = SelectionStart; if (SelectionStart > 0 && SelectionStart < Text.Length) caretIndex = caretIndex - 1; RemoveFromSecureString(SelectionStart - 1, 1); //SelectionStart = caretIndex; } e.Handled = true; break; default: //e.Handled = true; break; } } private void CustomPasswordBox_CharacterReceived(UIElement sender, Windows.UI.Xaml.Input.CharacterReceivedRoutedEventArgs args) { if (!char.IsDigit(args.Character)) return; AddToSecureString(args.Character.ToString()); args.Handled = true; } private void CustomPasswordBox_BeforeTextChanging(TextBox sender, TextBoxBeforeTextChangingEventArgs args) { args.Cancel = args.NewText.Any(c => !char.IsDigit(c) && !char.ToString(c).ToString().Equals("●")); //if (args.NewText.Replace("●", "") != "") // AddToSecureString(args.NewText.Replace("●", "")); } private void CustomPasswordBox_ContextMenuOpening(object sender, ContextMenuEventArgs e) { e.Handled = true; } private void CustomPasswordBox_SelectionChanged(object sender, RoutedEventArgs e) { SelectionStart = Text.Length; SelectionLength = 0; } private void CustomPasswordBox_TextCompositionStarted(TextBox sender, TextCompositionStartedEventArgs args) { return; } private void MaskTimer_Tick(object sender, object e) { MaskAllDisplayText(); }
e. 實現Timer裡面的2s後將字串變成掩碼
2s後看TextBox裡面的字元數,然後設定對應長度的掩碼
private void MaskAllDisplayText() { maskTimer.Stop(); int caretIndex = SelectionStart; Text = new string('●', Text.Length); SelectionStart = caretIndex; }
f. 新增字元和刪除字元
private void AddToSecureString(string text) { if (SelectionLength > 0) { RemoveFromSecureString(SelectionStart, SelectionLength); } if (Password.Length >= 4 || RealPassword.Length >= 4) return; foreach (char c in text) { System.Diagnostics.Debug.WriteLine(text); int caretIndex = SelectionStart; if (caretIndex - 1 < 0) Password.InsertAt(0, c); else Password.InsertAt(caretIndex - 1, c); RealPassword += c.ToString(); //MaskAllDisplayText(); if (caretIndex == Text.Length) { maskTimer.Stop(); maskTimer.Start(); //Text = Text.Insert(caretIndex++, c.ToString()); } else { //Text = Text.Insert(caretIndex++, "●"); } SelectionStart = Text.Length; } } private void RemoveFromSecureString(int startIndex, int trimLength) { int caretIndex = SelectionStart; for (int i = 0; i < trimLength; ++i) { Password.RemoveAt(startIndex); RealPassword = RealPassword.Remove(startIndex, 1); } Text = Text.Remove(startIndex, trimLength); SelectionStart = caretIndex; }
3. 控制元件使用方法
<local:CustomPasswordBox InputScope="NumericPin" MaxLength="4" />
一個例子
<Grid> <local:CustomPasswordBox x:Name="PSW" InputScope="NumericPin" Padding="200 0 0 0" Style="{StaticResource MyTextBox}" Height="Auto" MaxLength="4" CharacterSpacing="1000" FontSize="100" VerticalAlignment="Top"/> <TextBlock x:Name="realPSW" FontSize="88" Margin="0 200" HorizontalAlignment="Center" Text="{Binding ElementName=PSW, Path=RealPassword, Mode=OneWay}"/> </Grid>
你也可以看我在Youtube上的視訊演示
歡迎點選右下角的訂閱按鈕,還有紅色小鈴鐺。謝謝