c# - كيفية ربط إلى PasswordBox في MVVM




wpf wpf-controls (19)

لقد واجهت مشكلة مع الربط إلى PasswordBox. يبدو أنها مخاطرة أمنية لكنني أستخدم نمط MVVM لذا أرغب في تجاوز هذا. لقد وجدت بعض التعليمات الشيقة هنا (هل استخدم أحد هذا أو شيئًا مشابهًا؟)

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

يبدو تقنيًا رائعًا ، لكنني غير متأكد من كيفية استرداد كلمة المرور.

لدي أساسا خصائص في بلدي LoginViewModel Username وكلمة Password . Username ما يرام ويعمل كما هو في TextBox .

لقد استخدمت الرمز أعلاه كما هو موضح وأدخلت هذا

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

عندما كان لدي PasswordBox كـ TextBox و Binding Path=Password ثم تم تحديث الخاصية في LoginViewModel بي.

كوداتي بسيط جدا ، في الأساس لدي Command Button . عندما أضغط عليه يتم استدعاء CanLogin وإذا كان يعود صحيح فإنه يدعو Login .
يمكنك معرفة أنني أتحقق من الملكية الخاصة بـ Username هنا والتي تعمل بشكل رائع.

في Login أرسل إلى خدمتي Username Password مرور ، Username يحتوي على بيانات من View بي ولكن Password Null|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 بي ، هذه ليست مشكلة ، ولكن في ViewModel Password فارغة.

هل أفعل شيئًا خاطئًا أو أفتقد خطوة؟

أضع نقطة توقف وأضمن أن الكود أدخل طبقة المساعد الثابتة ولكنه لا يقوم بتحديث Password بي في ViewModel .


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}"/>

حل بسيط دون انتهاك نمط MVVM هو تقديم حدث (أو تفويض) في ViewModel الذي يحصد كلمة المرور.

في ViewModel :

public event EventHandler<HarvestPasswordEventArgs> 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);

سنتي 2:

أنا وضعت مرة واحدة في حوار تسجيل الدخول نموذجي (صناديق المستخدم وكلمة المرور ، بالإضافة إلى زر "موافق") باستخدام WPF و MVVM. لقد قمت بحل مشكلة ربط كلمة المرور ببساطة عن طريق تمرير عنصر التحكم في PasswordBox نفسه كمعلمة للأمر المرتبط بزر "Ok". إذن في وجهة نظري كان لدي:

<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 تعرف شيئًا عن كيفية تنفيذ العرض ، ولكن في هذا المشروع بالتحديد أستطيع تحمله. آمل أن يكون مفيدا لشخص كذلك.


ظننت أنني سأضع الحل في هذا المزيج ، لأن هذه مشكلة شائعة ... ووجود الكثير من الخيارات دائمًا أمر جيد.

أنا ببساطة ملفوفة في PasswordBox في UserControl وتنفيذ DependencyProperty لتتمكن من ربط. أنا أفعل كل ما بوسعي لتجنب تخزين أي نص واضح في الذاكرة ، بحيث يتم كل شيء من خلال SecureString و خاصية PasswordBox.Password . خلال حلقة foreach ، يتم كشف كل شخصية ، ولكنها قصيرة جدًا. بصراحة ، إذا كنت قلقًا بشأن طلب WPF الخاص بك للتعرض لهذا التعرض الوجيز ، فستواجه مشاكل أمنية أكبر يجب التعامل معها.

جمال هذا هو أنك لا تكسر أي قواعد MVVM ، حتى تلك "النقية" ، حيث أن هذا هو UserControl ، لذلك يُسمح لها بأن يكون لها كود خلفي. عند استخدامه ، يمكنك الحصول على اتصال خالص بين View و VideModel دون أن تكون VideModel دراية بأي جزء من View أو مصدر كلمة المرور. فقط تأكد من أنك ملزم لـ SecureString في 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 (Version 1 - No two-way binding support.)

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}"/>

