有几个线程在那里关于错误验证.然而,如果它来把东西放在一起为我的代码,我失败.我从理解WPF的方式.因此,你的帮助,建议和审查是非常感谢.
**编辑:**一些编辑已经完成,在考虑@EldHasp评论后。我会标记它们,所以它们是可见的。
像这样的帖子确实给予了我一个应该做什么的想法:
- Understanding error validation inside a UserControl with MVVM
- Hiding validation adornment when hiding a control的
- How to bind to a PasswordBox in MVVM(编辑)
我的问题
如果在PasswordBox
中验证了一个错误,错误消息不会显示,红色框也会显示。我正在验证Password
属性是否为空。
**编辑:**如评论中所述,我将密码存储在string
中。在阅读相关线程后,我再次更改了该类型,并将类型string
替换为类型SecureString
。ViewModel的属性UiPassword现在是SecureString
。BindablePasswordBox
中的属性Password也是这种类型。BindablePasswordBox
文件背后的代码经过调整以处理这些更改。当前情况如登录页面的新截图所示。密码上根本没有可见的错误验证。然而,这仍然是我想要实现的-在使用PasswordBox
和SecureString
时。
什么工作正常
如果在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>
型
1条答案
按热度按时间yacmzcpb1#
在我看来,你以一种从根本上错误的方式实现了这个任务。PasswordBox是为SECURE密码管理而设计的。你将它提取到一个字符串中,从而破坏了所有的安全性。从你的实现来看,你只需要屏蔽密码输入。没有必要安全地使用它。在这种情况下,你只需要替换文本框中的字体,所有的问题都会自动解决。
这种实施方式的示例:
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这样的翻译.他们不喜欢一般的主题在这里.