Jak powiązać z PasswordBox w MVVM

Natknąłem się na problem z przywiązaniem do hasła. Wydaje się, że to zagrożenie bezpieczeństwa, ale używam wzorca MVVM, więc chcę to ominąć. Znalazłem tu jakiś ciekawy Kod (czy ktoś używał tego lub czegoś podobnego?)

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

Technicznie wygląda świetnie, ale nie wiem, jak odzyskać hasło.

W zasadzie mam właściwości w moim LoginViewModel dla Username i Password. Username jest w porządku i jest działa jak TextBox.

Użyłem powyższego kodu i wpisałem to

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

Kiedy miałem PasswordBox jako TextBox i Binding Path=Password wtedy nieruchomość w moim LoginViewModel została zaktualizowana.

Mój kod jest bardzo prosty, w zasadzie mam Command do mojego Button. Po naciśnięciu wywołana jest CanLogin, a jeśli zwraca true, wywołuje Login.
Możesz zobaczyć, że sprawdzam moją nieruchomość pod kątem Username tutaj, co działa świetnie.

W Login wysyłam do mojego serwisu Username i Password, Username zawiera dane z mojego View Ale Password jest 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"/>

Mam swoje TextBox, to żaden problem, ale w moim ViewModel Password jest pusty.

Czy robię coś nie tak, czy tracę krok?

Umieściłem breakpoint i na pewno kod wchodzi do statycznej klasy pomocniczej, ale nigdy nie aktualizuje mojej Password w mojej ViewModel.

Author: wonea, 2009-09-27

30 answers

Przepraszam, ale źle to robisz.

Ludzie powinni mieć następujące wytyczne bezpieczeństwa wytatuowane na wewnętrznej stronie powiek:
nigdy nie przechowuj haseł tekstowych w pamięci.

Powód, dla którego WPF / Silverlight PasswordBox nie ujawnia DP dla właściwości hasła, jest związany z bezpieczeństwem.
Jeśli WPF / Silverlight miałby zachować DP dla hasła, wymagałoby to, aby framework zachował samo hasło niezaszyfrowane w pamięci. Który jest uważany za dość kłopotliwy wektor ataku. PasswordBox używa szyfrowanej pamięci (swego rodzaju) i jedynym sposobem dostępu do hasła jest właściwość CLR.

Sugerowałbym, że przy dostępie do PasswordBox.Hasło właściwość CLR powstrzymasz się od umieszczania jej w dowolnej zmiennej lub jako wartości dla dowolnej właściwości.
Utrzymanie hasła w postaci zwykłego tekstu na pamięci RAM Komputera klienta jest nie do zabezpieczenia.
Więc pozbądź się tego " public string Password { get; set;}", które tam masz.

Podczas dostępu do PasswordBox.Hasło, po prostu je wyjmij i wyślij na serwer jak najszybciej. Nie zachowuj wartości hasła i nie traktuj go tak, jak każdy inny tekst maszyny klienta. Nie przechowuj jasnych haseł tekstowych w pamięci.

Wiem, że to łamie wzorzec MVVM, ale nigdy nie powinieneś wiązać się z PasswordBox.Dołączone hasło DP, przechowywać hasło w ViewModel lub innych podobnych sztuczek.

Jeśli szukasz nadmiarowego rozwiązania, oto jeden:
1. Utwórz interfejs IHavePassword za pomocą jednej metody, która zwraca hasło Wyczyść tekst.
2. Niech twój UserControl zaimplementuje interfejs IHavePassword.
3. Zarejestruj instancję UserControl w IoC jako implementującą interfejs IHavePassword.
4. Gdy odbywa się żądanie serwera wymagające hasła, zadzwoń do IoC w celu wdrożenia IHavePassword i tylko wtedy uzyskaj upragnione hasło.

Tylko moje zdanie na ten temat.

-- Justin

 144
Author: JustinAngel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-09 22:09:58

Moje 2 grosze:

