如何绑定到MVVM中的密码框

我遇到了绑定到密码框的问题。 这似乎是一个安全风险,但我使用MVVM模式,所以我想绕过这一点。 我在这里find了一些有趣的代码(有没有人使用过这个或类似的东西?)

http://www.wpftutorial.net/PasswordBox.html

它在技术上看起来不错,但我不确定如何检索密码。

我基本上在我的LoginViewModel UsernamePassword属性。 Username很好,正在工作,因为它是一个TextBox

我使用上面的代码,并input这个

 <PasswordBox ff:PasswordHelper.Attach="True" ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/> 

当我把PasswordBox作为一个TextBoxBinding Path=Password ,我的LoginViewModel的属性被更新了。

我的代码很简单,基本上我有一个Command我的Button 。 当我按下它时, CanLogin被调用,如果返回true,它会调用Login
你可以看到我检查我的财产的Username在这里工作很好。

Login我发送给我的服务的UsernamePasswordUsername包含我的View数据但PasswordNull|Empty

 private DelegateCommand loginCommand; public string Username { get; set; } public string Password { get; set; } public ICommand LoginCommand { get { if (loginCommand == null) { loginCommand = new DelegateCommand( Login, CanLogin ); } return loginCommand; } } private bool CanLogin() { return !string.IsNullOrEmpty(Username); } private void Login() { bool result = securityService.IsValidLogin(Username, Password); if (result) { } else { } } 

这就是我正在做的

 <TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}" MinWidth="180" /> <PasswordBox ff:PasswordHelper.Attach="True" ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/> 

我有我的TextBox ,这是没有问题的,但在我的ViewModelPassword是空的。

我做错了什么或缺less一个步骤?

我把一个断点,足够肯定的代码进入静态辅助类,但它永远不会更新我的ViewModel Password

对不起,但你做错了。

人们应该在他们的眼皮上纹上以下的安全指引:
切勿将纯文本密码保存在内存中。

WPF / Silverlight PasswordBox不公开DP密码属性的原因是安全相关的。
如果WPF / Silverlight保持密码的DP,则需要框架保持密码本身在内存中不encryption。 这被认为是一个相当麻烦的安全攻击载体。 密码箱使用encryption的内存(各种),唯一的方法来访问密码是通过CLR属性。

我build议在访问PasswordBox.Password CLR属性时,不要将它放在任何variables中或作为任何属性的值。
保持您的密码在客户端机器上的纯文本RAM是一个安全禁止。
所以摆脱那个“公共string密码{get; set;}”你已经到了那里。

访问PasswordBox.Password时,只需将其发送出去,并尽快将其发送到服务器。 不要保持密码的价值,不要像任何其他客户端机器文本那样对待它。 不要在内存中保留明确的文本密码。

我知道这打破了MVVM模式,但你永远不应该绑定到PasswordBox.Password附加DP,将您的密码存储在ViewModel或任何其他类似的shenanigans。

如果你正在寻找一个超架构的解决scheme,这里有一个:
1.用一个返回密码明文的方法创buildIHavePassword接口。
2.让你的UserControl实现一个IHavePassword接口。
3.用您的IoC注册UserControl实例来实现IHavePassword接口。
4.当需要密码的服务器请求正在发生时,请致电您的IoC以获取IHavePassword实现,并且只能获得令人垂涎的密码。

只是我的承担。

– 贾斯汀

我的2美分:

我使用WPF和MVVM开发了一个典型的login对话框(用户和密码框,加上“确定”button)。 我通过简单地将PasswordBox控件本身作为parameter passing给附加到“Ok”button的命令来解决密码绑定问题。 所以在我看来:

 <PasswordBox Name="txtPassword" VerticalAlignment="Top" Width="120" /> <Button Content="Ok" Command="{Binding Path=OkCommand}" CommandParameter="{Binding ElementName=txtPassword}"/> 

