it-swarm.com.de

So binden Sie an eine PasswordBox in MVVM

Ich bin auf ein Problem beim Binden an eine PasswordBox gestoßen. Es scheint ein Sicherheitsrisiko zu sein, aber ich verwende das MVVM-Muster, daher möchte ich das umgehen. Ich habe hier einen interessanten Code gefunden (hat jemand diesen oder etwas ähnliches verwendet?)

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

Technisch sieht es toll aus, aber ich bin nicht sicher, wie ich das Passwort abrufen kann. 

Ich habe grundsätzlich Eigenschaften in meiner LoginViewModel für Username und Password. Username ist in Ordnung und funktioniert, da es eine TextBox ist.

Ich habe den oben angegebenen Code verwendet und diesen eingegeben

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

Wenn ich PasswordBox als TextBox und Binding Path=Password hatte, wurde die Eigenschaft in meiner LoginViewModel aktualisiert.

Mein Code ist sehr einfach, im Grunde habe ich eine Command für meine Button. Wenn ich es drücke, wird CanLogin aufgerufen und wenn true zurückgegeben wird, wird Login aufgerufen.
Sie können sehen, dass ich meine Eigenschaft auf Username überprüfe, was großartig funktioniert. 

In Login schicke ich zu meinem Dienst eine Username und Password, Username enthält Daten von meiner View, aber Password ist 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 { }
    }

Das mache ich

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

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

Ich habe meine TextBox, das ist kein Problem, aber in meiner ViewModel ist die Password leer.

Mache ich etwas falsch oder verpasse ich einen Schritt?

Ich habe einen Haltepunkt gesetzt und der Code tritt zwar in die statische Hilfsklasse ein, aber er aktualisiert niemals meine Password in meiner ViewModel.

224
mark smith

Entschuldigung, aber Sie machen es falsch.

Menschen sollten die folgenden Sicherheitsrichtlinien auf der Innenseite ihrer Augenlider tätowieren lassen:
Bewahren Sie niemals Klartextpasswörter im Speicher auf.

Der Grund, warum die WPF/Silverlight PasswordBox keinen DP für die Password-Eigenschaft verfügbar macht, hängt mit der Sicherheit zusammen.
Wenn WPF/Silverlight einen DP für ein Kennwort behalten würde, müsste das Framework das Kennwort selbst unverschlüsselt im Speicher aufbewahren. Dies ist ein ziemlich lästiger Sicherheitsangriffsvektor. Die PasswordBox verwendet verschlüsselten Speicher (von Art), und der Zugriff auf das Kennwort ist nur über die CLR-Eigenschaft möglich. 

Ich würde vorschlagen, dass Sie beim Zugriff auf die PasswordBox.Password-CLR-Eigenschaft davon Abstand nehmen, sie in eine beliebige Variable oder als Wert für eine beliebige Eigenschaft zu platzieren.
Wenn Sie Ihr Kennwort im Klartext auf dem Client-Computer RAM speichern, ist dies eine Sicherheits-Nr.
Entfernen Sie also das "public string Password {get; set;}", das Sie dort oben gefunden haben. 

Wenn Sie auf PasswordBox.Password zugreifen, holen Sie es einfach heraus und senden Sie es an den Server ASAP. Bewahren Sie den Wert des Kennworts nicht auf und behandeln Sie es nicht wie andere Client-Computertexte. Bewahren Sie keine Klartextpasswörter im Speicher auf. 

Ich weiß, dass dies das MVVM-Muster bricht, aber Sie sollten sich niemals an PasswordBox.Password Attached DP binden. Speichern Sie Ihr Kennwort im ViewModel oder in anderen ähnlichen Phänomenen.

Wenn Sie nach einer übergeordneten Architektur suchen, finden Sie hier eine:
1. Erstellen Sie die IHavePassword-Schnittstelle mit einer Methode, die den Kennwort-Klartext zurückgibt.
2. Lassen Sie Ihre UserControl eine IHavePassword-Schnittstelle implementieren.
3. Registrieren Sie die UserControl-Instanz bei der Implementierung der IHavePassword-Schnittstelle bei Ihrem IoC.
4. Rufen Sie bei einer Serveranfrage, die Ihr Kennwort verlangt, Ihre IoC für die Implementierung von IHavePassword ein und erhalten Sie nur dann das begehrte Kennwort.

