我遇到了绑定到密码盒的问题。这似乎是一个安全风险,但我正在使用MVVM模式,所以我希望绕过这个。我在这里发现了一些有趣的代码(有人使用过这个或类似的代码吗?)

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

从技术上讲,它看起来很棒,但我不确定如何检索密码。

我基本上有属性在我的LoginViewModel用户名和密码。用户名是好的,正在工作,因为它是一个文本框。

我使用上面的代码,并输入这个

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

当我有PasswordBox作为一个文本框和绑定路径=密码,然后在我的LoginViewModel属性更新。

我的代码非常简单,基本上我有一个命令为我的按钮。当我按下它CanLogin被调用,如果它返回真,它调用Login。 你可以看到,我检查了我的用户名属性,这很好。

在登录我发送到我的服务的用户名和密码,用户名包含数据从我的视图,但密码是空|空

private DelegateCommand loginCommand;

public string Username { get; set; }
public string Password { get; set; }


public ICommand LoginCommand
{
    get
    {
        if (loginCommand == null)
        {
            loginCommand = new DelegateCommand(
                Login, CanLogin );
        }
        return loginCommand;
    }
}

private bool CanLogin()
{
    return !string.IsNullOrEmpty(Username);
}

private void Login()
{
    bool result = securityService.IsValidLogin(Username, Password);

    if (result) { }
    else { }
}

这就是我正在做的

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

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

我有我的文本框,这是没有问题的,但在我的ViewModel密码是空的。

是我做错了什么还是漏了一步?

我放了一个断点,果然代码进入静态助手类,但它从不更新我的ViewModel中的密码。


当前回答

也许我遗漏了一些东西,但似乎大多数解决方案都把事情复杂化了,并且忽略了安全实践。

此方法不违反MVVM模式,并保持完全的安全性。是的,从技术上讲,它是后面的代码,但它只不过是一个“特殊情况”绑定。ViewModel仍然没有视图实现的知识,在我看来,如果你试图将PasswordBox传递到ViewModel,它就会知道。

Code Behind !=自动违反MVVM。这完全取决于你怎么处理它。在这种情况下,我们只是手动编码绑定,所以它都被认为是UI实现的一部分,因此是ok的。

在ViewModel中,只是一个简单的属性。我把它设置为“只写”,因为无论出于什么原因,都不需要从ViewModel外部检索它,但它并不一定要这样。注意,它是一个SecureString,而不仅仅是一个字符串。

public SecureString SecurePassword { private get; set; }

在xaml中,您设置了PasswordChanged事件处理程序。

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>

在后面的代码中:

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

使用这种方法,您的密码始终保持在SecureString中,因此提供了最大的安全性。如果你真的不关心安全性,或者你需要为需要它的下游方法提供明文密码(注意:大多数需要密码的。net方法也支持SecureString选项,所以你可能真的不需要明文密码,即使你认为你需要),你可以只使用password属性。是这样的:

(ViewModel财产)

public string Password { private get; set; }

(代码后面)

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

如果你想保持事物的强类型,你可以用ViewModel的接口代替(动态的)强制转换。但实际上,“正常的”数据绑定也不是强类型的,所以这不是什么大问题。

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

所以最好的是-你的密码是安全的,你的ViewModel就像任何其他属性一样有一个属性,你的View是自包含的,不需要外部引用。

其他回答

这对我来说很好。

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

您可以在WPF应用程序框架(WAF)项目的ViewModel样例应用程序中找到PasswordBox的解决方案。

然而,犹斯丁是对的。不要在View和ViewModel之间以纯文本的形式传递密码。请使用SecureString代替(参见MSDN PasswordBox)。

<UserControl x:Class="Elections.Server.Handler.Views.LoginView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:cal="http://www.caliburnproject.org" mc:Ignorable="d" Height="531" Width="1096"> <ContentControl> <ContentControl.Background> <ImageBrush/> </ContentControl.Background> <Grid > <Border BorderBrush="#FFABADB3" BorderThickness="1" HorizontalAlignment="Left" Height="23" Margin="900,100,0,0" VerticalAlignment="Top" Width="160"> <TextBox TextWrapping="Wrap"/> </Border> <Border BorderBrush="#FFABADB3" BorderThickness="1" HorizontalAlignment="Left" Height="23" Margin="900,150,0,0" VerticalAlignment="Top" Width="160"> <PasswordBox x:Name="PasswordBox"/> </Border> <Button Content="Login" HorizontalAlignment="Left" Margin="985,200,0,0" VerticalAlignment="Top" Width="75"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <cal:ActionMessage MethodName="Login"> <cal:Parameter Value="{Binding ElementName=PasswordBox}" /> </cal:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </Button> </Grid> </ContentControl> </UserControl>

using System; using System.Windows; using System.Windows.Controls; using Caliburn.Micro; namespace Elections.Server.Handler.ViewModels { public class LoginViewModel : PropertyChangedBase { MainViewModel _mainViewModel; public void SetMain(MainViewModel mainViewModel) { _mainViewModel = mainViewModel; } public void Login(Object password) { var pass = (PasswordBox) password; MessageBox.Show(pass.Password); //_mainViewModel.ScreenView = _mainViewModel.ControlPanelView; //_mainViewModel.TitleWindow = "Panel de Control"; //HandlerBootstrapper.Title(_mainViewModel.TitleWindow); } } }

,简单!

对于像我这样的新手,这里有一个完整的工作样本。谢谢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);
    }
}

为了在不破坏MVVM的情况下解决OP问题,我将使用自定义值转换器和必须从密码框中检索的值(密码)的包装器。

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

在视图模型中:

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

因为视图模型使用IWrappedParameter<T>,所以它不需要任何关于PasswordBoxWrapper或PasswordBoxConverter的知识。这样您就可以将PasswordBox对象与视图模型隔离开来,并且不会破坏MVVM模式。

在视图中:

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