How to bind to a PasswordBox in MVVM
Asked Answered
K

32

298

I have come across a problem with binding to a PasswordBox. It seems it's a security risk but I am using the MVVM pattern so I wish to bypass this. I found some interesting code here (has anyone used this or something similar?)

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

It technically looks great, but I am unsure of how to retrieve the password.

I basically have properties in my LoginViewModel for Username and Password. Username is fine and is working as it's a TextBox.

I used the code above as stated and entered this

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

When I had the PasswordBox as a TextBox and Binding Path=Password then the property in my LoginViewModel was updated.

My code is very simple, basically I have a Command for my Button. When I press it CanLogin is called and if it returns true it calls Login.
You can see I check my property for Username here which works great.

In Login I send along to my service a Username and Password, Username contains data from my View but Password is 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 { }
}

This is what I am doing

<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
         MinWidth="180" />

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

I have my TextBox, this is no problem, but in my ViewModel the Password is empty.

Am I doing something wrong or missing a step?

I put a breakpoint and sure enough the code enter the static helper class but it never updates my Password in my ViewModel.

Konrad answered 27/9, 2009 at 16:34 Comment(2)
Well it turns out the code didn't work but i tried an alternative code here and it works perfectly. blog.functionalfun.net/2008/06/…Konrad
Doesn't passing in the whole passwordbox control go against separating the view from the viewmodel?Escarole
S
186

Sorry, but you're doing it wrong.

People should have the following security guideline tattooed on the inside of their eyelids:
Never keep plain text passwords in memory.

The reason the WPF/Silverlight PasswordBox doesn't expose a DP for the Password property is security related.
If WPF/Silverlight were to keep a DP for Password it would require the framework to keep the password itself unencrypted in memory. Which is considered quite a troublesome security attack vector. The PasswordBox uses encrypted memory (of sorts) and the only way to access the password is through the CLR property.

I would suggest that when accessing the PasswordBox.Password CLR property you'd refrain from placing it in any variable or as a value for any property.
Keeping your password in plain text on the client machine RAM is a security no-no.
So get rid of that public string Password { get; set; } you've got up there.

When accessing PasswordBox.Password, just get it out and ship it to the server ASAP. Don't keep the value of the password around and don't treat it as you would any other client machine text. Don't keep clear text passwords in memory.

I know this breaks the MVVM pattern, but you shouldn't ever bind to PasswordBox.Password Attached DP, store your password in the ViewModel or any other similar shenanigans.

If you're looking for an over-architected solution, here's one:
1. Create the IHavePassword interface with one method that returns the password clear text.
2. Have your UserControl implement a IHavePassword interface.
3. Register the UserControl instance with your IoC as implementing the IHavePassword interface.
4. When a server request requiring your password is taking place, call your IoC for the IHavePassword implementation and only than get the much coveted password.

Just my take on it.

-- Justin