而在ViewModel中,附加命令的Execute方法如下:

 void Execute(object parameter) { var passwordBox = parameter as PasswordBox; var password = passwordBox.Password; //Now go ahead and check the user name and password } 

这稍微违反了MVVM模式,因为现在ViewModel知道View是如何实现的,但是在那个特定的项目中我可以负担得起。 希望对某个人也有用。

也许我错过了一些东西,但是似乎这些解决scheme中的大部分都是过于复杂的东西,并且放弃了安全的做法。

此方法不违反MVVM模式并保持完整的安全性。 是的,从技术上讲它是代码隐藏,但它不过是一个“特殊情况”绑定。 ViewModel仍然不了解View的实现,在我的脑海里,如果你想把PasswordBox传递给ViewModel的话,我的想法就是这样。

Code Behind!=自动MVVM违规。 这一切都取决于你用它做什么。 在这种情况下,我们只是手动编码一个绑定,所以它被认为是UI实现的一部分,因此是可以的。

在ViewModel中,只是一个简单的属性。 我做了“只写”,因为不应该有任何理由需要从ViewModel外部检索它,但它不一定是。 请注意,它是一个SecureString,而不仅仅是一个string。

 public SecureString SecurePassword { private get; set; } 

在xaml中,您设置了一个PasswordChanged事件处理程序。

 <PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/> 

在后面的代码中:

 private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) { if (this.DataContext != null) { ((dynamic)this.DataContext).SecurePassword = ((PasswordBox)sender).SecurePassword; } } 

使用这种方法,您的密码始终保持在SecureString中,因此提供了最大的安全性。 如果你真的不关心安全性,或者你需要明文密码来获得需要它的下游方法(注意:大多数需要密码的.NET方法也支持SecureString选项,所以你可能并不需要明文密码即使你认为你这样做),你也可以直接使用Password属性。 喜欢这个:

(ViewModel属性)

 public string Password { private get; set; } 

(代码后面)

 private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) { if (this.DataContext != null) { ((dynamic)this.DataContext).Password = ((PasswordBox)sender).Password; } } 

如果你想保持强types的东西,你可以用你的ViewModel的接口replace(dynamic)转换。 但是,实际上,“正常的”数据绑定也不是强types的,所以它没有什么大不了的。

 private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) { if (this.DataContext != null) { ((IMyViewModel)this.DataContext).Password = ((PasswordBox)sender).Password; } } 

所以最好的世界 – 你的密码是安全的,你的ViewModel只是有一个属性像任何其他财产,你的视图是自我包含的,不需要外部引用。

你可以使用这个XAML:

 <PasswordBox Name="PasswordBox"> <i:Interaction.Triggers> <i:EventTrigger EventName="PasswordChanged"> <i:InvokeCommandAction Command="{Binding PasswordChangedCommand}" CommandParameter="{Binding ElementName=PasswordBox}"/> </i:EventTrigger> </i:Interaction.Triggers> </PasswordBox> 

而这个命令的执行方法是:

 private void ExecutePasswordChangedCommand(PasswordBox obj) { if (obj != null) Password = obj.Password; } 

这对我来说工作得很好。

 <Button Command="{Binding Connect}" CommandParameter="{Binding ElementName=MyPasswordBox}"/> 

不违反MVVM模式的一个简单的解决scheme是在ViewModel中引入一个事件(或委托)来获取密码。

ViewModel中

public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;

与这些EventArgs:

 class HarvestPasswordEventArgs : EventArgs { public string Password; } 

View中 ,订阅创buildViewModel的事件并填写密码值。

 _viewModel.HarvestPassword += (sender, args) => args.Password = passwordBox1.Password; 

ViewModel中 ,当你需要密码的时候,你可以触发事件并从那里获取密码:

 if (HarvestPassword == null) //bah return; var pwargs = new HarvestPasswordEventArgs(); HarvestPassword(this, pwargs); LoginHelpers.Login(Username, pwargs.Password); 