Stworzyłem kiedyś typowe okno logowania (pola użytkownika i hasła, plus przycisk "Ok") przy użyciu WPF i MVVM. Rozwiązałem problem związany z hasłem, po prostu przekazując samą kontrolkę PasswordBox jako parametr poleceniu dołączonemu do przycisku "Ok". Więc w widoku miałem:

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

I w ViewModel, Metoda Execute dołączonego polecenia była następująca:

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

To trochę narusza wzorzec MVVM, ponieważ teraz ViewModel coś wie o tym, jak widok jest realizowany, ale w tym konkretnym projekcie mogłem sobie na to pozwolić. Mam nadzieję, że jest to również przydatne dla kogoś.

 173
Author: Konamiman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-10 17:57:31

Może czegoś mi brakuje, ale wydaje się, że większość z tych rozwiązań komplikuje sprawy i eliminuje bezpieczne praktyki.

Ta metoda nie narusza wzorca MVVM i zachowuje pełne bezpieczeństwo. Tak, technicznie jest to kod ZA, ale to nic więcej niż" specjalny przypadek " wiążący. ViewModel nadal nie ma wiedzy o implementacji widoku, co moim zdaniem robi, jeśli próbujesz przekazać PasswordBox do ViewModel.

Kod Z Tyłu != Automatyczne naruszenie MVVM. Wszystko zależy od tego, co z nim zrobisz. W tym przypadku, jesteśmy po prostu ręcznie kodowania wiązania, więc jego wszystkie uważane za część implementacji UI i dlatego jest ok.

W ViewModel, tylko prosta właściwość. Zrobiłem to "tylko pisać", ponieważ nie powinno być potrzeby, aby pobrać go z zewnątrz ViewModel z jakiegokolwiek powodu, ale to nie musi być. Zauważ, że jest to SecureString, a nie tylko ciąg znaków.

public SecureString SecurePassword { private get; set; }

W xaml ustawiasz PasswordChanged opiekun imprezy.

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>

W kodzie za:

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

Dzięki tej metodzie Twoje hasło pozostaje w SecureString przez cały czas, a tym samym zapewnia maksymalne bezpieczeństwo. Jeśli naprawdę nie dbasz o bezpieczeństwo lub potrzebujesz hasła do czystego tekstu dla wymagającej go metody (Uwaga: większość metod. NET, które wymagają hasła, obsługuje również opcję SecureString, więc możesz nie potrzebować hasła do czystego tekstu, nawet jeśli tak myślisz), możesz po prostu użyć hasła nieruchomość zamiast. Tak:

(ViewModel property)

public string Password { private get; set; }

(kod ZA)

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

Jeśli chcesz, aby rzeczy były mocno wpisywane, możesz zastąpić (dynamiczną) obsadę interfejsem swojego modelu widoku. Ale tak naprawdę, "normalne" wiązania danych nie są mocno wpisane albo, więc to nie jest taka wielka sprawa.

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

Więc najlepsze ze wszystkich światów-Twoje hasło jest bezpieczne, Twój ViewModel po prostu ma właściwość jak każda inna właściwość, a Twój widok jest samodzielny bez wymagane referencje zewnętrzne.

 141
Author: Steve In CO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-28 17:45:19

Możesz użyć tego XAML:

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

I ta metoda wykonania polecenia:

private void ExecutePasswordChangedCommand(PasswordBox obj)
{ 
   if (obj != null)
     Password = obj.Password;
}
 18
Author: Sergey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-02 13:52:44

To mi pasuje.

<Button Command="{Binding Connect}" 
        CommandParameter="{Binding ElementName=MyPasswordBox}"/>
 12
Author: Vladislav Borovikov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-02 14:12:10

Prostym rozwiązaniem bez naruszania wzorca MVVM jest wprowadzenie zdarzenia (lub delegata) do modelu widoku, który zbiera hasło.

W widoku :

public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;

Z tymi wydarzeniami:

class HarvestPasswordEventArgs : EventArgs
{
    public string Password;
}