Strohbehn answered 29/9, 2009 at 15:31 Comment(17)
Couldn't you use the SecureString in the VM for WPF to solve this problem? It doesn't seem like there is something for Silverlight.Korella
I agree with your intention and the message you are conveying but your answer implies that the password string is never in memory if you follow this approach. The value of the password will be in memory from the moment the user types it. Eliminating the property holding your passphrase is a good idea and will limit the copies of your password that get left laying around for the garbage collector to reap or that could perhaps be found by other managed and unmanaged code running as part of your program, but will not hide it altogether.Dermatology
For most cases, you don't need that level of security. What's the point of making that one thing hard when there're so many other ways to steal passwords? Atleast WPF should've allowed the use of SecureString like @Korella said.Telepathy
Out of curiosity, is there still no way to bind to the SecureString version? It is completely understandable that you might not want to provide a binding to Password directly, but to have no binding at all just makes the entire control more difficult / irritating to use. Clearly there are workarounds for this, but there shouldn't have to be.Sinhalese
If the bad guys have access to the RAM of your machine, you have bigger issues than them stealing your password.Katowice
I think the problem that @JusinAngel is pointing out when you see how easy it is to access internal properties using external tools like snoop, which could just be running in the background recording the text property on anything called password. It wouldn't be that difficult of an attack.Quintic
I agree, getting the value of the PasswordBox.Password property using tools like Snoop is so easy (much easier than retieving it from the ViewModel) that it is not worth hiding that string in the ViewModel.Belew
Sometimes we just need a simple PasswordBox control just to hide chars! no meaning of a password in a form login..Vulgus
For years, I have been using a custom user control that behaves just like PasswordBox, but only returns the text value as SecureString. Yes, this prevents Snoop from displaying the password in plain text. However, the plain text value of SecureString can still be extracted quite easily and only deters novice hacks. If your system is at risk of covertly employing key loggers and sniffers like Snoop, your should re-evaluate at your system security.Consign
The name PasswordBox or the property Password doesn't necessarily imply that concerns in all cases, or in the typical assumed case, match the concerns of the implementation. It's a reasonable guess that this person was "doing it wrong" in this case. But there are cases where the only concern is direct visibility, and pre-populated, but password-style hidden text is desired, despite NO needs for underlying data security. This exists among conceivable reasons for wanting to use/abuse a PasswordBox-like control.Falsetto
It's inexplicable that there isn't a bindable SecureString property for the password. It would introduce no extra security concerns over a CLR property. Furthermore, SecureString is really only useful in very limited circumstances. Mostly it just gives the illusion of security. My advice - subclass TextBox and make your own password box. It's quite easy and you don't have to go through all these ridiculous hoops.Dvinsk
@Strohbehn Could you give us an idea of what could happen if I keep a plain text password in memory? I'm curious as to what exactly would be the security threat –Darrondarrow
@RaikolAmaro, Clear password in memory can be snooped. This link will give some explanations. security.stackexchange.com/questions/29019/…Pentlandite
@Pentlandite Thanks a lot. That is exactly what I was looking for.Darrondarrow
Why not using postcards to send passwords? It is completely safe from memory hacking.Fleenor
Hi guys !! If I can help you, do you should try the simple approach, don't use viewmodel for this case, but use the code-behind, so there you can get all values using a simple code, var user = this.txtUser.Text; this.txtPassWord.Password(). The shorter the better !!!!Moult
I don't want to just send the password to the server. I want to encrypt it and send that to the server.Stocker
V
245

Maybe I am missing something, but it seems like most of these solutions overcomplicate things and do away with secure practices.

This method does not violate the MVVM pattern and maintains complete security. Yes, technically it is code behind, but it is nothing more than a "special case" binding. The ViewModel still has no knowledge of the View implementation, which in my mind it does if you are trying to pass the PasswordBox in to the ViewModel.

Code Behind != Automatic MVVM violation. It all depends on what you do with it. In this case, we are just manually coding a binding, so its all considered part of the UI implementation and therefore is ok.

In the ViewModel, just a simple property. I made it "write only" since there shouldn't be a need to retrieve it from outside the ViewModel for any reason, but it doesn't have to be. Note that it is a SecureString, not just a string.

public SecureString SecurePassword { private get; set; }

In the xaml, you set up a PasswordChanged event handler.

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>

In the code behind:

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

With this method, your password remains in a SecureString at all times and therefore provides maximum security. If you really don't care about security or you need the clear text password for a downstream method that requires it (note: most .NET methods that require a password also support a SecureString option, so you may not really need a clear text password even if you think you do), you can just use the Password property instead. Like this:

(ViewModel property)

public string Password { private get; set; }

(Code behind)

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

If you wanted to keep things strongly typed, you could substitute the (dynamic) cast with the interface of your ViewModel. But really, "normal" data bindings aren't strongly typed either, so its not that big a deal.

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

So best of all worlds - your password is secure, your ViewModel just has a property like any other property, and your View is self contained with no external references required.

Vevina answered 28/7, 2014 at 17:45 Comment(14)
This one looks good to me! If you wanted to be super strict on the security side im not sure this would cut it, but to me its a perfect middle ground. thanks!Peers
Thanks for practicality over rigid dogma about MVVM and paranoia. Works great, thanks.Ewell
The SecureString example would be great with this extension blogs.msdn.com/b/fpintos/archive/2009/06/12/…Tuition
Nice! Btw, Microsoft documentation has an "important note" stating that it should be disposed: msdn.microsoft.com/en-us/library/…Centenarian
Nice indeed. I wish MS would have just added a Password DP of type SecureString to this control.Displace
See my answer for a variant that uses a behavior instead of code behind and allows you to reset the password from the viewmodel.Erg
This is the perfect answer, as it keeps security and MVVM.Makebelieve
This is the best answer. Why is it so far down the page (11th answer for me)?Genic
I'm trying to follow this example but the type of my PasswordBox.Password is string, not SecureString, and it won't let me cast one to the other (and I am not sure I should be doing that anyway). Anyone got any ideas what may be wrong? Found it! The property I want is SecurePassword. Maybe something changed in the framework since this example.Shope
If you look at the first example, it uses SecurePassword. The second one using Password includes the disclaimer "If you really don't care about security or you need the clear text password..." So nothing has changed, both were shown in the original post.Vevina
the datacontext is always null, any idea?Izettaizhevsk
You have to actually have set the DataContext, but that's part of data binding. You should be doing it as part of your view setup. If you aren't setting a DataContext, you won't be able to bind anything, password or otherwise.Vevina
Interesting side-note. I tried making the get-er for SecurePassword public, and Windows Defender reported a Trojan pattern named Fuery.B!cl on my system every time I ran the program.Ardatharde
This is great and pragmatic answer for most common use cases.Claro
G
221

