Как связать с паролем в MVVM

Я столкнулся с проблемой привязки к PasswordBox. Кажется, это угроза безопасности, но я использую шаблон MVVM, поэтому я хочу обойти это. Я нашел здесь интересный код (кто-нибудь использовал это или что-то подобное?)

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

Это технически выглядит отлично, но я не уверен, как получить пароль.

У меня в основном есть свойства в моем LoginViewModel для имени Username и Password . Username прекрасно, и оно работает как TextBox .

Я использовал код выше, как указано, и ввел это

  

Когда у меня был PasswordBox как TextBox и Binding Path=Password тогда свойство в моем LoginViewModel было обновлено.

Мой код очень прост, в основном у меня есть Command для моей Button . Когда я CanLogin вызывается CanLogin и если он возвращает true, он вызывает Login .
Вы можете увидеть, что я проверяю свою собственность на Username которое отлично работает.

В Login я посылаю к моей службе Username и Password , Username содержит данные из моего 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 , это не проблема, но в моей модели ViewModel Password пуст.

Я что-то делаю неправильно или пропустил шаг?

Я поставил точку останова и достаточно уверен, что код входит в статический вспомогательный class, но он никогда не обновляет мой Password в моей ViewModel .

    Извините, но вы делаете это неправильно.

    У людей должно быть следующее правило безопасности, наносимое татуировкой внутри их век:
    Никогда не храните в памяти простые текстовые пароли.

    Причина, по которой WPF / Silverlight PasswordBox не предоставляет DP для свойства Password, связана с безопасностью.
    Если WPF / Silverlight должен был хранить DP для пароля, для этого потребовалось бы, чтобы фреймворк сохранял сам пароль незашифрованным в памяти. Это считается довольно сложным вектором атаки безопасности. В PasswordBox используется зашифрованная память (сортировка), и единственный способ доступа к паролю – это свойство CLR.

    Я бы предположил, что при доступе к свойству CLB PasswordBox.Password вы воздерживаетесь от его размещения в любой переменной или в качестве значения для любого свойства.
    Сохранение вашего пароля в текстовом виде на ОЗУ клиента является безопасным no-no.
    Поэтому избавитесь от этой «общедоступной строки Password {get; set;}», которую вы получили там.

    При доступе к паролю PasswordBox.Password просто получите его и отправьте на сервер как можно скорее. Не храните значение пароля и не рассматривайте его так же, как и любой другой текст машинного компьютера. Не храните в памяти четкие текстовые пароли.

    Я знаю, что это нарушает шаблон MVVM, но вы никогда не должны привязываться к PasswordBox.Password Attached DP, хранить свой пароль в ViewModel или любых других подобных махинациях.

    Если вы ищете перенастроенное решение, выполните одно из следующих действий:
    1. Создайте интерфейс IHavePassword с помощью одного метода, который возвращает текстовый текст с паролем.
    2. Попросите свой UserControl реализовать интерфейс IHavePassword.
    3. Зарегистрируйте экземпляр UserControl с помощью IoC в качестве реализации интерфейса IHavePassword.
    4. Когда выполняется запрос сервера, требующий вашего пароля, вызовите IoC для реализации IHavePassword и получите только тот же самый желанный пароль.

    Просто я возьму его.

    – Джастин

    Мои 2 цента:

    Я разработал один раз типичный диалог входа (поля пользователя и пароля, плюс кнопка «ОК») с использованием WPF и MVVM. Я решил проблему привязки пароля, просто передав сам элемент управления PasswordBox в качестве параметра в команду, прикрепленную к кнопке «ОК». Так что, по моему мнению,

       

    И в 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 знает что-то о том, как реализуется представление, но в этом конкретном проекте я мог себе это позволить. Надеюсь, это полезно и для кого-то.

    Может быть, мне что-то не хватает, но похоже, что большинство из этих решений слишком усложняют ситуацию и устраняют безопасные методы.

    Этот метод не нарушает шаблон MVVM и сохраняет полную безопасность. Да, технически это код позади, но это не что иное, как привязка «специального случая». ViewModel до сих пор не знает о реализации View, которая, на мой взгляд, делает это, если вы пытаетесь передать PasswordBox в ViewModel.

    Code Behind! = Автоматическое нарушение MVVM. Все зависит от того, что вы с ним делаете. В этом случае мы просто кодируем привязку вручную, поэтому его рассматриваемая часть реализации пользовательского интерфейса и, следовательно, в порядке.

    В ViewModel просто свойство. Я сделал это «писать только», потому что по какой-то причине не нужно будет извлекать его извне ViewModel, но это необязательно. Обратите внимание, что это SecureString, а не только строка.

     public SecureString SecurePassword { private get; set; } 

    В xaml вы настроили обработчик события 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; } } 

    Если вы хотите, чтобы все было строго напечатано, вы можете подставить (динамический) листинг с помощью интерфейса ViewModel. Но на самом деле, «нормальные» привязки данных не являются строго типизированными, так что это не такая большая сделка.

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

    Итак, лучший из всех миров – ваш пароль защищен, ваш ViewModel просто обладает свойством, как и любое другое свойство, и ваш вид сам по себе не требует внешних ссылок.

    Вы можете использовать этот XAML:

            

    И эта команда выполняет метод:

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

    Это прекрасно работает для меня.

      

    Простым решением, не нарушающим шаблон MVVM, является представление события (или делегата) в ViewModel, который собирает пароль.

    В ViewModel :

    public event EventHandler HarvestPassword;

    с этими EventArgs:

     class HarvestPasswordEventArgs : EventArgs { public string Password; } 

    в представлении , подпишитесь на мероприятие по созданию ViewModel и заполните значение пароля.

     _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 { ///  /// The password dependency property. ///  public static readonly DependencyProperty PasswordProperty; private bool isPreventCallback; private RoutedEventHandler savedCallback; ///  /// Static constructor to initialize the dependency properties. ///  static BindablePasswordBox() { PasswordProperty = DependencyProperty.Register( "Password", typeof(string), typeof(BindablePasswordBox), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnPasswordPropertyChanged)) ); } ///  /// Saves the password changed callback and sets the child element to the password box. ///  public BindablePasswordBox() { savedCallback = HandlePasswordChanged; PasswordBox passwordBox = new PasswordBox(); passwordBox.PasswordChanged += savedCallback; Child = passwordBox; } ///  /// The password dependency property. ///  public string Password { get { return GetValue(PasswordProperty) as string; } set { SetValue(PasswordProperty, value); } } ///  /// Handles changes to the password dependency property. ///  /// the dependency object /// the event args 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; } ///  /// Handles the password changed event. ///  /// the sender /// the event args private void HandlePasswordChanged(object sender, RoutedEventArgs eventArgs) { PasswordBox passwordBox = (PasswordBox) sender; isPreventCallback = true; Password = passwordBox.Password; isPreventCallback = false; } } } 

    Эта реализация несколько отличается. Вы передаете пароль в представление через привязку свойства в ViewModel, он не использует никаких параметров команды. ViewModel остается неизвестным в представлении. У меня есть проект VB vs 2010, который можно загрузить с SkyDrive. Wpf MvvM PassWordBox Example.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511

    То, как я использую PasswordBox в приложении Wpf MvvM, довольно упрощенно и хорошо работает для меня. Это не значит, что я думаю, что это правильный путь или лучший способ. Это всего лишь реализация использования PasswordBox и шаблона MvvM.

    В основном вы создаете общедоступное свойство readonly, которое View может связывать как 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 или контейнера управления. Пример:

       

    Оттуда у вас есть полный контроль над паролем. Я также использую PasswordAccessor (Just a Function of String), чтобы вернуть значение пароля при входе в систему или в любом другом месте, в котором вы хотите использовать пароль. В Примере я имею общедоступное свойство в общей пользовательской объектной модели. Пример:

     Public Property PasswordAccessor() As Func(Of String) 

    В пользовательском объекте свойство строки пароля доступно только для чтения без какого-либо резервного хранилища, оно просто возвращает пароль из PasswordBox. Пример:

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

    Затем в ViewModel я удостоверяюсь, что Accessor создан и установлен в свойство PasswordBox.Password. Пример:

     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 

    Когда мне понадобится строка «Пароль» для входа в систему, я просто получаю свойство «Пользовательские объекты», которое действительно вызывает функцию, чтобы захватить пароль и вернуть его, а фактический пароль не сохраняется в пользовательском объекте. Пример: будет в 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 не нуждается в каких-либо знаниях элементов управления View. Объект View Just привязывается к свойству в ViewModel, но не к какому-либо другому, чем представление привязки к изображению или другому ресурсу. В этом случае этот ресурс (Свойство) просто является usercontrol. Он позволяет тестировать, поскольку ViewModel создает и владеет Свойством, а свойство не зависит от представления. Что касается безопасности, я не знаю, насколько хороша эта реализация. Но, используя функцию, значение не сохраняется в самом Свойстве, доступ к которому осуществляется только с помощью свойства.

    Чтобы решить проблему OP, не нарушая MVVM, я бы использовал пользовательский преобразователь значений и оболочку для значения (пароля), которое должно быть извлечено из windows пароля.

     public interface IWrappedParameter { T Value { get; } } public class PasswordBoxWrapper : IWrappedParameter { 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>(password => { Login(Username, password); }); } } private void Login(string username, string password) { // Perform login here... } 

    Поскольку модель представления использует IWrappedParameter , ей не нужно знать какие-либо сведения о PasswordBoxWrapper или PasswordBoxConverter . Таким образом вы можете изолировать объект PasswordBox от модели представления и не нарушать шаблон MVVM.

    В представлении:

        ...   

    Хотя я согласен с тем, что важно не хранить пароль в любом месте, мне все же нужна возможность создать экземпляр модели представления без представления и выполнить мои тесты против него.

    Решение, которое сработало для меня, состояло в том, чтобы зарегистрировать функцию PasswordBox.Password с помощью модели представления и вызвать модель представления при выполнении кода входа.

    Это означает строку кода в коде кода.

    Итак, в моем Login.xaml у меня есть

      

    и в Login.xaml.cs У меня есть

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

    затем в LoginViewModel.cs. У меня определен PasswordHandler.

     public Func PasswordHandler { get; set; } 

    и когда логин должен произойти, код вызывает обработчик, чтобы получить пароль из представления …

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

    Таким образом, когда я хочу проверить viewmodel, я могу просто установить PasswordHandler на анонимный метод, который позволяет мне передавать любой пароль, который я хочу использовать в тесте.

    Я потратил много времени на различные решения. Мне не понравилась идея декораторов, поведение испортило пользовательский интерфейс проверки, код за … действительно?

    Лучше всего придерживаться пользовательского прикрепленного свойства и привязать его к свойству SecureString в вашей модели представления. Держите его там как можно дольше. Всякий раз, когда вам потребуется быстрый доступ к простому паролю, временно конвертируйте его в незащищенную строку, используя следующий код:

     namespace Namespace.Extensions { using System; using System.Runtime.InteropServices; using System.Security; ///  /// Provides unsafe temporary operations on secured strings. ///  [SuppressUnmanagedCodeSecurity] public static class SecureStringExtensions { ///  /// Converts a secured string to an unsecured string. ///  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; } ///  /// Copies the existing instance of a secure string into the destination, clearing the destination beforehand. ///  public static void CopyInto(this SecureString source, SecureString destination) { destination.Clear(); foreach (var chr in source.ToUnsecuredString()) { destination.AppendChar(chr); } } ///  /// Converts an unsecured string to a secured string. ///  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 собирать ваш элемент пользовательского интерфейса, поэтому не обращайте внимания на использование статического обработчика событий для события PasswordBox в PasswordBox . Я также обнаружил аномалию, когда элемент управления не обновлял пользовательский интерфейс при использовании свойства SecurePassword для его настройки, потому что вместо этого я SecurePassword пароль в Password .

     namespace Namespace.Controls { using System.Security; using System.Windows; using System.Windows.Controls; using Namespace.Extensions; ///  /// Creates a bindable attached property for the  property. ///  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); } ///  /// Encapsulated event logic ///  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:

      

    Моя собственность в модели просмотра выглядела так:

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

    RequiredSecureString – это простой пользовательский валидатор, который имеет следующую логику:

     [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; } } 

    Здесь у вас есть это. Полное и проверенное чистое решение MVVM.

    Я использовал этот метод и передал поле пароля, хотя это нарушает MVVM, это было важно для меня, потому что я использовал элемент управления содержимым с шаблоном данных для моего входа в свою оболочку, которая представляет собой сложную оболочную среду. Таким образом, доступ к коду позади оболочки был бы дерьмом.

    Передавая пароль, я думаю, это то же самое, что и доступ к управлению из кода позади, насколько я знаю. Я согласен с паролями, не храню в памяти и т. Д. В этой реализации у меня нет свойства для модели с паролем.

    Команда кнопок

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

    ViewModel

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

    Я решил, что брошу свое решение в миксе, так как это такая распространенная проблема … и наличие большого количества вариантов всегда хорошо.

    Я просто завернул PasswordBox в UserControl и реализовал DependencyProperty чтобы иметь возможность связывать. Я делаю все, что в моих силах, чтобы не хранить ясный текст в памяти, поэтому все делается через свойство SecureString и PasswordBox.Password . Во время цикла foreach каждый персонаж становится открытым, но он очень короткий. Честно говоря, если вы беспокоитесь о том, что ваше приложение WPF будет скомпрометировано из этой краткой экспозиции, у вас появятся более серьезные проблемы с безопасностью, которые должны быть устранены.

    Красота заключается в том, что вы не нарушаете никаких правил MVVM, даже «пуристских», так как это UserControl , поэтому ему разрешено иметь код. Когда вы его используете, вы можете иметь чистую связь между View и ViewModel если ваш VideModel не знает о какой-либо части View или источнике пароля. Просто убедитесь, что вы SecureString к SecureString в ViewModel .

    BindablePasswordBox.xaml

        

    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; } } } 

    Использование версии 1:

      

    BindablePasswordBox.xaml.cs (версия 2 – поддерживает двустороннюю привязку).

     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:

      

    you can do it with attached property, see it.. PasswordBox with MVVM

    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 :

      

    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 SignIn { get { if (this.signIn == null) { this.signIn = new RelayCommand((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" }; 

    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):

          

    Behavior:

     using System.Security; using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; namespace Evidence.OutlookIntegration.AddinLogic.Behaviors { ///  /// Intermediate class that handles password box binding (which is not possible directly). ///  public class PasswordBoxBindingBehavior : Behavior { // 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(); } ///  /// Link up the intermediate SecureString (BoundPassword) to the UI instance ///  private void AssociatedObjectOnPasswordChanged(object s, RoutedEventArgs e) { this.BoundPassword = this.AssociatedObject.SecurePassword; } ///  /// Reacts to password reset on viewmodel (ViewModel.Password = new SecureString()) ///  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; } } } } 

    In windows universal app

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

       

    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

      

    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:

          

    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; } } 

    Меня устраивает!

    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:

      

    Then I add a single method call into view constructor:

     public LoginWindow() { InitializeComponent(); ExposeControl.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.

                     

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

    in class viewmodel

     public string password; PasswordChangedCommand = new DelegateCommand(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

            

    или

            

    If you want it combined it all in only one control and one command

          

    And on your Vm (like Konamiman showed)

     public void AuthentifyEmp(object obj) { var passwordBox = obj as PasswordBox; var password = passwordBox.Password; } private RelayCommand _authentifyEmpCommand; public RelayCommand AuthentifyEmpCommand => _authentifyEmpCommand ?? (_authentifyEmpCommand = new RelayCommand(AuthentifyEmp, null)); 

    Here’s my take on it:

    1. Using an attached property to bind the password defeats the purpose of securing the password. The Password property of a password box is not bindable for a reason.

    2. Passing the password box as command parameter will make the ViewModel aware of the control. This will not work if you plan to make your ViewModel reusable cross platform. Don’t make your VM aware of your View or any other controls.

    3. I don’t think introducing a new property, an interface, subscribing to password changed events or any other complicated things is necessary for a simple task of providing the password.

    XAML

       

    Code behind – using code behind does not necessarily violate MVVM. As long as you don’t put any business logic in it.

     btnLogin.CommandParameter = new Func(()=>pbPassword.Password); 

    ViewModel

     LoginCommand = new RelayCommand>(getpwd=> { service.Login(username, getpwd()); }); 
    Давайте будем гением компьютера.