Nur meine Meinung dazu. 

- Justin 

148
JustinAngel

Meine 2 Cent:

Ich habe einmal ein typisches Anmeldedialogfeld (Benutzer- und Kennwortfelder sowie die Schaltfläche "Ok") mit WPF und MVVM entwickelt. Ich habe das Problem der Kennwortbindung gelöst, indem ich einfach das PasswordBox-Steuerelement selbst als Parameter an den Befehl übergeben habe, der an die Schaltfläche "Ok" angehängt ist. Also in der Ansicht hatte ich:

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

Im ViewModel lautete die Execute-Methode des angehängten Befehls wie folgt:

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

Dies verstößt leicht gegen das MVVM-Muster, da das ViewModel jetzt etwas über die Implementierung der View weiß, aber in diesem speziellen Projekt konnte ich es mir leisten. Ich hoffe es ist auch für jemanden nützlich.

176
Konamiman

Vielleicht fehlt mir etwas, aber die meisten dieser Lösungen scheinen zu kompliziert zu sein und sichere Praktiken zu beseitigen. 

Diese Methode verstößt nicht gegen das MVVM-Muster und gewährleistet vollständige Sicherheit. Ja, technisch liegt es hinter dem Code, aber es ist nichts anderes als eine "Sonderfall" -Bindung. Das ViewModel hat noch keine Kenntnis der View-Implementierung, was meiner Meinung nach der Fall ist, wenn Sie versuchen, die PasswordBox an das ViewModel zu übergeben. 

Code hinter! = Automatische MVVM-Verletzung. Es hängt alles davon ab, was Sie damit machen. In diesem Fall müssen Sie lediglich eine Bindung manuell codieren, sodass sie als Teil der Benutzeroberflächenimplementierung gilt und daher in Ordnung ist.

Im ViewModel nur eine einfache Eigenschaft. Ich habe es "nur schreiben" lassen, da es nicht notwendig sein sollte, es außerhalb des ViewModel aus irgendeinem Grund abzurufen, aber es muss nicht sein. Beachten Sie, dass es sich um einen SecureString handelt, nicht nur um einen String.

public SecureString SecurePassword { private get; set; }

In xaml richten Sie einen PasswordChanged-Ereignishandler ein.

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>

Im Code dahinter:

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

Bei dieser Methode bleibt Ihr Passwort immer in einem SecureString und bietet daher maximale Sicherheit. Wenn Sie sich wirklich nicht für die Sicherheit interessieren oder das Klartextkennwort für eine Downstream-Methode benötigen, für die dies erforderlich ist (Hinweis: Die meisten .NET-Methoden, für die ein Kennwort erforderlich ist, unterstützen auch die SecureString-Option. Daher benötigen Sie möglicherweise kein Klartextkennwort Selbst wenn Sie glauben, dass Sie dies tun, können Sie stattdessen einfach die Eigenschaft Password verwenden. So was:

(ViewModel-Eigenschaft)

public string Password { private get; set; }

(Code hinter)

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

Wenn Sie die Dinge stark typisieren möchten, können Sie die (dynamische) Umwandlung durch die Benutzeroberfläche Ihres ViewModel ersetzen. Allerdings sind "normale" Datenbindungen auch nicht stark typisiert, daher ist es keine große Sache.

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

Das Beste aus allen Welten - Ihr Passwort ist sicher, Ihr ViewModel hat nur eine Eigenschaft wie jede andere Eigenschaft, und Ihre View ist in sich geschlossen, ohne dass externe Verweise erforderlich sind.

151
Steve In CO

Sie können diese XAML verwenden:

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

Und dieser Befehl ausführen Methode:

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

Das funktioniert gut für mich.

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

Eine einfache Lösung ohne Verletzung des MVVM-Musters besteht darin, ein Ereignis (oder einen Delegierten) in ViewModel einzuführen, das das Kennwort einholt.

Im ViewModel :

public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;

mit diesen EventArgs:

class HarvestPasswordEventArgs : EventArgs
{
    public string Password;
}

abonnieren Sie in der View das Ereignis beim Erstellen des ViewModel und geben Sie den Kennwortwert ein.

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

Wenn Sie das Kennwort im ViewModel benötigen, können Sie das Ereignis auslösen und das Kennwort von dort abrufen:

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

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

LoginHelpers.Login(Username, pwargs.Password);
10
Jan Willem B

Ich habe ein Gist here gepostet, bei dem es sich um ein bindbares Passwortfeld handelt.

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;
        }
    }
}
8
Taylor Leese