My 2 cents:

I developed once a typical login dialog (user and password boxes, plus "Ok" button) using WPF and MVVM. I solved the password binding issue by simply passing the PasswordBox control itself as a parameter to the command attached to the "Ok" button. So in the view I had:

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

And in the ViewModel, the Execute method of the attached command was as follows:

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

This slightly violates the MVVM pattern since now the ViewModel knows something about how the View is implemented, but in that particular project I could afford it. Hope it is useful for someone as well.

Goer answered 10/1, 2011 at 17:57 Comment(8)
Hello Konamiman,when the Execute method is called.In my viewmodel i have a class User(login,pass) and a command authenticate.How can i use Execute in that context?Showoff
very helpful, thanks. fyi, someone might be used to seeing something like _loginCommand = new RelayCommand(param => Login(UserName, (PasswordBox)param), param => CanLogIn);Chapple
this is an ok solution but fails for something like a password + password confirmation comboSeabrooke
Hello Konamiman, I'm using your solution but it doesn't work on Windows 8.1 Store app. I've asked this question: #26222094Longbow
Thanks for this! This solved a huge problem I had with moving the data from the UI thread to the main program thread. Make sure to implement the SecureString approach, and ~get rid of the password as soon as possible~. Dump it. Dispose it. Clear it. Do what you need to do. Also, make sure you implement IDisposable.Sabella
You can also use x:Name="MyPasswordBox"Lignify
This is the quick and easy solution I was looking for. Thanks!Galatea
Handling controls in the view model clearly violates the MVVM pattern. There are far better solutions.Canonicity
S
186

Sorry, but you're doing it wrong.

People should have the following security guideline tattooed on the inside of their eyelids:
Never keep plain text passwords in memory.

The reason the WPF/Silverlight PasswordBox doesn't expose a DP for the Password property is security related.
If WPF/Silverlight were to keep a DP for Password it would require the framework to keep the password itself unencrypted in memory. Which is considered quite a troublesome security attack vector. The PasswordBox uses encrypted memory (of sorts) and the only way to access the password is through the CLR property.

I would suggest that when accessing the PasswordBox.Password CLR property you'd refrain from placing it in any variable or as a value for any property.
Keeping your password in plain text on the client machine RAM is a security no-no.
So get rid of that public string Password { get; set; } you've got up there.

When accessing PasswordBox.Password, just get it out and ship it to the server ASAP. Don't keep the value of the password around and don't treat it as you would any other client machine text. Don't keep clear text passwords in memory.

I know this breaks the MVVM pattern, but you shouldn't ever bind to PasswordBox.Password Attached DP, store your password in the ViewModel or any other similar shenanigans.

If you're looking for an over-architected solution, here's one:
1. Create the IHavePassword interface with one method that returns the password clear text.
2. Have your UserControl implement a IHavePassword interface.
3. Register the UserControl instance with your IoC as implementing the IHavePassword interface.
4. When a server request requiring your password is taking place, call your IoC for the IHavePassword implementation and only than get the much coveted password.

Just my take on it.

-- Justin

