如何在MVVM模式下将文本框设置为“密码框”并显示星号?

62

我该如何在XAML中实现这个功能:

伪代码:

<TextBox Text="{Binding Password}" Type="Password"/>

为了在用户输入密码时显示星号或点号。

我尝试了PasswordCharPasswordBox不同的示例,但无法使它们工作。

例如,我可以像这里所示那样实现:

<PasswordBox Grid.Column="1" Grid.Row="1"
    PasswordChar="*"/>

但是我当然想将Text属性绑定到我的ViewModel,这样当按钮被点击时,我可以发送绑定的TextBox的值(不使用代码后台实现),我想要实现这个:

<TextBox Grid.Column="1" Grid.Row="0" 
    Text="{Binding Login}" 
    Style="{StaticResource FormTextBox}"/>
<PasswordBox Grid.Column="1" Grid.Row="1"
    Text="{Binding Password}" 
    PasswordChar="*"
    Style="{StaticResource FormTextBox}"/>

但是PasswordBox没有Text属性。


1
你能详细说明为什么 PasswordChar 和 PasswordBox 没有起作用吗? - Andrew Hare
似乎使用MVVM并不是那么直接,我在这里找到了一个类似的问题:https://dev59.com/rnNA5IYBdhLWcg3wF5qO - Edward Tanguay
9个回答

39

要获取或设置PasswordBox中的密码,请使用Password属性,例如:

string password = PasswordBox.Password;

据我所知,这不支持数据绑定,因此您需要在代码后台中设置该值,并相应地更新它。


5
我不认为那真的有效,至少在我尝试过后不行。 - Brandon
正如你之前找到的问题所提到的(被接受的答案),有一种方法可以解决这个问题,只是取决于你是否认为它值得额外的代码。背后的代码是最简单的方法。 - Brandon
1
文章正确指出“不能使用数据绑定是很遗憾的”,但它提供了一个两页的附加属性解决方法,让我们看看它是否实际可行:http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html - Edward Tanguay
3
不允许在PasswordBox上进行数据绑定的原因似乎是出于安全考虑,具体讨论可参考此处链接:http://social.msdn.microsoft.com/forums/en-US/wpf/thread/7ca97b60-2d8e-4a27-8c5b-b8d5d7370a5e/ - Edward Tanguay
3
我测试了这个解决方案,它非常好用:http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html - Edward Tanguay
显示剩余3条评论

19

正如Tasnim Fabiha提到的,可以更改TextBox的字体以显示只有点号/星号。但我没有找到他所说的那个字体...因此我给出我的工作示例:

<TextBox Text="{Binding Password}" 
     FontFamily="pack://application:,,,/Resources/#password" />

仅仅复制粘贴是无效的,首先你需要下载所提到的字体“password.ttf”

链接地址:https://github.com/davidagraf/passwd/blob/master/public/ttf/password.ttf

然后将其复制到您的项目资源文件夹中(项目->属性->资源->添加资源->添加现有文件)。然后将其生成操作设置为:资源。

完成这些步骤后,您将只看到点,但仍然可以从中复制文本,因此需要禁用CTRL+C快捷方式,方法如下:

<TextBox Text="{Binding Password}" 
     FontFamily="pack://application:,,,/Resources/#password" > 
    <TextBox.InputBindings>
        <!--Disable CTRL+C (COPY) -->
        <KeyBinding Command="ApplicationCommands.NotACommand"
            Key="C"
            Modifiers="Control" />
        <!--Disable CTRL+X (CUT) -->
        <KeyBinding Command="ApplicationCommands.NotACommand"
            Key="X"
            Modifiers="Control" />
    </TextBox.InputBindings>
    <TextBox.ContextMenu>
        <!--Hide context menu where you could copy/cut as well -->
        <ContextMenu Visibility="Collapsed" />
    </TextBox.ContextMenu>
</TextBox>

编辑: 根据@CodingNinja的评论添加了Ctrl+X和上下文菜单禁用功能,谢谢。


1
对我来说它可以工作(也在.NET 4.8上进行了测试)。尝试调整FontFamily的位置,我认为它可能会略有不同,并确保您将其设置为RESOURCE。 - ssamko
1
不客气。为了使其100%安全,还要禁用CTRL + X(剪切)快捷键。 - ssamko
@Luishg 我尝试了ssamko的解决方案,第一次并没有起作用。然后我像这样写了完整路径 <TextBox Text="{Binding Password}" FontFamily="pack://application:,,,/{在此处插入您的项目的确切名称};component/Resources/#password" />。现在可以正常工作了。 - Dôn Kayt
1
@ssamko 别忘了在上下文菜单中使用复制/粘贴!https://dev59.com/5HVD5IYBdhLWcg3wWKLc#3484929 - CodingNinja
非常感谢。这真的很有帮助。我正在努力做到这一点。此外,我需要添加占位符。我可以添加占位符和密码字符。 - Rakibul
显示剩余2条评论