Diese Implementierung ist etwas anders. Sie übergeben ein Kennwortfeld an die View Through-Bindung einer Eigenschaft in ViewModel. Es verwendet keine Befehlsparameter. Das ViewModel bleibt ignoriert von der Ansicht. Ich habe ein VB vs 2010-Projekt, das von SkyDrive heruntergeladen werden kann. Wpf MvvM PassWordBox Example.Zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511

Die Art und Weise, wie ich PasswordBox in einer Wpf MvvM-Anwendung verwende, ist ziemlich simpel und funktioniert gut für mich. Das heißt nicht, dass ich denke, dass es der richtige oder der beste Weg ist. Es ist nur eine Implementierung von Passwordbox und dem MvvM-Pattern.

Grundsätzlich erstellen Sie eine öffentliche Readonly-Eigenschaft, an die die View als PasswordBox gebunden werden kann (das eigentliche Steuerelement). Beispiel:

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

Ich verwende ein Hintergrundfeld, um die Eigenschaft selbst zu initialisieren.

Dann binden Sie von Xaml aus den Inhalt eines ContentControl- oder eines Control Container-Beispiels:

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

Von dort aus haben Sie die volle Kontrolle über die Passwortbox. Ich verwende auch einen PasswordAccessor (Nur eine Funktion von String), um den Passwortwert bei der Anmeldung zurückzugeben oder was auch immer Sie für das Passwort wünschen. Im Beispiel habe ich eine öffentliche Eigenschaft in einem generischen Benutzerobjektmodell . Beispiel: 

Public Property PasswordAccessor() As Func(Of String)

In dem Benutzerobjekt ist die Eigenschaft für die Kennwortzeichenfolge ohne Sicherungsspeicher schreibgeschützt. Es gibt nur das Kennwort aus der Kennwort-Box zurück 

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

Dann vergewissere ich mich im ViewModel, dass der Accessor erstellt wurde, und setze die PasswordBox.Password-Eigenschaft ' Beispiel:

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

Wenn ich die Kennwortzeichenfolge für die Anmeldung benötige, sage ich einfach die Benutzerobjekte-Kennworteigenschaft, die die Funktion wirklich aufruft, um das Kennwort zu übernehmen und es zurückzugeben, dann wird das tatsächliche Kennwort nicht vom Benutzerobjekt gespeichert. Beispiel: wäre im 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

Das sollte es tun. Das ViewModel benötigt keine Kenntnis der Steuerelemente der View. Die Ansicht wird nur an die Eigenschaft im ViewModel gebunden und unterscheidet sich nicht von der Ansicht, die an ein Bild oder eine andere Ressource gebunden wird. In diesem Fall handelt es sich bei dieser Ressource (Eigenschaft) lediglich um eine Benutzersteuerung. Es erlaubt das Testen, da ViewModel die Eigenschaft erstellt und besitzt und die Eigenschaft unabhängig von der Ansicht ist. Was Sicherheit angeht, weiß ich nicht, wie gut diese Implementierung ist. Bei Verwendung einer Funktion wird der Wert jedoch nicht in der Eigenschaft selbst gespeichert, auf die gerade die Eigenschaft zugreift.

6
William Rawson

Um das OP-Problem zu lösen, ohne die MVVM zu beschädigen, würde ich einen benutzerdefinierten Wertkonverter und einen Wrapper für den Wert (das Kennwort) verwenden, der aus der Kennwortbox abgerufen werden muss.

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

Im Ansichtsmodell:

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

Da das Ansichtsmodell IWrappedParameter<T> verwendet, muss es weder über PasswordBoxWrapper noch PasswordBoxConverter Bescheid wissen. Auf diese Weise können Sie das PasswordBox-Objekt vom Ansichtsmodell isolieren und das MVVM-Muster nicht unterbrechen.

