diff --git a/README.md b/README.md index 5d441e75..28797228 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,10 @@ Step 4:enjoy coding ## Latest examples +### PinBox + +![PinBox](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/PinBox.png) + ### Effects ![Effects](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/Effects.png) @@ -110,12 +114,12 @@ Step 4:enjoy coding ![Poptip](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/Poptip.gif) +## History publication + ### Divider ![Divider](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/Divider.png) -## History publication - ### GeometryAnimation ![GeometryAnimation](https://raw.githubusercontent.com/HandyOrg/HandyOrgResource/master/HandyControl/Resources/GeometryAnimation.gif) diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PasswordBoxDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PasswordBoxDemoCtl.xaml index 172bc51d..d6781b14 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PasswordBoxDemoCtl.xaml +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PasswordBoxDemoCtl.xaml @@ -13,8 +13,7 @@ - - + diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PinBoxDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PinBoxDemoCtl.xaml index d5175cbc..728c99a3 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PinBoxDemoCtl.xaml +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PinBoxDemoCtl.xaml @@ -4,6 +4,9 @@ xmlns:hc="https://handyorg.github.io/handycontrol" Background="{DynamicResource RegionBrush}"> - + + + + diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PinBoxDemoCtl.xaml.cs b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PinBoxDemoCtl.xaml.cs index dc8f65fd..02c0ca3a 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PinBoxDemoCtl.xaml.cs +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PinBoxDemoCtl.xaml.cs @@ -1,4 +1,7 @@ -namespace HandyControlDemo.UserControl +using System.Windows; +using HandyControl.Controls; + +namespace HandyControlDemo.UserControl { public partial class PinBoxDemoCtl { @@ -6,5 +9,13 @@ { InitializeComponent(); } + + private void PinBox_OnCompleted(object sender, RoutedEventArgs e) + { + if (e.OriginalSource is PinBox pinBox) + { + Growl.Info(pinBox.Password); + } + } } } diff --git a/src/Shared/HandyControl_Shared/Controls/Input/PasswordBox.cs b/src/Shared/HandyControl_Shared/Controls/Input/PasswordBox.cs index 1c2037aa..5fdd95be 100644 --- a/src/Shared/HandyControl_Shared/Controls/Input/PasswordBox.cs +++ b/src/Shared/HandyControl_Shared/Controls/Input/PasswordBox.cs @@ -60,8 +60,6 @@ namespace HandyControl.Controls private SecureString _password; - private char _passwordChar; - private System.Windows.Controls.TextBox _textBox; public PasswordBox() @@ -223,12 +221,6 @@ namespace HandyControl.Controls } } } - - if (_passwordChar != default(char)) - { - ActualPasswordBox.PasswordChar = _passwordChar; - _passwordChar = default; - } } if (_textBox != null) diff --git a/src/Shared/HandyControl_Shared/Controls/Input/PinBox.cs b/src/Shared/HandyControl_Shared/Controls/Input/PinBox.cs index addf3612..89fa3e43 100644 --- a/src/Shared/HandyControl_Shared/Controls/Input/PinBox.cs +++ b/src/Shared/HandyControl_Shared/Controls/Input/PinBox.cs @@ -1,10 +1,14 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; +using System.Security; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using HandyControl.Data; using HandyControl.Tools; +using HandyControl.Tools.Extension; namespace HandyControl.Controls { @@ -21,6 +25,10 @@ namespace HandyControl.Controls private bool _changed; + private bool _isInternalAction; + + private List _passwordList; + private RoutedEventHandler _passwordBoxsGotFocusEventHandler; private RoutedEventHandler _passwordBoxsPasswordChangedEventHandler; @@ -51,6 +59,8 @@ namespace HandyControl.Controls private void PasswordBoxsPasswordChanged(object sender, RoutedEventArgs e) { + if (_isInternalAction) return; + if (e.OriginalSource is System.Windows.Controls.PasswordBox passwordBox) { if (passwordBox.Password.Length > 0) @@ -88,6 +98,7 @@ namespace HandyControl.Controls if (e.OriginalSource is System.Windows.Controls.PasswordBox passwordBox) { _inputIndex = _panel.Children.IndexOf(passwordBox); + passwordBox.SelectAll(); } } @@ -103,7 +114,10 @@ namespace HandyControl.Controls return; } - FocusManager.SetFocusedElement(this, _panel.Children[_inputIndex]); + var passwordBox = _panel.Children[_inputIndex] as System.Windows.Controls.PasswordBox; + passwordBox?.SelectAll(); + + FocusManager.SetFocusedElement(this, passwordBox); } else if (e.Key == Key.Right) { @@ -113,7 +127,10 @@ namespace HandyControl.Controls return; } - FocusManager.SetFocusedElement(this, _panel.Children[_inputIndex]); + var passwordBox = _panel.Children[_inputIndex] as System.Windows.Controls.PasswordBox; + passwordBox?.SelectAll(); + + FocusManager.SetFocusedElement(this, passwordBox); } } @@ -129,6 +146,13 @@ namespace HandyControl.Controls if (e.Key == Key.Delete || e.Key == Key.Back) { + if (_panel.Children[_inputIndex] is System.Windows.Controls.PasswordBox passwordBox) + { + _isInternalAction = true; + passwordBox.Clear(); + _isInternalAction = false; + } + if (--_inputIndex < 0) { _inputIndex = 0; @@ -139,8 +163,66 @@ namespace HandyControl.Controls } } + public string Password + { + get + { + return string.Join("", _panel.Children.OfType().Select(item => item.Password)); + } + set + { + if (_panel == null) + { + _passwordList = new List(); + + if (value == null) + { + value = string.Empty; + } + + foreach (var item in value) + { + var secureString = new SecureString(); + secureString.AppendChar(item); + _passwordList.Add(secureString); + } + + return; + } + + var length = Length; + if (string.IsNullOrEmpty(value) || value.Length != length || value.Length != _panel.Children.Count) + { + _panel.Children.OfType().Do(item => item.Clear()); + } + else + { + _panel.Children.OfType().DoWithIndex((item, index) => item.Password = value[index].ToString()); + } + } + } + + /// + /// 掩码字符 + /// + public static readonly DependencyProperty PasswordCharProperty = + System.Windows.Controls.PasswordBox.PasswordCharProperty.AddOwner(typeof(PinBox), + new FrameworkPropertyMetadata('●')); + + public char PasswordChar + { + get => (char)GetValue(PasswordCharProperty); + set => SetValue(PasswordCharProperty, value); + } + public static readonly DependencyProperty LengthProperty = DependencyProperty.Register( - "Length", typeof(int), typeof(PinBox), new PropertyMetadata(MinLength, null, CoerceLength), ValidateHelper.IsInRangeOfPosInt); + "Length", typeof(int), typeof(PinBox), new PropertyMetadata(MinLength, OnLengthChanged, CoerceLength), ValidateHelper.IsInRangeOfPosInt); + + private static void OnLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctl = (PinBox) d; + ctl.UpdateItems(); + } private static object CoerceLength(DependencyObject d, object basevalue) => (int)basevalue < 4 ? MinLength : basevalue; @@ -179,9 +261,9 @@ namespace HandyControl.Controls public static readonly RoutedEvent CompletedEvent = EventManager.RegisterRoutedEvent("Completed", RoutingStrategy.Bubble, - typeof(EventHandler), typeof(PinBox)); + typeof(RoutedEventHandler), typeof(PinBox)); - public event EventHandler Completed + public event RoutedEventHandler Completed { add => AddHandler(CompletedEvent, value); remove => RemoveHandler(CompletedEvent, value); @@ -189,6 +271,8 @@ namespace HandyControl.Controls private void UpdateItems() { + if (_panel == null) return; + _panel.Children.Clear(); var length = Length; @@ -208,7 +292,8 @@ namespace HandyControl.Controls Margin = ItemMargin, Width = ItemWidth, Height = ItemHeight, - Padding = new Thickness() + Padding = default, + PasswordChar = PasswordChar }; } @@ -220,6 +305,34 @@ namespace HandyControl.Controls if (_panel != null) { UpdateItems(); + + var length = Length; + if (_passwordList != null && _passwordList.Count == length && _panel.Children.Count == length) + { + for (var i = 0; i < length; i++) + { + var password = _passwordList[i]; + if (password.Length > 0) + { + var valuePtr = IntPtr.Zero; + try + { + valuePtr = Marshal.SecureStringToGlobalAllocUnicode(password); + if (_panel.Children[i] is System.Windows.Controls.PasswordBox passwordBox) + { + passwordBox.Password = Marshal.PtrToStringUni(valuePtr) ?? throw new InvalidOperationException(); + } + } + finally + { + Marshal.ZeroFreeGlobalAllocUnicode(valuePtr); + password.Clear(); + } + } + } + + _passwordList.Clear(); + } } } } diff --git a/src/Shared/HandyControl_Shared/Tools/Extension/EnumerableExtension.cs b/src/Shared/HandyControl_Shared/Tools/Extension/EnumerableExtension.cs index 68232166..3d5685db 100644 --- a/src/Shared/HandyControl_Shared/Tools/Extension/EnumerableExtension.cs +++ b/src/Shared/HandyControl_Shared/Tools/Extension/EnumerableExtension.cs @@ -16,5 +16,16 @@ namespace HandyControl.Tools.Extension return enumerable; } + + public static IEnumerable DoWithIndex(this IEnumerable source, Action predicate) + { + var enumerable = source as IList ?? source.ToList(); + for (var i = 0; i < enumerable.Count; i++) + { + predicate.Invoke(enumerable[i], i); + } + + return enumerable; + } } } \ No newline at end of file