12
将密码框控件作为参数传递给登录命令。
<Button Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=PasswordBox}"...>

然后您可以在您的视图模型中调用 CType(parameter, PasswordBox).Password


5
这违背了MVVM模式。 - wingerse

11

谢谢Cody,这非常有帮助。我刚刚为使用C#中的委托命令的人添加了一个示例。

<PasswordBox x:Name="PasswordBox"
             Grid.Row="1" Grid.Column="1"
             HorizontalAlignment="Left" 
             Width="300" Height="25"
             Margin="6,7,0,7" />
<Button Content="Login"
        Grid.Row="4" Grid.Column="1"
        Style="{StaticResource StandardButton}"
        Command="{Binding LoginCommand}"
        CommandParameter="{Binding ElementName=PasswordBox}"
        Height="31" Width="92"
        Margin="5,9,0,0" />

 


(这是一个空段落,其中包含一个非断行空格的HTML实体)
public ICommand LoginCommand
{
   get
   {
        return new DelegateCommand<object>((args) =>
        {
            // Get Password as Binding not supported for control-type PasswordBox
            LoginPassword = ((PasswordBox) args).Password;

            // Rest of code here
        });
   }
}

这就是我一直在寻找的!我的代码后台没有模型。这个很好用。唯一的变化是我不得不使用RelayCommand而不是DelegateCommand。 - Waldron

9

6

您可以通过将以下值添加到TextBox控件的FontFamily属性,使其成为自定义的PasswordBox

<TextBox
    Text="{Binding Password}"
    FontFamily="ms-appx:///Assets/PassDot.ttf#PassDot"
    FontSize="35"/>

在我的情况下,这个方法完美地起作用。它会显示一个点来代替实际的文本(虽然不是星号(*))。

3
这个人是个英雄!虽然有点奇怪,但仍然是个英雄! - Buda Florin
我希望这个能够起作用,但对我来说它仍然显示文本。我需要下载PassDot.ttf吗? - rabejens
@rabejens 是的,您需要下载它。 - Tasnim Fabiha
您可以将字体捆绑并作为资源加载。 - Tasnim Fabiha
@TasnimFabiha 下载链接? - paraJdox1
嗨@Hacki,如果你下载的字体无法使用,可以查看这里的答案 https://dev59.com/QnNA5IYBdhLWcg3wBpHs#54677658 我猜这可能会有所帮助。 - Tasnim Fabiha

3
使用PasswordBox的问题在于它与SecureString一起使用,因此需要一个适配器将其绑定到String,这使得它在MVVM中不太友好。同时,您也无法使用剪贴板。尽管所有这些都是有原因的,但您可能不需要那么高的安全性。这里提供了一种替代方法,可以使用剪贴板,非常简单。您可以将TextBox的文本和背景设置为透明,并将文本绑定到其下方的TextBlock上。该TextBlock使用指定的转换器将字符转换为 *。
<Window.Resources>
    <local:TextToPasswordCharConverter x:Key="TextToPasswordCharConverter" />
</Window.Resources>

<Grid Width="200">
    <TextBlock Margin="5,0,0,0" Text="{Binding Text, Converter={StaticResource TextToPasswordCharConverter}, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" FontFamily="Consolas" VerticalAlignment="Center" />
    <TextBox Foreground="Transparent" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" FontFamily="Consolas" Background="Transparent" />
</Grid>

这里是值转换器:

class TextToPasswordCharConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return new String('*', value?.ToString().Length ?? 0);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

确保你的视图模型(ViewModel)上的Text属性(Text property)实现了INotifyPropertyChanged接口。


2

我使用的一种简单的解决方案是将文本框绑定到LocalPassword,它会显示一个由重复的 * 转换而来的值。任何输入都存储在_localPassword变量中。

<TextBox Text="{Binding LocalPassword}" />

private string _localPassword = null;
private string _localPasswordDisplayed { get => new string('*', _localPassword.Length); }
public string LocalPassword { get => _localPasswordDisplayed; set { _localPassword = value; OnPropertyChanged(); } }

1

我在视图代码后台执行以下操作,以设置视图模型中的属性。不确定它是否真正“破坏”了MVVM模式,但这是我找到的最好且最简单的解决方案。

  var data = this.DataContext as DBSelectionViewModel;

        data.PassKey = txtPassKey.Password;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接