using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Security; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using HandyControl.Data; using HandyControl.Interactivity; namespace HandyControl.Controls; [TemplatePart(Name = ElementPasswordBox, Type = typeof(System.Windows.Controls.PasswordBox))] [TemplatePart(Name = ElementTextBox, Type = typeof(System.Windows.Controls.TextBox))] public class PasswordBox : Control { private const string ElementPasswordBox = "PART_PasswordBox"; private const string ElementTextBox = "PART_TextBox"; private SecureString _password; private System.Windows.Controls.TextBox _textBox; /// /// 掩码字符 /// public static readonly DependencyProperty PasswordCharProperty = System.Windows.Controls.PasswordBox.PasswordCharProperty.AddOwner(typeof(PasswordBox), new FrameworkPropertyMetadata('●')); public char PasswordChar { get => (char) GetValue(PasswordCharProperty); set => SetValue(PasswordCharProperty, value); } public static readonly DependencyProperty ShowEyeButtonProperty = DependencyProperty.Register( nameof(ShowEyeButton), typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox)); public bool ShowEyeButton { get => (bool) GetValue(ShowEyeButtonProperty); set => SetValue(ShowEyeButtonProperty, ValueBoxes.BooleanBox(value)); } public static readonly DependencyProperty ShowPasswordProperty = DependencyProperty.Register( nameof(ShowPassword), typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox, OnShowPasswordChanged)); public bool ShowPassword { get => (bool) GetValue(ShowPasswordProperty); set => SetValue(ShowPasswordProperty, ValueBoxes.BooleanBox(value)); } public static readonly DependencyProperty IsSafeEnabledProperty = DependencyProperty.Register( nameof(IsSafeEnabled), typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.TrueBox, OnIsSafeEnabledChanged)); private static void OnIsSafeEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var p = (PasswordBox) d; p.SetCurrentValue(UnsafePasswordProperty, !(bool) e.NewValue ? p.Password : string.Empty); } public bool IsSafeEnabled { get => (bool) GetValue(IsSafeEnabledProperty); set => SetValue(IsSafeEnabledProperty, ValueBoxes.BooleanBox(value)); } public static readonly DependencyProperty UnsafePasswordProperty = DependencyProperty.Register( nameof(UnsafePassword), typeof(string), typeof(PasswordBox), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnUnsafePasswordChanged)); private static void OnUnsafePasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var p = (PasswordBox) d; if (!p.IsSafeEnabled) { p.Password = e.NewValue != null ? e.NewValue.ToString() : string.Empty; } } public string UnsafePassword { get => (string) GetValue(UnsafePasswordProperty); set => SetValue(UnsafePasswordProperty, value); } public static readonly DependencyProperty MaxLengthProperty = System.Windows.Controls.TextBox.MaxLengthProperty.AddOwner(typeof(PasswordBox)); public int MaxLength { get => (int) GetValue(MaxLengthProperty); set => SetValue(MaxLengthProperty, value); } public static readonly DependencyProperty SelectionBrushProperty = TextBoxBase.SelectionBrushProperty.AddOwner(typeof(PasswordBox)); public Brush SelectionBrush { get => (Brush) GetValue(SelectionBrushProperty); set => SetValue(SelectionBrushProperty, value); } #if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472) public static readonly DependencyProperty SelectionTextBrushProperty = TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(PasswordBox)); public Brush SelectionTextBrush { get => (Brush) GetValue(SelectionTextBrushProperty); set => SetValue(SelectionTextBrushProperty, value); } #endif public static readonly DependencyProperty SelectionOpacityProperty = TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(PasswordBox)); public double SelectionOpacity { get => (double) GetValue(SelectionOpacityProperty); set => SetValue(SelectionOpacityProperty, value); } public static readonly DependencyProperty CaretBrushProperty = TextBoxBase.CaretBrushProperty.AddOwner(typeof(PasswordBox)); public Brush CaretBrush { get => (Brush) GetValue(CaretBrushProperty); set => SetValue(CaretBrushProperty, value); } #if !NET40 public static readonly DependencyProperty IsSelectionActiveProperty = TextBoxBase.IsSelectionActiveProperty.AddOwner(typeof(PasswordBox)); public bool IsSelectionActive => ActualPasswordBox != null && (bool) ActualPasswordBox.GetValue(IsSelectionActiveProperty); #endif public PasswordBox() => CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) => Clear())); protected override void OnGotFocus(RoutedEventArgs e) { base.OnGotFocus(e); if(_textBox!=null)_textBox.Focus(); } public System.Windows.Controls.PasswordBox ActualPasswordBox { get; set; } [DefaultValue("")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string Password { get { if (ShowEyeButton && ShowPassword) { return _textBox.Text; } return ActualPasswordBox?.Password; } set { if (ActualPasswordBox == null) { _password = new SecureString(); value ??= string.Empty; foreach (var item in value) _password.AppendChar(item); return; } if (Equals(ActualPasswordBox.Password, value)) return; ActualPasswordBox.Password = value; } } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public SecureString SecurePassword => ActualPasswordBox?.SecurePassword; private static void OnShowPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctl = (PasswordBox) d; if (!ctl.ShowEyeButton) return; if ((bool) e.NewValue) { ctl._textBox.Text = ctl.ActualPasswordBox.Password; ctl._textBox.Select(string.IsNullOrEmpty(ctl._textBox.Text) ? 0 : ctl._textBox.Text.Length, 0); } else { ctl.ActualPasswordBox.Password = ctl._textBox.Text; ctl._textBox.Clear(); } } public override void OnApplyTemplate() { if (ActualPasswordBox != null) ActualPasswordBox.PasswordChanged -= PasswordBox_PasswordChanged; if (_textBox != null) _textBox.TextChanged -= TextBox_TextChanged; base.OnApplyTemplate(); ActualPasswordBox = GetTemplateChild(ElementPasswordBox) as System.Windows.Controls.PasswordBox; _textBox = GetTemplateChild(ElementTextBox) as System.Windows.Controls.TextBox; if(_textBox!=null) { _textBox.SetBinding(System.Windows.Controls.TextBox.MaxLengthProperty, new Binding(MaxLengthProperty.Name) { Source = this }); _textBox.SetBinding(System.Windows.Controls.TextBox.SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this }); #if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472) _textBox.SetBinding(System.Windows.Controls.TextBox.SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this }); #endif _textBox.SetBinding(System.Windows.Controls.TextBox.ForegroundProperty, new Binding(ForegroundProperty.Name) { Source = this }); _textBox.SetBinding(System.Windows.Controls.TextBox.SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this }); _textBox.SetBinding(System.Windows.Controls.TextBox.CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this }); } if (ActualPasswordBox != null) { ActualPasswordBox.PasswordChanged += PasswordBox_PasswordChanged; ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.MaxLengthProperty, new Binding(MaxLengthProperty.Name) { Source = this }); ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this }); #if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472) ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this }); #endif ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this }); ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this }); if (_password is { Length: > 0 }) { var valuePtr = IntPtr.Zero; try { valuePtr = Marshal.SecureStringToGlobalAllocUnicode(_password); ActualPasswordBox.Password = Marshal.PtrToStringUni(valuePtr) ?? throw new InvalidOperationException(); } finally { Marshal.ZeroFreeGlobalAllocUnicode(valuePtr); _password.Clear(); } } } if (_textBox != null) { _textBox.TextChanged += TextBox_TextChanged; } } public void Paste() { ActualPasswordBox.Paste(); if (ShowEyeButton && ShowPassword) { _textBox.Text = ActualPasswordBox.Password; } } public void SelectAll() { ActualPasswordBox.SelectAll(); if (ShowEyeButton && ShowPassword) { _textBox.SelectAll(); } } public void Clear() { ActualPasswordBox.Clear(); _textBox.Clear(); } private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) { if (!IsSafeEnabled) { SetCurrentValue(UnsafePasswordProperty, ActualPasswordBox.Password); if (ShowPassword) { _textBox.Text = ActualPasswordBox.Password; } } } private void TextBox_TextChanged(object sender, TextChangedEventArgs e) { if (!IsSafeEnabled && ShowPassword) { Password = _textBox.Text; SetCurrentValue(UnsafePasswordProperty, Password); } } }