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
+
+
+
### Effects

@@ -110,12 +114,12 @@ Step 4:enjoy coding

+## History publication
+
### Divider

-## History publication
-
### GeometryAnimation

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