In der Ansicht:

<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}}" />
6
Aoi Karasu

Ich stimme zu, dass es wichtig ist, zu vermeiden, dass das Kennwort an einer beliebigen Stelle gespeichert wird. Ich brauche jedoch immer noch die Möglichkeit, das Ansichtsmodell ohne Ansicht zu instantiieren und meine Tests dagegen auszuführen.

Die Lösung, die für mich funktionierte, bestand darin, die PasswordBox.Password-Funktion im Ansichtsmodell zu registrieren und vom Ansichtsmodell beim Ausführen des Anmeldecodes aufzurufen.

Dies bedeutet bedeutet eine Codezeile im Codebehind der Ansicht.

Also in meiner Login.xaml habe ich

<PasswordBox x:Name="PasswordBox"/>

und in Login.xaml.cs habe ich

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

dann habe ich in LoginViewModel.cs den PasswordHandler definiert

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

wenn ein Login erforderlich ist, ruft der Code den Handler auf, um das Kennwort aus der Ansicht zu erhalten ...

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

Wenn ich also das Viewmodel testen möchte, kann ich einfach PasswordHandler auf eine anonyme Methode festlegen, mit der ich jedes Kennwort übergeben kann, das ich im Test verwenden möchte.

5
mike mckechnie

Ich habe viel Zeit mit verschiedenen Lösungen verbracht. Ich mochte die Idee der Dekorateure nicht, das Durcheinander der Validierungs-Benutzeroberfläche, der Code dahinter ... wirklich? 

Am besten ist es jedoch, sich an eine benutzerdefinierte angefügte Eigenschaft zu halten und an Ihre SecureString-Eigenschaft in Ihrem Ansichtsmodell zu binden. Behalten Sie es so lange wie möglich dort. Wenn Sie einen schnellen Zugriff auf das einfache Kennwort benötigen, konvertieren Sie es mithilfe des folgenden Codes vorübergehend in eine unsichere Zeichenfolge:

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

Stellen Sie sicher, dass der GC Ihr UI-Element abrufen kann, und widerstehen Sie dem Drang, einen statischen Ereignishandler für das Ereignis PasswordChanged für die Variable PasswordBox zu verwenden. Ich entdeckte auch eine Anomalie, bei der das Steuerelement die Benutzeroberfläche nicht aktualisiert hat, wenn die SecurePassword-Eigenschaft zum Einrichten verwendet wird. Aus diesem Grund kopiere ich das Kennwort stattdessen in 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;
                }
            }
        }
    }
}

Und die XAML-Nutzung:

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

Meine Eigenschaft im Ansichtsmodell sah folgendermaßen aus:

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

Die Variable RequiredSecureString ist nur ein einfacher benutzerdefinierter Validator, der die folgende Logik aufweist:

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

Hier hast du es. Eine vollständige und getestete reine MVVM-Lösung.

4
MoonStom

Ich habe diese Methode verwendet und das Kennwortfeld übergeben, obwohl dies gegen die MVVM verstößt. Dies war für mich unerlässlich, da ich für meine Anmeldung in meiner Shell, die eine komplexe Shell-Umgebung ist, ein Inhaltssteuerelement mit Datenvorlage verwendet. Der Zugriff auf den Code hinter der Shell wäre also Mist gewesen.

Wenn ich die Passwortbox übergebe, ist es meiner Meinung nach dasselbe, wie der Zugriff auf die Steuerung von Code hinter mir, soweit ich weiß. Ich stimme den Passwörtern zu, nicht im Speicher behalten usw. Bei dieser Implementierung habe ich keine Eigenschaft für das Passwort im Ansichtsmodell.

Schaltfläche Befehl

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

Ich dachte, ich würde meine Lösung in den Mix werfen, da dies ein so gewöhnliches Problem ist ... und viele Optionen zu haben, ist immer eine gute Sache.