我在这里发布了一个GIST ,这是一个可绑定的密码框。

 using System.Windows; using System.Windows.Controls; namespace CustomControl { public class BindablePasswordBox : Decorator { /// <summary> /// The password dependency property. /// </summary> public static readonly DependencyProperty PasswordProperty; private bool isPreventCallback; private RoutedEventHandler savedCallback; /// <summary> /// Static constructor to initialize the dependency properties. /// </summary> static BindablePasswordBox() { PasswordProperty = DependencyProperty.Register( "Password", typeof(string), typeof(BindablePasswordBox), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnPasswordPropertyChanged)) ); } /// <summary> /// Saves the password changed callback and sets the child element to the password box. /// </summary> public BindablePasswordBox() { savedCallback = HandlePasswordChanged; PasswordBox passwordBox = new PasswordBox(); passwordBox.PasswordChanged += savedCallback; Child = passwordBox; } /// <summary> /// The password dependency property. /// </summary> public string Password { get { return GetValue(PasswordProperty) as string; } set { SetValue(PasswordProperty, value); } } /// <summary> /// Handles changes to the password dependency property. /// </summary> /// <param name="d">the dependency object</param> /// <param name="eventArgs">the event args</param> private static void OnPasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs eventArgs) { BindablePasswordBox bindablePasswordBox = (BindablePasswordBox) d; PasswordBox passwordBox = (PasswordBox) bindablePasswordBox.Child; if (bindablePasswordBox.isPreventCallback) { return; } passwordBox.PasswordChanged -= bindablePasswordBox.savedCallback; passwordBox.Password = (eventArgs.NewValue != null) ? eventArgs.NewValue.ToString() : ""; passwordBox.PasswordChanged += bindablePasswordBox.savedCallback; } /// <summary> /// Handles the password changed event. /// </summary> /// <param name="sender">the sender</param> /// <param name="eventArgs">the event args</param> private void HandlePasswordChanged(object sender, RoutedEventArgs eventArgs) { PasswordBox passwordBox = (PasswordBox) sender; isPreventCallback = true; Password = passwordBox.Password; isPreventCallback = false; } } } 

这个实现稍有不同。 您将密码框传递给ViewModel中View的View通过绑定,它不使用任何命令参数。 ViewModel保留视图的无知。 我有一个可以从SkyDrive下载的VB vs 2010项目。 Wpf MvvM PassWordBox Example.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511

我在Wpf MvvM应用程序中使用PasswordBox的方式非常简单,适用于我。 这并不意味着我认为这是正确的方式或最好的方式。 这只是使用PasswordBox和MvvM模式的一个实现。

Basicly你创build一个公共只读属性,视图可以绑定到一个PasswordBox(实际的控制)例如:

 Private _thePassWordBox As PasswordBox Public ReadOnly Property ThePassWordBox As PasswordBox Get If IsNothing(_thePassWordBox) Then _thePassWordBox = New PasswordBox Return _thePassWordBox End Get End Property 

我使用一个后台只是做属性的自我初始化。

然后从Xaml绑定ContentControl或控件容器的内容示例:

  <ContentControl Grid.Column="1" Grid.Row="1" Height="23" Width="120" Content="{Binding Path=ThePassWordBox}" HorizontalAlignment="Center" VerticalAlignment="Center" /> 

从那里你完全控制密码箱我也使用PasswordAccessor(只是一个string的函数)在login时返回密码值或任何你想要的密码。 在示例中,我在通用用户对象模型中有一个公共属性。 例:

 Public Property PasswordAccessor() As Func(Of String) 

在用户对象中,密码string属性是只读的,没有任何后备存储,只是从密码框返回密码。 例:

 Public ReadOnly Property PassWord As String Get Return If((PasswordAccessor Is Nothing), String.Empty, PasswordAccessor.Invoke()) End Get End Property 

然后在ViewModel中,我确保Accessor被创build并设置为PasswordBox.Password属性“Example:

 Public Sub New() 'Sets the Accessor for the Password Property SetPasswordAccessor(Function() ThePassWordBox.Password) End Sub Friend Sub SetPasswordAccessor(ByVal accessor As Func(Of String)) If Not IsNothing(VMUser) Then VMUser.PasswordAccessor = accessor End Sub 

当我需要密码string说login时,我只是得到用户对象的密码属性,真正调用函数来获取密码并返回它,然后实际的密码不是由用户对象存储。 例如:将在ViewModel中

 Private Function LogIn() as Boolean 'Make call to your Authentication methods and or functions. I usally place that code in the Model Return AuthenticationManager.Login(New UserIdentity(User.UserName, User.Password) End Function 

这应该做到这一点。 ViewModel不需要任何有关视图控件的知识。 视图只是绑定到ViewModel中的属性,与视图绑定到图像或其他资源没有任何区别。 在这种情况下,资源(Property)恰好是一个用户控件。 它允许testingViewModel创build并拥有该属性,该属性独立于视图。 至于安全性,我不知道这个实现有多好。 但是通过使用一个函数,该值不会被存储在属性本身中,只是被属性访问。

为了在不中断MVVM的情况下解决OP问题,我将使用自定义值转换器和一个包装器来获取密码框中的值(密码)。

 public interface IWrappedParameter<T> { T Value { get; } } public class PasswordBoxWrapper : IWrappedParameter<string> { private readonly PasswordBox _source; public string Value { get { return _source != null ? _source.Password : string.Empty; } } public PasswordBoxWrapper(PasswordBox source) { _source = source; } } public class PasswordBoxConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // Implement type and value check here... return new PasswordBoxWrapper((PasswordBox)value); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new InvalidOperationException("No conversion."); } } 

在视图模型中:

 public string Username { get; set; } public ICommand LoginCommand { get { return new RelayCommand<IWrappedParameter<string>>(password => { Login(Username, password); }); } } private void Login(string username, string password) { // Perform login here... } 

因为视图模型使用了IWrappedParameter<T> ,所以不需要对PasswordBoxWrapperPasswordBoxConverter有任何的了解。 通过这种方式,您可以将video模型中的PasswordBox对象与video模型隔离,而不会中断MVVM模式。

在看法:

 <Window.Resources> <h:PasswordBoxConverter x:Key="PwdConverter" /> </Window.Resources> ... <PasswordBox Name="PwdBox" /> <Button Content="Login" Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=PwdBox, Converter={StaticResource PwdConverter}}" /> 

虽然我同意避免在任何地方存储密码是很重要的,但是我仍然需要能够在没有视图的情况下实例化视图模型,并且执行我的testing。

我的解决scheme是注册PasswordBox.Password函数与视图模型,并让视图模型在执行login代码时调用它。

确实意味着视图代码隐藏中的一行代码。

所以,在我的Login.xaml中

 <PasswordBox x:Name="PasswordBox"/> 

并在Login.xaml.cs我有

 LoginViewModel.PasswordHandler = () => PasswordBox.Password; 

然后在LoginViewModel.cs中我定义了PasswordHandler

 public Func<string> PasswordHandler { get; set; } 

当login需要发生的代码调用处理程序从视图中获取密码…

 bool loginResult = Login(Username, PasswordHandler()); 

这样,当我想testing视图模型时,我可以简单地将PasswordHandler设置为一个匿名方法,让我提供我想在testing中使用的任何密码。

我用这个方法,并通过密码框,虽然这确实违反了MVVM,这对我来说是必不可less的,因为我在我的shell中使用了一个带有数据模板的内容控件,这是一个复杂的shell环境。 因此,访问shell后面的代码将是废话。

我认为通过密码箱就像从后面的代码访问控制一样。 我同意密码,不保留在内存等在这个实现我没有视图模型中的密码属性。

button命令

 Command="{Binding Path=DataContext.LoginCommand, ElementName=MyShell}" CommandParameter="{Binding ElementName=PasswordBox}" 

视图模型

 private void Login(object parameter) { System.Windows.Controls.PasswordBox p = (System.Windows.Controls.PasswordBox)parameter; MessageBox.Show(p.Password); } 

我花了很多时间寻找各种解决scheme。 我不喜欢装饰者的想法,行为搞乱validation用户界面,后面的代码…真的吗?

最好的方法是坚持自定义附加属性,并绑定到您的视图模型中的SecureString属性。 尽可能长时间保持在那里。 无论何时您需要快速访问普通密码,请使用以下代码临时将其转换为不安全的string:

 namespace Namespace.Extensions { using System; using System.Runtime.InteropServices; using System.Security; /// <summary> /// Provides unsafe temporary operations on secured strings. /// </summary> [SuppressUnmanagedCodeSecurity] public static class SecureStringExtensions { /// <summary> /// Converts a secured string to an unsecured string. /// </summary> public static string ToUnsecuredString(this SecureString secureString) { // copy&paste from the internal System.Net.UnsafeNclNativeMethods IntPtr bstrPtr = IntPtr.Zero; if (secureString != null) { if (secureString.Length != 0) { try { bstrPtr = Marshal.SecureStringToBSTR(secureString); return Marshal.PtrToStringBSTR(bstrPtr); } finally { if (bstrPtr != IntPtr.Zero) Marshal.ZeroFreeBSTR(bstrPtr); } } } return string.Empty; } /// <summary> /// Copies the existing instance of a secure string into the destination, clearing the destination beforehand. /// </summary> public static void CopyInto(this SecureString source, SecureString destination) { destination.Clear(); foreach (var chr in source.ToUnsecuredString()) { destination.AppendChar(chr); } } /// <summary> /// Converts an unsecured string to a secured string. /// </summary> public static SecureString ToSecuredString(this string plainString) { if (string.IsNullOrEmpty(plainString)) { return new SecureString(); } SecureString secure = new SecureString(); foreach (char c in plainString) { secure.AppendChar(c); } return secure; } } } 

确保你允许GC收集你的UI元素,所以要抵制在PasswordChanged上使用PasswordBox事件的静态事件处理程序的冲动。 我还发现了一个exception,在使用SecurePassword属性进行设置时控件没有更新UI,原因是我将密码复制到Password

 namespace Namespace.Controls { using System.Security; using System.Windows; using System.Windows.Controls; using Namespace.Extensions; /// <summary> /// Creates a bindable attached property for the <see cref="PasswordBox.SecurePassword"/> property. /// </summary> public static class PasswordBoxHelper { // an attached behavior won't work due to view model validation not picking up the right control to adorn public static readonly DependencyProperty SecurePasswordBindingProperty = DependencyProperty.RegisterAttached( "SecurePassword", typeof(SecureString), typeof(PasswordBoxHelper), new FrameworkPropertyMetadata(new SecureString(),FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AttachedPropertyValueChanged) ); private static readonly DependencyProperty _passwordBindingMarshallerProperty = DependencyProperty.RegisterAttached( "PasswordBindingMarshaller", typeof(PasswordBindingMarshaller), typeof(PasswordBoxHelper), new PropertyMetadata() ); public static void SetSecurePassword(PasswordBox element, SecureString secureString) { element.SetValue(SecurePasswordBindingProperty, secureString); } public static SecureString GetSecurePassword(PasswordBox element) { return element.GetValue(SecurePasswordBindingProperty) as SecureString; } private static void AttachedPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // we'll need to hook up to one of the element's events // in order to allow the GC to collect the control, we'll wrap the event handler inside an object living in an attached property // don't be tempted to use the Unloaded event as that will be fired even when the control is still alive and well (eg switching tabs in a tab control) var passwordBox = (PasswordBox)d; var bindingMarshaller = passwordBox.GetValue(_passwordBindingMarshallerProperty) as PasswordBindingMarshaller; if (bindingMarshaller == null) { bindingMarshaller = new PasswordBindingMarshaller(passwordBox); passwordBox.SetValue(_passwordBindingMarshallerProperty, bindingMarshaller); } bindingMarshaller.UpdatePasswordBox(e.NewValue as SecureString); } /// <summary> /// Encapsulated event logic /// </summary> private class PasswordBindingMarshaller { private readonly PasswordBox _passwordBox; private bool _isMarshalling; public PasswordBindingMarshaller(PasswordBox passwordBox) { _passwordBox = passwordBox; _passwordBox.PasswordChanged += this.PasswordBoxPasswordChanged; } public void UpdatePasswordBox(SecureString newPassword) { if (_isMarshalling) { return; } _isMarshalling = true; try { // setting up the SecuredPassword won't trigger a visual update so we'll have to use the Password property _passwordBox.Password = newPassword.ToUnsecuredString(); // you may try the statement below, however the benefits are minimal security wise (you still have to extract the unsecured password for copying) //newPassword.CopyInto(_passwordBox.SecurePassword); } finally { _isMarshalling = false; } } private void PasswordBoxPasswordChanged(object sender, RoutedEventArgs e) { // copy the password into the attached property if (_isMarshalling) { return; } _isMarshalling = true; try { SetSecurePassword(_passwordBox, _passwordBox.SecurePassword.Copy()); } finally { _isMarshalling = false; } } } } } 

和XAML的用法:

 <PasswordBox controls:PasswordBoxHelper.SecurePassword="{Binding LogonPassword, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"> 

我在视图模型中的属性如下所示:

 [RequiredSecureString] public SecureString LogonPassword { get { return _logonPassword; } set { _logonPassword = value; NotifyPropertyChanged(nameof(LogonPassword)); } } 

RequiredSecureString只是一个简单的自定义validation器,具有以下逻辑:

 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] public class RequiredSecureStringAttribute:ValidationAttribute { public RequiredSecureStringAttribute() :base("Field is required") { } public override bool IsValid(object value) { return (value as SecureString)?.Length > 0; } } 

在这里,你有它。 完整且经过testing的纯MVVM解决scheme。

你可以使用附加属性来做到这一点,看看它.. 密码框与MVVM

我想我会把我的解决scheme放在组合中,因为这是一个很普遍的问题……有很多select总是一件好事。

我简单地在UserControl包装了一个PasswordBox ,并实现了一个DependencyProperty来绑定。 我尽我所能避免在内存中存储任何明文,所以一切都通过SecureStringPasswordBox.Password属性完成。 在foreach循环中,每个字符都会暴露,但是非常简短。 老实说,如果你担心你的WPF应用程序被这个短暂的曝光所损害,那么你应该处理更大的安全问题。

这样做的好处在于,您不会违反任何MVVM规则,即使是“纯粹”规则,因为这是一个UserControl ,所以允许使用代码隐藏。 当您使用它时,您可以在ViewViewModel之间进行纯粹的通信,而不需要您的VideModel知道View的任何部分或密码的来源。 只要确保你在ViewModel绑定了SecureString

BindablePasswordBox.xaml

 <UserControl x:Class="BK.WPF.CustomControls.BindanblePasswordBox" 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" mc:Ignorable="d" d:DesignHeight="22" d:DesignWidth="150"> <PasswordBox x:Name="PswdBox"/> </UserControl> 

BindablePasswordBox.xaml.cs(版本1 – 不支持双向绑定)

 using System.ComponentModel; using System.Security; using System.Windows; using System.Windows.Controls; namespace BK.WPF.CustomControls { public partial class BindanblePasswordBox : UserControl { public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(SecureString), typeof(BindanblePasswordBox)); public SecureString Password { get { return (SecureString)GetValue(PasswordProperty); } set { SetValue(PasswordProperty, value); } } public BindanblePasswordBox() { InitializeComponent(); PswdBox.PasswordChanged += PswdBox_PasswordChanged; } private void PswdBox_PasswordChanged(object sender, RoutedEventArgs e) { var secure = new SecureString(); foreach (var c in PswdBox.Password) { secure.AppendChar(c); } Password = secure; } } } 

Usage of Version 1:

 <local:BindanblePasswordBox Width="150" HorizontalAlignment="Center" VerticalAlignment="Center" Password="{Binding Password, Mode=OneWayToSource}"/> 

BindablePasswordBox.xaml.cs (Version 2 – Has two-way binding support.)

 public partial class BindablePasswordBox : UserControl { public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(SecureString), typeof(BindablePasswordBox), new PropertyMetadata(PasswordChanged)); public SecureString Password { get { return (SecureString)GetValue(PasswordProperty); } set { SetValue(PasswordProperty, value); } } public BindablePasswordBox() { InitializeComponent(); PswdBox.PasswordChanged += PswdBox_PasswordChanged; } private void PswdBox_PasswordChanged(object sender, RoutedEventArgs e) { var secure = new SecureString(); foreach (var c in PswdBox.Password) { secure.AppendChar(c); } if (Password != secure) { Password = secure; } } private static void PasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var pswdBox = d as BindablePasswordBox; if (pswdBox != null && e.NewValue != e.OldValue) { var newValue = e.NewValue as SecureString; if (newValue == null) { return; } var unmanagedString = IntPtr.Zero; string newString; try { unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(newValue); newString = Marshal.PtrToStringUni(unmanagedString); } finally { Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString); } var currentValue = pswdBox.PswdBox.Password; if (currentValue != newString) { pswdBox.PswdBox.Password = newString; } } } } 