Strohbehn answered 29/9, 2009 at 15:31 Comment(17)
Couldn't you use the SecureString in the VM for WPF to solve this problem? It doesn't seem like there is something for Silverlight.Korella
I agree with your intention and the message you are conveying but your answer implies that the password string is never in memory if you follow this approach. The value of the password will be in memory from the moment the user types it. Eliminating the property holding your passphrase is a good idea and will limit the copies of your password that get left laying around for the garbage collector to reap or that could perhaps be found by other managed and unmanaged code running as part of your program, but will not hide it altogether.Dermatology
For most cases, you don't need that level of security. What's the point of making that one thing hard when there're so many other ways to steal passwords? Atleast WPF should've allowed the use of SecureString like @Korella said.Telepathy
Out of curiosity, is there still no way to bind to the SecureString version? It is completely understandable that you might not want to provide a binding to Password directly, but to have no binding at all just makes the entire control more difficult / irritating to use. Clearly there are workarounds for this, but there shouldn't have to be.Sinhalese
If the bad guys have access to the RAM of your machine, you have bigger issues than them stealing your password.Katowice
I think the problem that @JusinAngel is pointing out when you see how easy it is to access internal properties using external tools like snoop, which could just be running in the background recording the text property on anything called password. It wouldn't be that difficult of an attack.Quintic
I agree, getting the value of the PasswordBox.Password property using tools like Snoop is so easy (much easier than retieving it from the ViewModel) that it is not worth hiding that string in the ViewModel.Belew
Sometimes we just need a simple PasswordBox control just to hide chars! no meaning of a password in a form login..Vulgus
For years, I have been using a custom user control that behaves just like PasswordBox, but only returns the text value as SecureString. Yes, this prevents Snoop from displaying the password in plain text. However, the plain text value of SecureString can still be extracted quite easily and only deters novice hacks. If your system is at risk of covertly employing key loggers and sniffers like Snoop, your should re-evaluate at your system security.Consign
The name PasswordBox or the property Password doesn't necessarily imply that concerns in all cases, or in the typical assumed case, match the concerns of the implementation. It's a reasonable guess that this person was "doing it wrong" in this case. But there are cases where the only concern is direct visibility, and pre-populated, but password-style hidden text is desired, despite NO needs for underlying data security. This exists among conceivable reasons for wanting to use/abuse a PasswordBox-like control.Falsetto
It's inexplicable that there isn't a bindable SecureString property for the password. It would introduce no extra security concerns over a CLR property. Furthermore, SecureString is really only useful in very limited circumstances. Mostly it just gives the illusion of security. My advice - subclass TextBox and make your own password box. It's quite easy and you don't have to go through all these ridiculous hoops.Dvinsk
@Strohbehn Could you give us an idea of what could happen if I keep a plain text password in memory? I'm curious as to what exactly would be the security threat –Darrondarrow
@RaikolAmaro, Clear password in memory can be snooped. This link will give some explanations. security.stackexchange.com/questions/29019/…Pentlandite
@Pentlandite Thanks a lot. That is exactly what I was looking for.Darrondarrow
Why not using postcards to send passwords? It is completely safe from memory hacking.Fleenor
Hi guys !! If I can help you, do you should try the simple approach, don't use viewmodel for this case, but use the code-behind, so there you can get all values using a simple code, var user = this.txtUser.Text; this.txtPassWord.Password(). The shorter the better !!!!Moult
I don't want to just send the password to the server. I want to encrypt it and send that to the server.Stocker
B
24

You can use this XAML:

<PasswordBox>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="PasswordChanged">
            <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}}" CommandParameter="{Binding ElementName=PasswordBox}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</PasswordBox>

And this command execute method:

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

This requires adding the System.Windows.Interactivity assembly to your project and referencing it via xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity".

Bilbe answered 3/9, 2014 at 9:44 Comment(5)
FYI xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"Minorca
Without requiring to Name a the PasswordBox: CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}}" (note: not RelativeSource Self).Echopraxia
This solution violates the MVVM pattern.Canonicity
This command doesn't look right? Like what is the intended command name in the VM?Cavern
FYI Command is incorrect here, bind it to your command and use CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}}" as suggested aboveCavern
P
16

I spent a great deal of time looking at various solutions. I didn't like the decorators idea, behaviors mess up the validation UI, code behind... really?

The best one yet is to stick to a custom attached property and bind to your SecureString property in your view model. Keep it in there for as long as you can. Whenever you'll need quick access to the plain password, temporarily convert it to an unsecure string using the code below:

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

Make sure you allow the GC to collect your UI element, so resist the urge of using a static event handler for the PasswordChanged event on the PasswordBox. I also discovered an anomaly where the control wasn't updating the UI when using the SecurePassword property for setting it up, reason why I'm copying the password into Password instead.

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

And the XAML usage:

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