Ich wickelte einfach eine PasswordBox in eine UserControl ein und implementierte eine DependencyProperty, um binden zu können. Ich tue alles, um zu vermeiden, dass Klartext im Speicher abgelegt wird. Daher wird alles über eine SecureString und die PasswordBox.Password-Eigenschaft ausgeführt. Während der foreach-Schleife wird jedes Zeichen angezeigt, aber es ist sehr kurz. Wenn Sie sich Sorgen machen, dass Ihre WPF-Anwendung durch diesen kurzen Kontakt gefährdet werden könnte, haben Sie ehrlich gesagt größere Sicherheitsprobleme, die behandelt werden sollten.

Das Schöne daran ist, dass Sie nicht gegen MVVM-Regeln verstoßen, auch nicht gegen die "puristischen" Regeln, da dies eine UserControl ist, also Code hinter sich lassen darf. Wenn Sie es verwenden, können Sie eine reine Kommunikation zwischen View und ViewModel haben, ohne dass Ihre VideModel einen Teil von View oder die Quelle des Kennworts kennt. Stellen Sie einfach sicher, dass Sie an SecureString in Ihrer ViewModel gebunden sind.

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 - Keine bidirektionale Bindungsunterstützung.)

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

Verwendung von Version 1:

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

BindablePasswordBox.xaml.cs (Version 2 - Unterstützung für bidirektionale Bindung.)

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

Verwendung von Version 2:

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

sie können es mit angefügter Eigenschaft tun, sehen Sie es .. PasswordBox with MVVM

2
Rangel

Für vollständige Neulinge wie mich finden Sie hier ein vollständiges Beispiel der oben genannten Konamiman. Vielen Dank 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);
    }
}
2
fs_tigre

Für mich fühlen sich beide Dinge falsch an:

  • Klartext-Kennworteigenschaften implementieren
  • Senden der PasswordBox als Befehlsparameter an das ViewModel

Das Übertragen des SecurePassword (SecureString-Instanz) wie von Steve in CO beschrieben, scheint akzeptabel. Ich bevorzuge Behaviors dem Code hinter, und ich hatte auch die zusätzliche Anforderung, das Passwort vom Viewmodel zurücksetzen zu können.

Xaml (Password ist die ViewModel-Eigenschaft):

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

Verhalten:

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

    }
}
1
Mike Fuchs

In Windows Universal App

sie können diesen Code mit der Eigenschaft "Password" verwenden und mit der modelView verknüpfen

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

1
Baz08

Wenn Sie sich der Risiken bewusst sind, die diese Implementierung mit sich bringt, müssen Sie zur Synchronisierung des Kennworts zu Ihrem ViewModel einfach Mode = OneWayToSource hinzufügen.

XAML

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

Es ist sehr einfach . Erstellen Sie eine weitere Eigenschaft für das Kennwort und binden Sie diese mit TextBox

Alle Eingabeoperationen werden jedoch mit der tatsächlichen Kennworteigenschaft ausgeführt

private Zeichenfolge _Password;

    public string PasswordChar
    {
        get
        {
            string szChar = "";

            foreach(char szCahr in _Password)
            {
                szChar = szChar + "*";
            }

            return szChar;
        }

        set
        {
            _PasswordChar = value; NotifyPropertyChanged();
        }
    }

public string Passwort { erhalten { return _Password; }

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

1
Niji

Wie Sie sehen, bindet ich mich an Password, aber vielleicht bindet es an die statische Klasse.

Es ist eine beigefügte Eigenschaft . Diese Art von Eigenschaft kann auf jede Art von DependencyObject angewendet werden, nicht nur auf den Typ, in dem sie deklariert wird. Obwohl es in der statischen Klasse PasswordHelper deklariert ist, wird es auf die PasswordBox angewendet, auf der Sie es verwenden.

Um diese angefügte Eigenschaft zu verwenden, müssen Sie sie nur an die Password -Eigenschaft in Ihrem ViewModel binden:

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

Wie bereits erwähnt, sollte VM die Ansicht nicht kennen, aber das Passieren der gesamten PasswordBox scheint der einfachste Ansatz zu sein. Verwenden Sie Reflection, um die Password-Eigenschaft daraus zu extrahieren, anstatt Parameter an PasswordBox zu übergeben. In diesem Fall erwartet VM eine Art Kennwortcontainer mit der Eigenschaft Kennwort (ich verwende RelayCommands von 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;
    }
}

Es kann leicht mit anonymen Klassen getestet werden:

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