W widoku zapisz się do zdarzenia podczas tworzenia modelu widoku i wprowadź wartość hasła.

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

W widoku , Gdy potrzebujesz hasła, możesz odpalić Zdarzenie i pobrać hasło z tam:

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

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

LoginHelpers.Login(Username, pwargs.Password);
 9
Author: Jan Willem B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 09:29:19

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;
        }
    }
}
 8
Author: Taylor Leese,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-23 14:18:27

Ta implementacja jest nieco inna. Przekazujesz pole passwordbox do powiązania View thru właściwości w ViewModel, nie używa ona żadnych param poleceń. ViewModel pozostaje nieświadomy widoku. Mam projekt VB vs 2010, który można pobrać ze SkyDrive. Przykład Wpf MvvM PassWordBox.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511

Sposób, w jaki używam PasswordBox w aplikacji Wpf MvvM jest dość uproszczony i mi pasuje. To nie znaczy, że myślę, że jest to właściwy sposób lub najlepszy sposób. Jest to tylko implementacja użycia PasswordBox i wzorca MvvM.

Zasadniczo tworzysz publiczną właściwość readonly, którą Widok może powiązać jako PasswordBox (faktyczna Kontrola) przykład:

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

Używam pola zapasowego tylko do samo inicjalizacji właściwości.

Następnie z Xaml wiąże się Zawartość ContentControl lub kontenera kontrolnego Przykład:

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

Stamtąd masz pełną kontrolę nad passwordbox używam również PasswordAccessor (tylko funkcja łańcucha znaków), aby zwrócić wartość hasła podczas logowania lub cokolwiek innego chcesz hasło do. W przykładzie mam właściwość publiczną w ogólnym modelu obiektu użytkownika. Przykład:

Public Property PasswordAccessor() As Func(Of String)

W obiekcie User właściwość password string jest tylko odczytywana bez żadnego magazynu kopii zapasowej. Przykład:

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

Wtedy w ViewModel upewniam się, że Accessor jest utworzony i ustawiony na PasswordBox.Właściwość hasła" Przykład:

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

Kiedy muszę hasło ciąg powiedzieć do logowania po prostu dostać User objects Password właściwość, która naprawdę wywołuje funkcję, aby pobrać hasło i zwrócić go, a następnie rzeczywiste hasło nie jest przechowywane przez obiekt User. Przykład: 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
To powinno wystarczyć. Model widoku nie potrzebuje żadnej wiedzy o kontrolkach widoku. Widok Po prostu wiąże się z właściwością w ViewModel, nie różni się niczym od powiązania widoku z obrazem lub innym zasobem. W tym przypadku zasób (właściwość) jest po prostu kontrolerem użytkownika. Pozwala to na testowanie, gdy model widoku tworzy i jest właścicielem Właściwości, a właściwość jest niezależna od widoku. Co do bezpieczeństwa To Nie wiem jak dobra jest ta implementacja. Jednak przy użyciu funkcji wartość nie jest przechowywana w samej właściwości, do której dostęp ma właściwość.
 6
Author: William Rawson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-31 06:36:36

Aby rozwiązać problem OP bez łamania MVVM, użyłbym niestandardowego konwertera wartości i opakowania dla wartości (hasła), które mają być pobrane z pola hasła.

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

W modelu widoku:

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

Ponieważ model widoku używa IWrappedParameter<T>, nie musi mieć żadnej wiedzy na temat PasswordBoxWrapper ani PasswordBoxConverter. W ten sposób można oddzielić obiekt PasswordBox od modelu widoku i nie łamać wzorca MVVM.

W widoku:

<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
Author: Aoi Karasu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-12 14:56:41

Chociaż zgadzam się, że ważne jest, aby unikać przechowywania hasła w dowolnym miejscu, nadal potrzebuję możliwości tworzenia instancji modelu widoku bez widoku i wykonywania testów na nim.

Rozwiązaniem, które zadziałało dla mnie, było zarejestrowanie Skrzynki haseł.Funkcja hasła z modelem widoku i niech model widoku wywoła go podczas wykonywania kodu logowania.

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