Usage of Version 2:

 <local:BindanblePasswordBox Width="150" HorizontalAlignment="Center" VerticalAlignment="Center" Password="{Binding Password, Mode=TwoWay}"/> 

In windows universal app

you can use this code with the property "Password" and binding with the modelView

  <PasswordBox x:Uid="PasswordBox" Password="{Binding Waiter.Password, Mode=TwoWay}" Name="txtPassword" HorizontalAlignment="Stretch" Margin="50,200,50,0" VerticalAlignment="Top"/> 

Its very simple . Create another property for password and Bind this with TextBox

But all input operations perform with actual password property

private string _Password;

  public string PasswordChar { get { string szChar = ""; foreach(char szCahr in _Password) { szChar = szChar + "*"; } return szChar; } set { _PasswordChar = value; NotifyPropertyChanged(); } } 

public string Password { get { return _Password; }

  set { _Password = value; NotifyPropertyChanged(); PasswordChar = _Password; } } 

For anyone who is aware of the risks this implementation imposes, to have the password sync to your ViewModel simply add Mode=OneWayToSource .

XAML

 <PasswordBox ff:PasswordHelper.Attach="True" ff:PasswordHelper.Password="{Binding Path=Password, Mode=OneWayToSource}" /> 

As you can see i am binding to Password, but maybe its bind it to the static class..