Wenn Sie möchten, dass dies alles in nur einem Steuerelement und einem Befehl kombiniert wird

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

Und auf deiner Vm (wie Konamiman zeigte)

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));
0
Cristian G

In der ViewModel-Beispielanwendung des WPF Application Framework (WAF) - Projekts finden Sie eine Lösung für die PasswordBox.

Justin hat jedoch recht. Übergeben Sie das Kennwort nicht als Nur-Text zwischen View und ViewModel. Verwenden Sie stattdessen SecureString (siehe MSDN PasswordBox).

0
jbe

Ich verwende eine prägnante MVVM-freundliche Lösung, die bisher noch nicht erwähnt wurde. Zuerst benenne ich die PasswordBox in XAML:

<PasswordBox x:Name="Password" />

Dann füge ich einen einzelnen Methodenaufruf zum View-Konstruktor hinzu:

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

Und das ist es. Das Ansichtsmodell erhält eine Benachrichtigung, wenn es über DataContext mit einer Ansicht verbunden ist, und eine weitere Benachrichtigung, wenn es getrennt wird. Der Inhalt dieser Benachrichtigung kann über die Lambdas konfiguriert werden. Normalerweise handelt es sich jedoch lediglich um einen Setter- oder Methodenaufruf für das Ansichtsmodell, der die problematische Steuerung als Parameter übergibt.

Sie kann MVVM-freundlich gestaltet werden, indem die Ansichtsschnittstelle anstelle von untergeordneten Steuerelementen angezeigt wird.

Der obige Code basiert auf Helfer-Klasse , die in meinem Blog veröffentlicht wurde.

0
Robert Važan

nun, meine Antwort ist einfacher, nur für das MVVM-Muster

im Klassen-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;

}

die Kennworteigenschaft von PasswordBox, die von win bereitgestellt wird, oder WatermarkPasswordBox, die von XCeedtoolkit bereitgestellt wird, generiert RoutedEventArgs, damit Sie sie binden können.

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

oder

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

ich habe gemacht wie:

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

Für mich geht das!

Ich habe ewig versucht, das hier zum Laufen zu bringen. Am Ende habe ich aufgegeben und nur das PasswordBoxEdit von DevExpress verwendet.

Es ist die einfachste Lösung aller Zeiten, da das Binden ohne schreckliche Tricks möglich ist.

Lösung auf der DevExpress-Website

Für das Protokoll bin ich in keiner Weise mit DevExpress verbunden.

0
Contango

Ich habe eine Authentifizierungsprüfung verwendet, gefolgt von einem U-Boot, das von einer Mediator-Klasse zur Ansicht aufgerufen wurde (die auch eine Authentifizierungsprüfung implementiert), um das Kennwort in die Datenklasse zu schreiben.

Es ist keine perfekte Lösung. Es wurde jedoch mein Problem behoben, dass ich das Passwort nicht verschieben konnte.

0
Miles

Hier ist meine Meinung dazu: 

  1. Die Verwendung einer angefügten Eigenschaft zum Binden des Kennworts verhindert den Zweck der Sicherung des Kennworts. Die Password-Eigenschaft eines Passwortfelds kann aus einem bestimmten Grund nicht gebunden werden.

  2. Wenn Sie das Kennwortfeld als Befehlsparameter übergeben, wird das ViewModel über das Steuerelement informiert. Dies funktioniert nicht, wenn Sie ViewModel plattformübergreifend wiederverwendbar machen möchten. Machen Sie Ihre VM nicht auf Ihre Ansicht oder andere Steuerelemente aufmerksam.

  3. Ich denke nicht, dass die Einführung einer neuen Eigenschaft, einer Benutzeroberfläche, das Abonnieren von Ereignissen mit geändertem Kennwort oder andere komplizierte Dinge für eine einfache Aufgabe des Kennworts erforderlich sind. 

XAML

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

Code hinter - Die Verwendung von Code verstößt nicht unbedingt gegen MVVM. Solange Sie keine Geschäftslogik verwenden.

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

ViewModel

LoginCommand = new RelayCommand<Func<string>>(getpwd=> { service.Login(username, getpwd()); });
0
Lance

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

;) einfach! 

0
Hector Lobo