diff --git a/src/Wpf.Ui/Controls/PasswordBox/PasswordBox.cs b/src/Wpf.Ui/Controls/PasswordBox/PasswordBox.cs
index e4be9da26..e802d3796 100644
--- a/src/Wpf.Ui/Controls/PasswordBox/PasswordBox.cs
+++ b/src/Wpf.Ui/Controls/PasswordBox/PasswordBox.cs
@@ -16,8 +16,9 @@ namespace Wpf.Ui.Controls;
///
/// The modified password control.
///
-public class PasswordBox : Wpf.Ui.Controls.TextBox
+public partial class PasswordBox : Wpf.Ui.Controls.TextBox
{
+ private readonly PasswordHelper _passwordHelper;
private bool _lockUpdatingContents;
/// Identifies the dependency property.
@@ -112,6 +113,7 @@ public event RoutedEventHandler PasswordChanged
public PasswordBox()
{
_lockUpdatingContents = false;
+ _passwordHelper = new PasswordHelper(this);
}
///
@@ -228,47 +230,11 @@ private void UpdateTextContents(bool isTriggeredByTextInput)
}
var caretIndex = CaretIndex;
- var selectionIndex = SelectionStart;
- var currentPassword = Password ?? string.Empty;
- var newPasswordValue = currentPassword;
+ var newPasswordValue = _passwordHelper.GetPassword();
if (isTriggeredByTextInput)
{
- var currentText = Text;
- var newCharacters = currentText.Replace(PasswordChar.ToString(), string.Empty);
-
- if (currentText.Length < currentPassword.Length)
- {
- newPasswordValue = currentPassword.Remove(
- selectionIndex,
- currentPassword.Length - currentText.Length
- );
- }
-
- if (newCharacters.Length > 1)
- {
- var index = currentText.IndexOf(newCharacters[0]);
-
- newPasswordValue =
- index > newPasswordValue.Length - 1
- ? newPasswordValue + newCharacters
- : newPasswordValue.Insert(index, newCharacters);
- }
- else
- {
- for (int i = 0; i < currentText.Length; i++)
- {
- if (currentText[i] == PasswordChar)
- {
- continue;
- }
-
- newPasswordValue =
- currentText.Length == newPasswordValue.Length
- ? newPasswordValue.Remove(i, 1).Insert(i, currentText[i].ToString())
- : newPasswordValue.Insert(i, currentText[i].ToString());
- }
- }
+ newPasswordValue = _passwordHelper.GetNewPassword();
}
_lockUpdatingContents = true;
diff --git a/src/Wpf.Ui/Controls/PasswordBox/PasswordHelper.cs b/src/Wpf.Ui/Controls/PasswordBox/PasswordHelper.cs
new file mode 100644
index 000000000..64ed3fa0d
--- /dev/null
+++ b/src/Wpf.Ui/Controls/PasswordBox/PasswordHelper.cs
@@ -0,0 +1,118 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
+// All Rights Reserved.
+
+using System.Diagnostics;
+
+// ReSharper disable once CheckNamespace
+namespace Wpf.Ui.Controls;
+
+#pragma warning disable SA1601
+
+public partial class PasswordBox
+{
+ private class PasswordHelper
+ {
+ private readonly PasswordBox _passwordBox;
+ private string _currentText;
+ private string _newPasswordValue;
+ private string _currentPassword;
+
+ public PasswordHelper(PasswordBox passwordBox)
+ {
+ _passwordBox = passwordBox;
+ _currentText = string.Empty;
+ _newPasswordValue = string.Empty;
+ _currentPassword = string.Empty;
+ }
+
+ public string GetNewPassword()
+ {
+ _currentPassword = GetPassword();
+ _newPasswordValue = _currentPassword;
+ _currentText = _passwordBox.Text;
+ var selectionIndex = _passwordBox.SelectionStart;
+ var passwordChar = _passwordBox.PasswordChar;
+ var newCharacters = _currentText.Replace(passwordChar.ToString(), string.Empty);
+ bool isDeleted = false;
+
+ if (IsDeleteOption())
+ {
+ _newPasswordValue = _currentPassword.Remove(
+ selectionIndex,
+ _currentPassword.Length - _currentText.Length
+ );
+ isDeleted = true;
+ }
+
+ switch (newCharacters.Length)
+ {
+ case > 1:
+ {
+ var index = _currentText.IndexOf(newCharacters[0]);
+
+ _newPasswordValue =
+ index > _newPasswordValue.Length - 1
+ ? _newPasswordValue + newCharacters
+ : _newPasswordValue.Insert(index, newCharacters);
+ break;
+ }
+
+ case 1:
+ {
+ for (int i = 0; i < _currentText.Length; i++)
+ {
+ if (_currentText[i] == passwordChar)
+ {
+ continue;
+ }
+
+ UpdatePasswordWithInputCharacter(i, _currentText[i].ToString());
+ break;
+ }
+
+ break;
+ }
+
+ case 0 when !isDeleted:
+ {
+ // The input is a PasswordChar, which is to be inserted at the designated position.
+ int insertIndex = selectionIndex - 1;
+ UpdatePasswordWithInputCharacter(insertIndex, passwordChar.ToString());
+ break;
+ }
+ }
+
+ return _newPasswordValue;
+ }
+
+ private void UpdatePasswordWithInputCharacter(int insertIndex, string insertValue)
+ {
+ Debug.Assert(_currentText == _passwordBox.Text, "_currentText == _passwordBox.Text");
+
+ if (_currentText.Length == _newPasswordValue.Length)
+ {
+ // If it's a direct character replacement, remove the existing one before inserting the new one.
+ _newPasswordValue = _newPasswordValue.Remove(insertIndex, 1).Insert(insertIndex, insertValue);
+ }
+ else
+ {
+ _newPasswordValue = _newPasswordValue.Insert(insertIndex, insertValue);
+ }
+ }
+
+ private bool IsDeleteOption()
+ {
+ Debug.Assert(_currentText == _passwordBox.Text, "_currentText == _passwordBox.Text");
+ Debug.Assert(
+ _currentPassword == _passwordBox.Password,
+ "_currentPassword == _passwordBox.Password"
+ );
+
+ return _currentText.Length < _currentPassword.Length;
+ }
+
+ public string GetPassword() => _passwordBox.Password ?? string.Empty;
+ }
+}