My property in the view model looked like this:

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

The RequiredSecureString is just a simple custom validator that has the following logic:

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

Here you have it. A complete and tested pure MVVM solution.

Pyromorphite answered 18/2, 2016 at 6:0 Comment(0)
B
14

This works just fine for me.

<Button Command="{Binding Connect}" 
        CommandParameter="{Binding ElementName=MyPasswordBox}"/>
Betel answered 27/9, 2009 at 16:34 Comment(5)
What about CommandParameter="{Binding ElementName=MyPasswordBox, Path=SecurePassword"} ?Clinical
LukeN, this doesn't work (at least for me). Probably for the same reason - SecurePassword is not dependency property.Skyscape
Assuming that the ICommand is implemented in the view model, this solution would violate the MVVM pattern.Canonicity
@vkrzv, This markup works: <PasswordBox Name="pb" /> <Button CommandParameter="{Binding ElementName=pb, Path=SecurePassword}" />Eatable
CommandParameter binding to PasswordBox is fine in the original answer. But the CommandParameter will be an empty SecurePassword if binding to PasswordBox.SecurePasswordDespondent
S
10

A simple solution without violating the MVVM pattern is to introduce an event (or delegate) in the ViewModel that harvests the password.

In the ViewModel:

public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;

with these EventArgs:

class HarvestPasswordEventArgs : EventArgs
{
    public string Password;
}

in the View, subscribe to the event on creating the ViewModel and fill in the password value.

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

In the ViewModel, when you need the password, you can fire the event and harvest the password from there:

if (HarvestPassword == null)
  //bah 
  return;

var pwargs = new HarvestPasswordEventArgs();
HarvestPassword(this, pwargs);

LoginHelpers.Login(Username, pwargs.Password);
Sterne answered 17/10, 2012 at 9:29 Comment(2)
The one thing you are missing is that when subscribing a view to a view model event, you should be using a WeakEventManager<TEventSource, TEventArgs> to avoid memory leaks. Often times the view will not have the same lifetime as the viewmodel. WeakEventManager<IViewModel, EventArgs>.AddHandler(iViewModelInstance, nameof(IViewModel.Event), eventHandlerMethod);Reinhart
I prefer this solution, since it's simple, does not violate MVVM, has minimal code behind, allows right usage of passwordbox (if you use ´SecurePassword´ instead). Also it's simple now to implement other HarvestPassword methods now (like SmartCard....)Augmentation
L
8

To solve the OP problem without breaking the MVVM, I would use custom value converter and a wrapper for the value (the password) that has to be retrieved from the password box.

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

In the view model:

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

Because the view model uses IWrappedParameter<T>, it does not need to have any knowledge about PasswordBoxWrapper nor PasswordBoxConverter. This way you can isolate the PasswordBox object from the view model and not break the MVVM pattern.

In the view:

<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}}" />
Liber answered 12/3, 2014 at 14:56 Comment(2)
very elegant solution imo. i have based mine upon this. the only difference: i pass SecureString SecurePassword to login function instead of String Password. so that there are no unencrypted strings with passwort flying around memory.Mcatee
It has been a while but I can't seems to get this to work because of my RelayCommand. would u mind adding yours?Europeanize
G
7

I posted a GIST here that is a bindable password box.

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;
        }
    }
}
Griffiths answered 9/7, 2010 at 16:17 Comment(3)
while this isn't bad, you lose the ability to set simple attributes like padding and tabindexSeabrooke
Taylor, I inlined the gist so that it's available in the answer. (It was looking like a link-only answer otherwise.. just trying to avoid this being deleted as such.) Feel free to mess with the inlined content.Attitudinize
@Seabrooke but you can fix that with styles. I solve this problem in a similar way but I use a ContentControl you can then just use a PasswordBox as a the content and style that in XAML as you fit. The purpose of the ContentControl is just to subscribe to the PasswordChanged event and expose a two-directional bindable property. All in all, it's 65 lines of code and pretty much what this decorate class does. See here for my gist of the following gist.github.com/leidegre/c7343b8c720000fe3132Degraded
A
6

This implementation is slightly different. You pass a PasswordBox to the View through binding of a property in ViewModel. It doesn't use any command params. The ViewModel stays ignorant of the View. I have a VB VS 2010 Project that can be downloaded from SkyDrive. WPF MVVM PassWordBox Example.zip

The way that I am using PasswordBox in a WPF MVVM application is pretty simplistic and works well for me.