Więc, w moim loginie.xaml Mam

<PasswordBox x:Name="PasswordBox"/>

Oraz w Login.xaml.cs Mam

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

Następnie w LoginViewModel.cs mam zdefiniowany PasswordHandler

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

I gdy konieczne jest logowanie, kod wywołuje funkcję obsługi, aby uzyskać hasło z widoku...

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

W ten sposób, kiedy chcę przetestować viewmodel, mogę po prostu ustawić PasswordHandler NA anonimową metodę, która pozwoli mi dostarczyć dowolne hasło, którego chcę użyć w teście.

 5
Author: mike mckechnie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-16 13:07:57

[11]}spędziłem dużo czasu na analizowaniu różnych rozwiązań. Nie podobał mi się pomysł dekoratorów, zachowania psują interfejs walidacji, kod z tyłu... naprawdę?

Najlepiej jest trzymać się niestandardowej dołączonej właściwości i powiązać ją z twoją SecureString właściwością w modelu widoku. Trzymaj go tam tak długo, jak możesz. Jeśli potrzebujesz szybkiego dostępu do zwykłego hasła, tymczasowo przekonwertuj je na niezabezpieczony ciąg znaków za pomocą poniższego kodu:

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

Upewnij się, że zezwalasz na GC aby zebrać element interfejsu użytkownika, należy oprzeć się pokusie użycia statycznej obsługi zdarzenia PasswordChanged na PasswordBox. Odkryłem również anomalię, w której kontrolka nie aktualizowała interfejsu użytkownika podczas korzystania z właściwości SecurePassword do konfiguracji, dlatego zamiast tego kopiuję hasło do 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;
                }
            }
        }
    }
}

I użycie XAML:

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

Moja własność w modelu widoku wyglądała tak:

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

RequiredSecureString jest po prostu prostym walidatorem niestandardowym, który ma następujące logika:

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

Tutaj masz to. Kompletne i przetestowane rozwiązanie Pure MVVM.

 4
Author: MoonStom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 06:37:21

Użyłem tej metody i przekazałem pole Hasło, chociaż to nie narusza MVVM to było istotne dla mnie, ponieważ używałem kontroli treści z szablonem danych dla mojego logowania w mojej powłoki, która jest złożonym środowisku powłoki. Więc dostęp do kodu z tyłu powłoki byłby beznadziejny.

Podanie hasła wydaje mi się takie samo, jak dostęp do kontroli z kodu z tyłu, o ile wiem. Zgadzam się hasła, nie trzymać w pamięci itp w tej implementacji nie mam właściwość dla hasła w modelu widoku.

Polecenie Przycisku

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
Author: Legz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-30 10:52:16

Pomyślałem, że wrzucę moje rozwiązanie do miksu, ponieważ jest to tak powszechny problem... a posiadanie wielu opcji jest zawsze dobrą rzeczą.

Po prostu owinąłem PasswordBox w UserControl i zaimplementowałem DependencyProperty, aby móc wiązać. Robię wszystko, co w mojej mocy, aby uniknąć zapisywania wyraźnego tekstu w pamięci, więc wszystko odbywa się za pomocą właściwości SecureString i PasswordBox.Password. Podczas pętli foreach każdy znak zostaje odsłonięty, ale jest bardzo krótki. Szczerze mówiąc, jeśli martwisz się o swój WPF aplikacja może być zagrożona z powodu tej krótkiej ekspozycji, masz większe problemy z bezpieczeństwem, które należy rozwiązać.

Piękno tego jest to, że nie łamiesz żadnych zasad MVVM, nawet tych "purystycznych", ponieważ jest to UserControl, więc wolno mieć kod-behind. Kiedy go używasz, możesz mieć czystą komunikację między View i ViewModel bez Twojej VideModel świadomości jakiejkolwiek części View lub źródła hasła. Tylko upewnij się, że wiążesz się z SecureString w swoim 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 (Wersja 1 - Brak obsługi wiązań dwukierunkowych.)

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

