XAML 如何使密码框显示错误消息(并删除红色框)的错误验证?

c6ubokkw  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(107)

有几个线程在那里关于错误验证.然而,如果它来把东西放在一起为我的代码,我失败.我从理解WPF的方式.因此,你的帮助,建议和审查是非常感谢.

**编辑:**一些编辑已经完成,在考虑@EldHasp评论后。我会标记它们,所以它们是可见的。

像这样的帖子确实给予了我一个应该做什么的想法:

我的问题

如果在PasswordBox中验证了一个错误,错误消息不会显示,红色框也会显示。我正在验证Password属性是否为空。

**编辑:**如评论中所述,我将密码存储在string中。在阅读相关线程后,我再次更改了该类型,并将类型string替换为类型SecureStringViewModel的属性UiPassword现在是SecureStringBindablePasswordBox中的属性Password也是这种类型。BindablePasswordBox文件背后的代码经过调整以处理这些更改。当前情况如登录页面的新截图所示。密码上根本没有可见的错误验证。然而,这仍然是我想要实现的-在使用PasswordBoxSecureString时。

什么工作正常

如果在TextBox上验证了错误,则会显示错误消息,但不会显示红色框。如果在TextBox中输入了至少一个字符,则错误消息消失。我正在验证属性Text是否为空。
x1c 0d1x的数据
我所做的一切
我当前的问题出现了,当我用PasswordBox替换TextBox输入密码时。然后与TextBox一起工作的一切都不起作用。请参阅我的UserControl上的此实现(下面的未删节代码):

<TextBox Grid.Column="2" Grid.Row="0" 
                         x:Name="txtUserName"
                         MinWidth="200"
                         Text="{Binding UiUserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />

                <!-- Password components; TODO - the DataErrors are not displayed properly -->
                <Label Grid.Column="1" Grid.Row="1" Content="Password" />
                <components:BindablePasswordBox Grid.Column="2" Grid.Row="1"
                                                x:Name="txtPassword"
                                                MinWidth="200"
                                                Password="{Binding UiPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />

字符串

完整代码

App.xaml

<Application x:Class="PaperDeliveryWpf.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:PaperDeliveryWpf">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/ShellBodyStyle.xaml" />
                <ResourceDictionary Source="Resources/ShellFooterStyle.xaml" />
                <ResourceDictionary Source="Resources/ShellHeaderStyle.xaml" />
                <ResourceDictionary Source="Resources/ShellTitleStyle.xaml" />
                <ResourceDictionary Source="Resources/TextBoxStyle.xaml" />
                <ResourceDictionary Source="Resources/PasswordBoxStyle.xaml" />
                <ResourceDictionary Source="Resources/ButtonStyle.xaml" />
                <ResourceDictionary Source="Resources/DataTemplate.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>


DataTemplate.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:usercontrols="clr-namespace:PaperDeliveryWpf.UserControls"
                    xmlns:viewmodels="clr-namespace:PaperDeliveryWpf.ViewModels">

    <DataTemplate DataType="{x:Type viewmodels:LoginViewModel}">
        <usercontrols:LoginUserControl />
    </DataTemplate>
    
</ResourceDictionary>


BindablePasswordBox.xaml

<UserControl x:Class="PaperDeliveryWpf.UserControls.Components.BindablePasswordBox"
             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:local="clr-namespace:PaperDeliveryWpf.UserControls.Components"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">

    <PasswordBox x:Name="passwordBox" PasswordChanged="BindablePasswordBox_PasswordChanged"/>

</UserControl>


PasswordBoxStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style TargetType="PasswordBox">
        <!-- Sets basic look of the PasswordBox -->
        <Setter Property="Padding" Value="2 1" />
        <Setter Property="BorderBrush" Value="LightGray" />

        <!-- Removes the red border around the PasswordBox, if validation found errors -->
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <AdornedElementPlaceholder />
                </ControlTemplate>
            </Setter.Value>
        </Setter>

        <!-- Enables the UI to show the error messages -->
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <StackPanel>
                        <Border Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3">
                            <ScrollViewer x:Name="PART_ContentHost" />
                        </Border>
                        <ItemsControl ItemsSource="{TemplateBinding Validation.Errors}" Margin="0 5 0 5">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Foreground="Red" Text="{Binding ErrorContent}" />
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
</ResourceDictionary>