It is an attached property . This kind of property can be applied to any kind of DependencyObject , not just the type in which it is declared. So even though it is declared in the PasswordHelper static class, it is applied to the PasswordBox on which you use it.

To use this attached property, you just need to bind it to the Password property in your ViewModel :

 <PasswordBox w:PasswordHelper.Attach="True" w:PasswordHelper.Password="{Binding Password}"/> 

As mentioned before VM should be unaware of the View but passing whole PasswordBox looks like the simplest approach. So maybe instead of casting passed parameter to PasswordBox use Reflection to extract Password property from it. In this case VM expects some kind of Password Container with property Password(I'm ussing RelayCommands from MVMM Light-Toolkit):

 public RelayCommand<object> SignIn { get { if (this.signIn == null) { this.signIn = new RelayCommand<object>((passwordContainer) => { var password = passwordContainer.GetType().GetProperty("Password").GetValue(passwordContainer) as string; this.authenticationService.Authenticate(this.Login, password); }); } return this.signIn; } } 

It can be easily tested with anonymous class:

 var passwordContainer = new { Password = "password" }; 

You find a solution for the PasswordBox in the ViewModel sample application of the WPF Application Framework (WAF) project.

However, Justin is right. Don't pass the password as plain text between View and ViewModel. Use SecureString instead (See MSDN PasswordBox).

I have done like:

XAML:

 <PasswordBox x:Name="NewPassword" PasswordChanged="NewPassword_PasswordChanged"/> <!--change tablenameViewSource: yours!--> <Grid DataContext="{StaticResource tablenameViewSource}" Visibility="Hidden"> <TextBox x:Name="Password" Text="{Binding password, Mode=TwoWay}"/> </Grid> 

C#:

 private void NewPassword_PasswordChanged(object sender, RoutedEventArgs e) { try { //change tablenameDataTable: yours! and tablenameViewSource: yours! tablenameDataTable.Rows[tablenameViewSource.View.CurrentPosition]["password"] = NewPassword.Password; } catch { this.Password.Text = this.NewPassword.Password; } } 

It works for me!

I used an authentication check followed by a sub called by a mediator class to the View (which also implements an authentication check) to write the password to the data class.

It's not a perfect solution; however, it remedied my problem of not being able to move the password.

I am using succinct MVVM-friendly solution that hasn't been mentioned yet. First, I name the PasswordBox in XAML:

 <PasswordBox x:Name="Password" /> 

Then I add a single method call into view constructor:

 public LoginWindow() { InitializeComponent(); ExposeControl<LoginViewModel>.Expose(this, view => view.Password, (model, box) => model.SetPasswordBox(box)); } 

就是这样。 View model will receive notification when it is attached to a view via DataContext and another notification when it is detached. The contents of this notification are configurable via the lambdas, but usually it's just a setter or method call on the view model, passing the problematic control as a parameter.

It can be made MVVM-friendly very easily by having the view expose interface instead of child controls.

The above code relies on helper class published on my blog.

I spent ages trying to get this working. In the end, I gave up and just used the PasswordBoxEdit from DevExpress.

It is the simplest solution ever, as it allows binding without pulling any horrible tricks.

Solution on DevExpress website

For the record, I am not affiliated with DevExpress in any way.

To me, both of these things feel wrong:

  • Implementing clear text password properties
  • Sending the PasswordBox as a command parameter to the ViewModel

Transferring the SecurePassword (SecureString instance) as described by Steve in CO seems acceptable. I prefer Behaviors to code behind, and I also had the additional requirement of being able to reset the password from the viewmodel.

Xaml ( Password is the ViewModel property):

 <PasswordBox> <i:Interaction.Behaviors> <behaviors:PasswordBinding BoundPassword="{Binding Password, Mode=TwoWay}" /> </i:Interaction.Behaviors> </PasswordBox> 

行为:

 using System.Security; using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; namespace Evidence.OutlookIntegration.AddinLogic.Behaviors { /// <summary> /// Intermediate class that handles password box binding (which is not possible directly). /// </summary> public class PasswordBoxBindingBehavior : Behavior<PasswordBox> { // BoundPassword public SecureString BoundPassword { get { return (SecureString)GetValue(BoundPasswordProperty); } set { SetValue(BoundPasswordProperty, value); } } public static readonly DependencyProperty BoundPasswordProperty = DependencyProperty.Register("BoundPassword", typeof(SecureString), typeof(PasswordBoxBindingBehavior), new FrameworkPropertyMetadata(OnBoundPasswordChanged)); protected override void OnAttached() { this.AssociatedObject.PasswordChanged += AssociatedObjectOnPasswordChanged; base.OnAttached(); } /// <summary> /// Link up the intermediate SecureString (BoundPassword) to the UI instance /// </summary> private void AssociatedObjectOnPasswordChanged(object s, RoutedEventArgs e) { this.BoundPassword = this.AssociatedObject.SecurePassword; } /// <summary> /// Reacts to password reset on viewmodel (ViewModel.Password = new SecureString()) /// </summary> private static void OnBoundPasswordChanged(object s, DependencyPropertyChangedEventArgs e) { var box = ((PasswordBoxBindingBehavior)s).AssociatedObject; if (box != null) { if (((SecureString)e.NewValue).Length == 0) box.Password = string.Empty; } } } } 
 <UserControl x:Class="Elections.Server.Handler.Views.LoginView" 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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:cal="http://www.caliburnproject.org" mc:Ignorable="d" Height="531" Width="1096"> <ContentControl> <ContentControl.Background> <ImageBrush/> </ContentControl.Background> <Grid > <Border BorderBrush="#FFABADB3" BorderThickness="1" HorizontalAlignment="Left" Height="23" Margin="900,100,0,0" VerticalAlignment="Top" Width="160"> <TextBox TextWrapping="Wrap"/> </Border> <Border BorderBrush="#FFABADB3" BorderThickness="1" HorizontalAlignment="Left" Height="23" Margin="900,150,0,0" VerticalAlignment="Top" Width="160"> <PasswordBox x:Name="PasswordBox"/> </Border> <Button Content="Login" HorizontalAlignment="Left" Margin="985,200,0,0" VerticalAlignment="Top" Width="75"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <cal:ActionMessage MethodName="Login"> <cal:Parameter Value="{Binding ElementName=PasswordBox}" /> </cal:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </Button> </Grid> </ContentControl> </UserControl> 

well my answerd is more simple just in the for the MVVM pattern

in class viewmodel

 public string password; PasswordChangedCommand = new DelegateCommand<RoutedEventArgs>(PasswordChanged); Private void PasswordChanged(RoutedEventArgs obj) { var e = (WatermarkPasswordBox)obj.OriginalSource; //or depending or what are you using var e = (PasswordBox)obj.OriginalSource; password =e.Password; } 

the password property of the PasswordBox that win provides or WatermarkPasswordBox that XCeedtoolkit provides generates an RoutedEventArgs so you can bind it.

now in xmal view

 <Xceed:WatermarkPasswordBox Watermark="Input your Password" Grid.Column="1" Grid.ColumnSpan="3" Grid.Row="7" PasswordChar="*" > <i:Interaction.Triggers> <i:EventTrigger EventName="PasswordChanged"> <prism:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path= DataContext.PasswordChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path= Password}"/> </i:EventTrigger> </i:Interaction.Triggers> </Xceed:WatermarkPasswordBox> 

要么

 <PasswordBox Grid.Column="1" Grid.ColumnSpan="3" Grid.Row="7" PasswordChar="*" > <i:Interaction.Triggers> <i:EventTrigger EventName="PasswordChanged"> <prism:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path= DataContext.PasswordChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path= Password}"/> </i:EventTrigger> </i:Interaction.Triggers> </PasswordBox>