Basically you create a public readonly property that the View can bind to as a PasswordBox (The actual control):

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

I use a backing field just to do the self initialization of the property.

Then from the Xaml you bind the Content of a ContentControl or a Control Container:

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

From there you have full control of the PasswordBox. I also use a PasswordAccessor (just a function of String) to return the Password Value when doing login or whatever else you want the Password for. In the example I have a public property in a Generic User Object Model. Example:

Public Property PasswordAccessor() As Func(Of String)

In the User Object the password string property is readonly without any backing store. It just returns the Password from the PasswordBox. Example:

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

Then in the ViewModel I make sure that the Accessor is created and set to the PasswordBox.Password property:

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

When I need the Password string say for login I just get the User Objects Password property that really invokes the Function to grab the password and return it, then the actual password is not stored by the User Object. Example: would be in the 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

That should do it. The ViewModel doesn't need any knowledge of the View's Controls. The View just binds to the Property in the ViewModel, not any different than the View Binding to an image or other resource. In this case that resource(Property) just happens to be a usercontrol. It allows for testing as the ViewModel creates and owns the Property and the Property is independent of the View. As for security I don't know how good this implementation is. But by using a Function the value is not stored in the Property itself just accessed by the Property.

Allometry answered 31/8, 2011 at 6:36 Comment(0)
P
5

While I agree it's important to avoid storing the password anywhere, I still need the ability to instantiate the view model without a view and execute my tests against it.

The solution that worked for me was to register the PasswordBox.Password function with the view model, and have the view model invoke it when executing the login code.

This does mean a line of code in the view's codebehind.

So, in my Login.xaml I have

<PasswordBox x:Name="PasswordBox"/>

and in Login.xaml.cs I have

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

then in LoginViewModel.cs I have the PasswordHandler defined

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

and when login needs to happen the code invokes the handler to get the password from the view...

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

This way, when I want to test the viewmodel I can simply set PasswordHandler to an anonymous method that lets me deliver whatever password I want to use in the test.

Plantagenet answered 16/10, 2014 at 13:7 Comment(0)
E
3

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>

Behavior:

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

    }
}
Erg answered 7/10, 2014 at 17:15 Comment(1)
BoundPassword threw an exception: InvalidOperationException: PasswordBoxBindingBehavior type must derive from FrameworkElement or FrameworkContentElement. Can you please tell me how I can solve this?Aubyn
K
3

I figured I'd throw my solution in the mix, since this is such a common issue... and having plenty of options is always a good thing.

I simply wrapped a PasswordBox in a UserControl and implemented a DependencyProperty to be able to bind. I'm doing everything I can to avoid storing any clear text in the memory, so everything is done through a SecureString and the PasswordBox.Password property. During the foreach loop, each character does get exposed, but it's very brief. Honestly, if you're worried about your WPF application to be compromised from this brief exposure, you've got bigger security issues that should be handled.

The beauty of this is that you are not breaking any MVVM rules, even the "purist" ones, since this is a UserControl, so it's allowed to have code-behind. When you're using it, you can have pure communication between View and ViewModel without your VideModel being aware of any part of View or the source of the password. Just make sure you're binding to SecureString in your ViewModel.

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}"/>
Knp answered 22/9, 2015 at 4:47 Comment(1)
I've tried implementing this, but you get an infinite loop when you update the password on the UI; because if (Password != secure) will always be false as SecureString doesn't override equals. Any thoughts?Stillhunt
C
3

Send a SecureString to the view model using an Attached Behavior and ICommand

There is nothing wrong with code-behind when implementing MVVM. MVVM is an architectural pattern that aims to separate the view from the model/business logic. MVVM describes how to achieve this goal in a reproducible way (the pattern). It doesn't care about implementation details, like how do you structure or implement the view. It just draws the boundaries and defines what is the view, the view model and what the model in terms of this pattern's terminology.

MVVM doesn't care about the language (XAML or C#) or compiler (partial classes). Being language independent is a mandatory characteristic of a design pattern - it must be language neutral.

However, code-behind has some draw backs like making your UI logic harder to understand, when it is wildly distributed between XAML and C#. But most important implementing UI logic or objects like templates, styles, triggers, animations etc in C# is very complex and ugly/less readable than using XAML. XAML is a markup language that uses tags and nesting to visualize object hierarchy. Creating UI using XAML is very convenient. Although there are situations where you are fine choosing to implement UI logic in C# (or code-behind). Handling the PasswordBox is one example.