BindablePasswordBox.xaml.cs(这是更新的代码;要查看原始内容,请参阅此线程的历史)

using System.Security;
using System.Windows;
using System.Windows.Controls;

namespace PaperDeliveryWpf.UserControls.Components;

public partial class BindablePasswordBox : UserControl
{
    public SecureString Password
    {
        get { return (SecureString)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.Register("Password", typeof(SecureString), typeof(BindablePasswordBox));

    public BindablePasswordBox()
    {
        InitializeComponent();
        passwordBox.PasswordChanged += BindablePasswordBox_PasswordChanged;
    }

    private void BindablePasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
    {
        Password = passwordBox.SecurePassword;
    }
}


LoginUserControl.xaml

<UserControl x:Class="PaperDeliveryWpf.UserControls.LoginUserControl"
             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:local="clr-namespace:PaperDeliveryWpf.UserControls"
             xmlns:viewModels="clr-namespace:PaperDeliveryWpf.ViewModels" 
             xmlns:components="clr-namespace:PaperDeliveryWpf.UserControls.Components"
             mc:Ignorable="d" 
             d:DataContext="{d:DesignInstance Type=viewModels:LoginViewModel}"
             d:DesignHeight="450" d:DesignWidth="800">
    
    <UserControl.Resources>
        <BooleanToVisibilityConverter x:Key="boolToVisibility" />
    </UserControl.Resources>

    <DockPanel>
        <!-- Header -->
        <Label DockPanel.Dock="Top" Content="Login Page" HorizontalAlignment="Center" FontSize="26" FontWeight="Bold" Margin="20" />

        <!-- Body -->
        <StackPanel DockPanel.Dock="Top">
            <Grid HorizontalAlignment="Center" FocusManager.FocusedElement="{Binding ElementName=txtUserName}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <!-- UserName components -->
                <Label Grid.Column="1" Grid.Row="0" Content="User Name" />
                <TextBox Grid.Column="2" Grid.Row="0" 
                         x:Name="txtUserName"
                         MinWidth="200"
                         Text="{Binding UiUserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />

                <!-- Password components; TODO - the DataErrors are not displayed properly -->
                <Label Grid.Column="1" Grid.Row="1" Content="Password" />
                <components:BindablePasswordBox Grid.Column="2" Grid.Row="1"
                                                x:Name="txtPassword"
                                                MinWidth="200"
                                                Password="{Binding UiPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />

                <!-- Optional design; this is not hiding the entered values in the textbox -->
                <!--<TextBox Grid.Column="2" Grid.Row="1" 
                         x:Name="txtPassword"
                         MinWidth="200"
                         Text="{Binding UiPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />-->
            </Grid>
            
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <Button Content="Login" Command="{Binding LoginButtonCommand}" />
                <Button Content="Cancel" Command="{Binding CancelButtonCommand}" />
            </StackPanel>
        </StackPanel>
        
        <!-- Footer -->
    </DockPanel>

</UserControl>


ShellView.xaml

<Window x:Class="PaperDeliveryWpf.Views.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PaperDeliveryWpf.Views" 
        xmlns:viewmodels="clr-namespace:PaperDeliveryWpf.ViewModels" 
        xmlns:userControls="clr-namespace:PaperDeliveryWpf.UserControls"
        Title="ShellView" Height="450" Width="800"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance Type=viewmodels:ShellViewModel}">
    
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="boolToVisibility" />
    </Window.Resources>
    
    <Grid>
        <!-- *** The main structure of the window is: Titel(Row=0), Header(Row=1), Body(Row=2) and Footer(Row=3) *** -->
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- *** The UI's body *** -->
        <ScrollViewer Grid.Row="2" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                    <ColumnDefinition Width="Auto"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                
                <!-- *** The ShellViewModel is hosting the logic, which UserControl shall be loaded *** -->
                <StackPanel Grid.Column="1">
                    <ContentControl Content="{Binding CurrentView,  ValidatesOnNotifyDataErrors=False}" />
                </StackPanel>
            </Grid>
        </ScrollViewer>
        