Użycie wersji 1:

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

BindablePasswordBox.xaml.cs (Wersja 2-posiada obsługę wiązań dwukierunkowych.)

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

Użycie wersji 2:

<local:BindanblePasswordBox Width="150" HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Password="{Binding Password, Mode=TwoWay}"/>
 3
Author: B.K.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-27 22:13:52

Możesz to zrobić z dołączoną właściwością, zobacz to.. PasswordBox z MVVM

 2
Author: Rangel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:47

Jak widzisz, wiążę się z hasłem, ale może wiąże go z klasą statyczną..

Jest to dołączona właściwość . Tego rodzaju właściwość może być zastosowana do dowolnego rodzaju DependencyObject, a nie tylko do typu, w którym jest zadeklarowana. Więc nawet jeśli jest zadeklarowana w klasie statycznej PasswordHelper, jest stosowana do PasswordBox, na której jej używasz.

Aby użyć tej dołączonej właściwości, wystarczy powiązać ją z Password właściwością w Twoim ViewModel :

<PasswordBox w:PasswordHelper.Attach="True" 
         w:PasswordHelper.Password="{Binding Password}"/>
 1
Author: Thomas Levesque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-27 16:45:54

Jak wspomniano wcześniej VM powinien być nieświadomy widoku, ale przekazanie całego hasła wygląda na najprostsze podejście. Więc może zamiast oddania przekazanego parametru do PasswordBox użyj Reflection, aby wyodrębnić z niego właściwość hasła. W tym przypadku vm oczekuje pewnego rodzaju kontenera haseł z hasłem właściwości (używam RelayCommands z 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;
    }
}

Można go łatwo przetestować za pomocą anonimowej klasy:

var passwordContainer = new
    {
        Password = "password"
    };
 1
Author: mokula,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-03 09:09:12

Dla mnie obie te rzeczy są złe:

  • implementacja właściwości hasła clear text
  • wysyłanie PasswordBox jako parametru polecenia do ViewModel

Przeniesienie SecurePassword (instancja SecureString) opisane przez Steve in CO wydaje się dopuszczalne. Wolę Behaviors kodować z tyłu, a także miałem dodatkowy wymóg, aby móc zresetować hasło z viewmodel.

Xaml (Password jest ViewModel własność):

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

Zachowanie:

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
Author: Mike Fuchs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:46

W Windows universal app

Możesz użyć tego kodu z właściwością "Password" i powiązać go z modelView

 <PasswordBox x:Uid="PasswordBox" Password="{Binding Waiter.Password, Mode=TwoWay}" Name="txtPassword" HorizontalAlignment="Stretch" Margin="50,200,50,0" VerticalAlignment="Top"/>
 1
Author: Baz08,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-12 09:09:34

To bardzo proste . Utwórz inną właściwość dla hasła i połącz ją z TextBox

Ale wszystkie operacje wejściowe wykonywane są z rzeczywistą właściwością hasła

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

 1
Author: Niji,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-26 13:01:09

Dla każdego, kto jest świadomy ryzyka, jakie narzuca Ta implementacja, aby hasło było zsynchronizowane z Twoim ViewModel wystarczy dodać Mode=OneWayToSource .

XAML

<PasswordBox
    ff:PasswordHelper.Attach="True"
    ff:PasswordHelper.Password="{Binding Path=Password, Mode=OneWayToSource}" />
 1
Author: Kevin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-06 06:50:55

Znajdziesz rozwiązanie dla PasswordBox w przykładowej aplikacji ViewModel WPF Application Framework (WAF) Projekt.

Jednak Justin ma rację. Nie przekazuj hasła jako zwykłego tekstu między View a ViewModel. Zamiast tego użyj SecureString (Patrz MSDN PasswordBox).

 0
Author: jbe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-02 17:58:38

Zrobiłem tak:

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

dla mnie działa!

 0