For this reasons handling the PasswordBox in the code-behind by handling the PasswordBox.PasswordChanged, is no violation of the MVVM pattern.

A clear violation would be to pass a control (the PasswordBox) to the view model. Many solutions recommend this e.g., bay passing the instance of the PasswordBox as ICommand.CommandParameter to the view model. Obviously a very bad and unnecessary recommendation.

If you don't care about using C#, but just want to keep your code-behind file clean or simply want to encapsulate a behavior/UI logic, you can always make use of attached properties and implement an attached behavior.

Opposed of the infamous wide spread helper that enables binding to the plain text password (really bad anti-pattern and security risk), this behavior uses an ICommand to send the password as SecureString to the view model, whenever the PasswordBox raises the PasswordBox.PasswordChanged event.

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <PasswordBox PasswordBox.Command="{Binding VerifyPasswordCommand}" />
</Window>

ViewModel.cs
You can find a RelayCommand implementation at Microsoft Docs: Relaying Command Logic.

public class ViewModel : INotifyPropertyChanged
{
  public ICommand VerifyPasswordCommand => new RelayCommand(VerifyPassword);

  public void VerifyPassword(object commadParameter)
  {
    if (commandParameter is SecureString secureString)
    {
      IntPtr valuePtr = IntPtr.Zero;
      try
      {
        valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
        string plainTextPassword = Marshal.PtrToStringUni(valuePtr);

        // Handle plain text password. 
        // It's recommended to convert the SecureString to plain text in the model, when really needed.
      } 
      finally 
      {
        Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
      }
    }
  }
}

PasswordBox.cs

// Attached behavior
class PasswordBox : DependencyObject
{
  #region Command attached property

  public static readonly DependencyProperty CommandProperty =
    DependencyProperty.RegisterAttached(
      "Command",
      typeof(ICommand),
      typeof(PasswordBox),
      new PropertyMetadata(default(ICommand), PasswordBox.OnSendPasswordCommandChanged));

  public static void SetCommand(DependencyObject attachingElement, ICommand value) =>
    attachingElement.SetValue(PasswordBox.CommandProperty, value);

  public static ICommand GetCommand(DependencyObject attachingElement) =>
    (ICommand) attachingElement.GetValue(PasswordBox.CommandProperty);

  #endregion

  private static void OnSendPasswordCommandChanged(
    DependencyObject attachingElement,
    DependencyPropertyChangedEventArgs e)
  {
    if (!(attachingElement is System.Windows.Controls.PasswordBox passwordBox))
    {
      throw new ArgumentException("Attaching element must be of type 'PasswordBox'");
    }

    if (e.OldValue != null)
    {
      return;
    }

    WeakEventManager<object, RoutedEventArgs>.AddHandler(
      passwordBox,
      nameof(System.Windows.Controls.PasswordBox.PasswordChanged),
      SendPassword_OnPasswordChanged);
  }

  private static void SendPassword_OnPasswordChanged(object sender, RoutedEventArgs e)
  {
    var attachedElement = sender as System.Windows.Controls.PasswordBox;
    SecureString commandParameter = attachedElement?.SecurePassword;
    if (commandParameter == null || commandParameter.Length < 1)
    {
      return;
    }

    ICommand sendCommand = GetCommand(attachedElement);
    sendCommand?.Execute(commandParameter);
  }
}
Canonicity answered 28/4, 2020 at 13:25 Comment(0)
P
2

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

Phenomenal answered 29/9, 2009 at 14:54 Comment(0)
K
2

I used this method and passed the password box, although this does violate the MVVM it was essential for me because I was using a content control with data template for my login within my shell which is a complex shell enviroment. So accessing the code behind of the shell would have been crap.

Passing the passwordbox I would think is same as accessing control from code behind as far as I know. I agree passwords, dont keep in memory etc In this implementation I don't have property for password in view model.

Button Command

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);
}
Kermit answered 30/7, 2011 at 5:30 Comment(1)
This is a clear violation of the MVVM pattern. The pattern doesn't allow to handle controls in the view model.Canonicity
R
2

For complete newbies like me, here is a complete working sample of what Konamiman suggested above. Thanks Konamiman.

XAML

    <PasswordBox x:Name="textBoxPassword"/>
    <Button x:Name="buttonLogin" Content="Login"
            Command="{Binding PasswordCommand}"
            CommandParameter="{Binding ElementName=textBoxPassword}"/> 