    </Grid>
</Window>

yacmzcpb

yacmzcpb1#

在我看来,你以一种从根本上错误的方式实现了这个任务。PasswordBox是为SECURE密码管理而设计的。你将它提取到一个字符串中,从而破坏了所有的安全性。从你的实现来看,你只需要屏蔽密码输入。没有必要安全地使用它。在这种情况下,你只需要替换文本框中的字体,所有的问题都会自动解决。
这种实施方式的示例:

namespace PasswordFont.ViewModels
{
    public class LauncherViewModel
    {
        public string? Password { get; set; }
    }
}
<Window.Resources>
        <vms:LauncherViewModel x:Key="viewModel" Pin="123456789"/>
        <FontFamily x:Key="passowrdFont">pack://application:,,,/Resources/Fonts/#password</FontFamily>
    </Window.Resources>
<Window x:Class="PasswordFont.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PasswordFont"
        xmlns:vms="clr-namespace:PasswordFont.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        FontSize="30">
    <Window.Resources>
        <vms:LauncherViewModel x:Key="viewModel" Password="123456789"/>
        <FontFamily x:Key="passowrdFont">pack://application:,,,/Resources/Fonts/#password</FontFamily>
        <ImageSource x:Key="open">pack://application:,,,/Resources/Icons/eye_open_icon.png</ImageSource>
        <ImageSource x:Key="closed">pack://application:,,,/Resources/Icons/eye_closed_icon.png</ImageSource>
        <DataTemplate x:Key="imageDataTemplate" DataType="ImageSource">
            <Image Source="{Binding}"/>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid VerticalAlignment="Center" HorizontalAlignment="Center" 
              Width="200">
            <TextBox x:Name="pinBox" Text="{Binding Pin, UpdateSourceTrigger=PropertyChanged}">
                <TextBox.Style>
                    <Style TargetType="TextBox">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsChecked, ElementName=passwordHide}"
                                        Value="True">
                                <Setter Property="FontFamily" Value="{DynamicResource passowrdFont}"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </TextBox.Style>
            </TextBox>
            <ToggleButton HorizontalAlignment="Right"
                        x:Name="passwordHide"
                        IsChecked="True"
                        ContentTemplate="{DynamicResource imageDataTemplate}" Height="{Binding ActualHeight, ElementName=pinBox, Mode=OneWay}">
                <FrameworkElement.Resources>
                    <Style TargetType="ToggleButton">
                        <Setter Property="ToolTip" Value="Показать"/>
                        <Setter Property="Content" Value="{DynamicResource open}"/>
                        <Style.Triggers>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter Property="ToolTip" Value="Скрыть"/>
                                <Setter Property="Content" Value="{DynamicResource closed}"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </FrameworkElement.Resources>
            </ToggleButton>
        </Grid>
    </Grid>
</Window>

Project source codes: PasswordFont.7z
主题与解释(我警告你-在俄罗斯):Implementation of a TextBox with the Hide/Show Password function [WPF, Eld Hasp]
”你问的问题的价值。
“捕获”错误是绑定的逻辑。
您已实现ViewModel.UiPassword属性与BindablePasswordBox.Password属性的绑定。因此,错误将显示在BindablePasswordBox中。在PasswordBox中,您通过直接显式赋值(SetValue)passwordBox.Password = Password;传递值。由于此赋值中未使用绑定,因此passwordBox将不会收到错误消息。
但是您可以控制错误的显示,特别是passwordBox
解决方法:
最好的选择,正如我上面所描述的,是使用带有掩码字体的TextBox。
如果对你来说纠正你的实现是非常重要的,那么<Style TargetType="PasswordBox">样式不应该为PasswordBox设置,而应该为你的UserControl <Style TargetType="components:BindablePasswordBox">设置。
我在PasswordBox上发现了这个帖子:[如何在MVVM中绑定PasswordBox](stackoverflow.com/a/45422711/20737189)。有很多东西需要思考。
这也是一个非常糟糕的方法。使用PassworBox的全部目的是避免字符串,只使用SecureString。这需要在所有级别上完成-从View开始,经过ViewModel,Model,Repository,并在数据库服务器结束。在任何地方使用字符串作为密码完全破坏了使用PassworBox的整个概念。在这种情况下,你应该使用一个带有屏蔽字体的文本框。在我的主题中可以找到一个使用PassworBox加密密码传输的例子(对不起,但还是用俄语):Attached Properties for PasswordBox: Password binding and secure password handling
如果你不能理解文章的意思与翻译,我可以把它翻译成英语,但我担心这将是不可能张贴在Stackoverflow这样的翻译.他们不喜欢一般的主题在这里.

相关问题