Author: José Roberto Cuello Alcaraz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-15 20:23:10

Użyłem sprawdzania uwierzytelniania, a następnie sub wywoływanego przez klasę mediatora do widoku (która również implementuje sprawdzanie uwierzytelniania), aby zapisać hasło do klasy danych.

Nie jest to idealne rozwiązanie, jednak rozwiązało to mój problem braku możliwości przeniesienia hasła.

 0
Author: Miles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-22 15:12:38

Używam zwięzłego rozwiązania przyjaznego MVVM, o którym jeszcze nie wspomniano. Najpierw nazwę PasswordBox w XAML:

<PasswordBox x:Name="Password" />

Następnie dodaję wywołanie jednej metody do konstruktora widoku:

public LoginWindow()
{
    InitializeComponent();
    ExposeControl<LoginViewModel>.Expose(this, view => view.Password,
        (model, box) => model.SetPasswordBox(box));
}
I to wszystko. Model widoku otrzyma powiadomienie, gdy zostanie dołączony do widoku poprzez DataContext, a kolejne powiadomienie, gdy zostanie odłączony. Zawartość tego powiadomienia można konfigurować za pomocą lambda, ale zazwyczaj jest to tylko wywołanie settera lub metody na modelu widoku, przekazujące problematyczna Kontrola jako parametr.

Może być bardzo łatwo przyjazny dla MVVM, mając interfejs view expose zamiast kontrolek potomnych.

Powyższy kod opiera się na klasie pomocniczej opublikowanej na moim blogu.

 0
Author: Robert Važan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-16 17:54:22

Spędziłem wieki próbując to naprawić. W końcu poddałem się i po prostu użyłem PasswordBoxEdit z DevExpress.

Jest to najprostsze rozwiązanie w historii, ponieważ pozwala na wiązanie bez wyciągania żadnych strasznych sztuczek.

Rozwiązanie na stronie DevExpress

Dla przypomnienia, nie jestem związany z DevExpress w żaden sposób.

 0
Author: Contango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-28 16:10:54

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

 0
Author: Hector Lobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 19:49:03

Cóż moja odpowiedź jest prostsza tylko w wzorze MVVM

W klasie 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;

}

Właściwość PasswordBox, który zapewnia win lub WatermarkPasswordBox, który zapewnia XCeedtoolkit, generuje RoutedEventArgs, dzięki czemu można go powiązać.

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

Lub

<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
Author: carlos rodriguez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-31 18:17:58

Jeśli chcesz, aby łączyło to wszystko tylko w jednym sterowaniu i jednym poleceniu

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

I na Twojej maszynie wirtualnej (jak pokazał Konamiman)

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
Author: Cristian G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-25 16:47:40

Oto moje zdanie na ten temat:

  1. Użycie dołączonej właściwości do powiązania hasła uniemożliwia zabezpieczenie hasła. Właściwość hasła pola hasła nie może być bindowana z jakiegoś powodu.

  2. Podanie pola password jako parametru command spowoduje, że ViewModel będzie świadomy kontroli. To nie zadziała, jeśli planujesz zrobić ViewModel wielokrotnego użytku cross platform. nie uświadamiaj swojej maszynie wirtualnej swojego widoku ani żadnych innych sterowanie.

  3. Nie sądzę, aby wprowadzenie nowej właściwości, interfejsu, subskrybowanie zdarzeń zmienionych hasłem lub innych skomplikowanych rzeczy było konieczne do prostego zadania podania hasła.

XAML

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

Code behind-użycie kodu behind niekoniecznie narusza MVVM. O ile nie wpiszesz w to żadnej logiki biznesowej.

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

ViewModel

LoginCommand = new RelayCommand<Func<string>>(getpwd=> { service.Login(username, getpwd()); });
 0
Author: Lance,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-22 14:22:02

Dla początkujących, takich jak ja, oto pełna próbka robocza tego, co Konamiman zasugerował powyżej. Dzięki 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);
    }
}
 0
Author: fs_tigre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-07 10:59:42