ViewModel

public class YourViewModel : ViewModelBase
{
    private ICommand _passwordCommand;
    public ICommand PasswordCommand
    {
        get {
            if (_passwordCommand == null) {
                _passwordCommand = new RelayCommand<object>(PasswordClick);
            }
            return _passwordCommand;
        }
    }

    public YourViewModel()
    {
    }

    private void PasswordClick(object p)
    {
        var password = p as PasswordBox;
        Console.WriteLine("Password is: {0}", password.Password);
    }
}
Roberson answered 7/9, 2018 at 10:59 Comment(1)
This is a clear violation of the MVVM pattern. The pattern doesn't allow to handle controls in the view model.Canonicity
M
1

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}"/>
Mi answered 27/9, 2009 at 16:45 Comment(0)
R
1

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"
    };
Revolving answered 3/11, 2013 at 9:9 Comment(1)
Comments are not for extended discussion; this conversation has been moved to chat.Twelvemonth
S
1

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

Splitting answered 28/7, 2016 at 19:49 Comment(0)
E
1

I don't have enough rep to comment on another answer unfortunately :( Just wanted to follow on from "Steve In CO"'s answer. Updated his "PasswrodChanged" event to the following to enable using the same PasswordChanged event for all PasswordBox's if you have multiple on the one view by using the Name property of the sender. There might be a better way to do this and if there are any reasons not to do this please let me know :)

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
    {
        if (sender is PasswordBox) { 
        if (this.DataContext != null)

        { ((dynamic)this.DataContext).GetType().GetProperty(((PasswordBox)sender).Name).SetValue((dynamic)this.DataContext, ((PasswordBox)sender).Password, null); }
    }
    }
Earthward answered 6/10, 2021 at 17:24 Comment(0)
G
0

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

Gentlemanatarms answered 2/10, 2009 at 17:58 Comment(1)
The way that is used in Pop3SettingsView of WAF is funny. PasswordBox passwordBox = (PasswordBox)sender; if (ViewModel != null) { ViewModel.Pop3Password = passwordBox.Password; } Pop3Password of ViewModel is the string property. so, its not secure as well .. better to use the attached propertyWeighin
F
0

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!

Funk answered 15/10, 2013 at 20:23 Comment(1)
You give me a nice idea. :)Blida
H
0

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}" />
Halve answered 13/11, 2013 at 10:32 Comment(0)
M
0

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.

Mulder answered 22/5, 2014 at 15:12 Comment(0)
E
0

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

And that's it. 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.

Examen answered 16/7, 2014 at 17:54 Comment(0)
V
0

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.

Velmaveloce answered 28/10, 2014 at 16:10 Comment(0)
V
0

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"/>
Verdin answered 12/4, 2016 at 9:9 Comment(0)
P
0

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

Pyroelectric answered 26/9, 2016 at 13:1 Comment(1)
The reason password box is not bindable is because we don't want to store password in a clear string. String is immutable and we're not sure how long will it stay in memory.Pentlandite
R
0

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>

or

<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>
Ravenna answered 31/7, 2017 at 18:17 Comment(0)
P
0

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

<PasswordBox x:Name="pbPassword" />
<Button Content="Login" Command="{Binding LoginCommand}" x:Name="btnLogin"/>

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<string>(()=>pbPassword.Password); 

ViewModel

LoginCommand = new RelayCommand<Func<string>>(getpwd=> { service.Login(username, getpwd()); });
Pentlandite answered 22/8, 2018 at 14:22 Comment(1)
But one can pass SecurePassword as a command parameter.Eatable
O
-1

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

<PasswordBox Name="PasswordBoxPin" PasswordChar="*">
    <PasswordBox.InputBindings>
        <KeyBinding Key="Return" Command="{Binding AuthentifyEmpCommand}" CommandParameter="{Binding ElementName=PasswordBoxPin}"/>
    </PasswordBox.InputBindings>
</PasswordBox>

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

EDIT: Based on comments I feel the need to specify that this is a violation of MVVM pattern, use it only if that is not one of your primary concerns.

Orbit answered 25/1, 2018 at 16:47 Comment(1)
This is a clear violation of the MVVM pattern. The pattern doesn't allow to handle controls in the view model.Canonicity

© 2022 - 2024 — McMap. All rights reserved.