PasswordBox в UserControl - DataBinding работает, но поле выглядит пустым
Предварительная информация / вопрос: все, что размещено ниже, работает (привязка данных к строке PasswordBox), за исключением того, что мой PasswordBox выглядит пустым, даже если его содержимое корректно.
Детали: у меня есть UserControl с PasswordBox и DependencyProperty. Этот DP может использоваться для установки / извлечения SecureString из этого PWBox через привязку данных.
public partial class PasswordUserControl : UserControl
{
public SecureString Password
{
get { return (SecureString)GetValue(PasswordProperty); }
set { SetValue(PasswordProperty, value); }
}
// https://msdn.microsoft.com/en-us/library/ms750428(v=vs.110).aspx
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.Register("Password", typeof(SecureString), typeof(PasswordUserControl),
new PropertyMetadata(default(SecureString)));
public PasswordUserControl()
{
InitializeComponent();
PasswordEntryBox.PasswordChanged += (sender, args) => {
Password = ((PasswordBox)sender).SecurePassword; };
}
}
Простой XAML:
<UserControl x:Class="WpfClient.PasswordUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfClient"
mc:Ignorable="d"
d:SizeToContent="true">
<StackPanel>
<DockPanel>
<Label Content="Password" Margin="0,0,5,0" DockPanel.Dock="Left"/>
<PasswordBox Name="PasswordEntryBox" Width="200" DockPanel.Dock="Right" />
</DockPanel>
</StackPanel>
Простое использование этого элемента управления в другом месте:
<local:PasswordUserControl Password="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
"Пароль привязки" выглядит как SecureString:
public SecureString Password
{
get { return m_Password; }
set
{
m_Password = value;
RaisePropertyChanged("Password");
}
}
Вопрос: привязка данных, кажется, работает в обоих направлениях; Я получаю правильный ввод пользователя (сработали входы в систему). PropertyChanged также работает: для каждого добавленного символа я получаю обновление свойства.
Что не работает: фактический PasswordBox выглядит пустым. Если я сначала установил пароль в коде и посмотрел на элемент управления, он не отображает кучу анонимных символов круга для уже введенного текста с привязкой к данным (но сама строка верна - так говорит отладчик, и действительные входы в систему работают!). Если я ввожу текст пароля в поле вручную, я получаю анонимные символы для своей записи, но мой ввод не добавляется к существующей строке, которая предположительно связана с данными. Это, по-видимому, стирает связанную с данными строку и создает новую с нуля.
Что я делаю неправильно?
2 ответа
Чтобы вы могли видеть любые символы пароля в PasswordBox
вам нужно установить его Password
собственность на string
, Это означает, что вам нужно будет извлечь строку простого пароля из SecureString
объект.
Вы должны делать это всякий раз, когда Password
свойство зависимости установлено в новое SecureString
значение.
Вы можете добавить PropertyChangedCallback
к свойству зависимости и реализовать UserControl
следующее:
public partial class PasswordUserControl : UserControl
{
public SecureString Password
{
get { return (SecureString)GetValue(PasswordProperty); }
set { SetValue(PasswordProperty, value); }
}
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.Register("Password", typeof(SecureString), typeof(PasswordUserControl),
new PropertyMetadata(default(SecureString), new PropertyChangedCallback(OnPasswordChanged)));
public PasswordUserControl()
{
InitializeComponent();
PasswordEntryBox.PasswordChanged += PasswordEntryBox_PasswordChanged;
}
private bool _handleEvent = true;
private void PasswordEntryBox_PasswordChanged(object sender, RoutedEventArgs e)
{
if(_handleEvent)
Password = ((PasswordBox)sender).SecurePassword;
}
private static void OnPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SecureString ss = e.NewValue as SecureString;
if(ss != null)
{
PasswordUserControl control = d as PasswordUserControl;
if(control != null)
{
control._handleEvent = false;
control.PasswordEntryBox.Password = ConvertToUnsecureString(ss);
control._handleEvent = true;
}
}
}
public static string ConvertToUnsecureString(SecureString securePassword)
{
if (securePassword == null)
throw new ArgumentNullException("securePassword");
IntPtr unmanagedString = IntPtr.Zero;
try
{
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
return Marshal.PtrToStringUni(unmanagedString);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
}
}
}
LoginPassword.Password = Globals.credentials == null ? "": new NetworkCredential("", Globals.credentials.SecurePassword).Password;
просто поместите одну строку в файл xaml.cs. Назовите поле пароля для "LoginPassword".