على الرغم من أنني موافق على أنه من المهم تجنب تخزين كلمة المرور في أي مكان ، ما زلت أحتاج إلى القدرة على إنشاء نموذج العرض بدون عرض وتنفيذ اختباراتي ضده.

كان الحل الذي نجح من أجلي هو تسجيل كلمة المرور PasswordBox.Password مع نموذج العرض ، وجعل نموذج العرض يستدعيها عند تنفيذ رمز الدخول.

هذا يعني سطرًا من الشفرة في كود العرض.

لذلك ، في بلدي Login.xaml لدي

<PasswordBox x:Name="PasswordBox"/>

وفي Login.xaml.cs لدي

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

ثم في LoginViewModel.cs لدي تعريف PasswordHandler

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

وعندما يحتاج تسجيل الدخول إلى رمز يحدث استدعاء المعالج للحصول على كلمة المرور من طريقة العرض ...

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

بهذه الطريقة ، عندما أرغب في اختبار نموذج العرض ، يمكنني ببساطة ضبط PasswordHandler على طريقة مجهولة تسمح لي بتسليم أي كلمة مرور أريد استخدامها في الاختبار.


قضيت الكثير من الوقت في البحث عن حلول مختلفة. لم أكن أحب فكرة الديكور ، والسلوكيات خبط واجهة المستخدم التحقق من الصحة ، رمز وراء ... حقا؟

أفضل واحد حتى الآن هو التمسك خاصية مخصصة المرفقة SecureString الخاصية SecureString الخاص بك في نموذج العرض الخاص بك. يبقيه هناك لطالما يمكنك. عندما تحتاج إلى وصول سريع إلى كلمة المرور العادية ، قم بتحويلها مؤقتًا إلى سلسلة غير آمنة باستخدام الكود أدناه:

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 بجمع عنصر واجهة المستخدم ، لذا قم بمقاومة الرغبة في استخدام معالج أحداث ثابت لحدث PasswordChanged في PasswordBox . كما اكتشفت شذوذًا حيث لم يكن عنصر التحكم يقوم بتحديث واجهة المستخدم عند استخدام خاصية SecurePassword ، السبب في أنني أقوم بنسخ كلمة المرور في 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 (e.g. 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));
   }
}

The 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 ، فقد كان ضروريًا بالنسبة لي لأنني كنت أستخدم عنصر تحكم المحتوى مع قالب البيانات لتسجيل الدخول داخل shell الخاص بي ، وهو بيئة هيكلية معقدة. لذا فإن الوصول إلى الشفرة خلف القشرة كان سيئًا.

تمرير passwordbox أعتقد أنه نفس الوصول إلى التحكم من التعليمات البرمجية خلف بقدر ما أعرف. أوافق على كلمات المرور ، لا تحتفظ في الذاكرة إلخ. في هذا التطبيق ليس لدي خاصية كلمة المرور في عرض النموذج.

قيادة زر

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

لقد نشرت GIST here وهو مربع كلمة مرور يمكن ربطه.

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

هذا يعمل على ما يرام بالنسبة لي.

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

يمكنك استخدام هذا 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;
}

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

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}" />

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.


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.


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


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


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>

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

using System;
using System.Windows;
using System.Windows.Controls;
using Caliburn.Micro;

namespace Elections.Server.Handler.ViewModels
{
    public class LoginViewModel : PropertyChangedBase
    {
        MainViewModel _mainViewModel;
        public void SetMain(MainViewModel mainViewModel)
        {
            _mainViewModel = mainViewModel;
        }

        public void Login(Object password)
        {
            var pass = (PasswordBox) password;
            MessageBox.Show(pass.Password);

            //_mainViewModel.ScreenView = _mainViewModel.ControlPanelView;
            //_mainViewModel.TitleWindow = "Panel de Control";
            //HandlerBootstrapper.Title(_mainViewModel.TitleWindow);
        }
    }
}

;) easy!







passwords