UserControl 中的 PasswordBox - DataBinding 有效但框看起来是空的
PasswordBox in UserControl - DataBinding works but box looks empty
前期info/question:
下面发布的所有内容都有效(数据绑定 PasswordBox 字符串),除了我的 PasswordBox 看起来 是空的,即使它的内容是正确的。
详情:
我有一个带有 PasswordBox 和 DependencyProperty 的 UserControl。该 DP 可用于 set/retrieve 通过数据绑定来自该 PWBox 的 SecureString。
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}" />
"Binding Password"感觉变成了一个SecureString:
public SecureString Password
{
get { return m_Password; }
set
{
m_Password = value;
RaisePropertyChanged("Password");
}
}
问题:
数据绑定似乎是双向的;我得到正确的用户输入(触发登录工作)。 PropertyChanged 也有效:对于添加的每个字符,我都会得到一个 属性 更新。
什么不起作用:实际的 PasswordBox 看起来是空的。如果我先在代码中设置密码并查看控件,它不会为已经输入的数据绑定文本显示一堆匿名圆圈字符(但字符串本身是正确的 - 调试器如此说并且实际登录工作!)。如果我手动将密码文本输入框中,我确实会为我的输入获取匿名字符,但我的输入不会添加到假定数据绑定的现有字符串中。它似乎擦除数据绑定字符串并从头开始创建一个新字符串。
我做错了什么?
为了能够在 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".
前期info/question: 下面发布的所有内容都有效(数据绑定 PasswordBox 字符串),除了我的 PasswordBox 看起来 是空的,即使它的内容是正确的。
详情: 我有一个带有 PasswordBox 和 DependencyProperty 的 UserControl。该 DP 可用于 set/retrieve 通过数据绑定来自该 PWBox 的 SecureString。
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}" />
"Binding Password"感觉变成了一个SecureString:
public SecureString Password
{
get { return m_Password; }
set
{
m_Password = value;
RaisePropertyChanged("Password");
}
}
问题: 数据绑定似乎是双向的;我得到正确的用户输入(触发登录工作)。 PropertyChanged 也有效:对于添加的每个字符,我都会得到一个 属性 更新。
什么不起作用:实际的 PasswordBox 看起来是空的。如果我先在代码中设置密码并查看控件,它不会为已经输入的数据绑定文本显示一堆匿名圆圈字符(但字符串本身是正确的 - 调试器如此说并且实际登录工作!)。如果我手动将密码文本输入框中,我确实会为我的输入获取匿名字符,但我的输入不会添加到假定数据绑定的现有字符串中。它似乎擦除数据绑定字符串并从头开始创建一个新字符串。
我做错了什么?
为了能够在 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".