【问题标题】:Error handling in WPF PasswordBoxWPF PasswordBox 中的错误处理
【发布时间】:2012-01-20 13:38:59
【问题描述】:

我一直很欣赏 Josh Smith 建立他的sample application 的方式。 而且我还尝试模拟他的应用程序的 ViewModels 实现 IDataErrorInfo 属性的方式,并通过自定义 DataTemplate 在用户面前呈现错误。 这是他用来显示错误的数据模板:

<DataTemplate DataType="{x:Type ValidationError}">
  <TextBlock FontSize="10"
             FontStyle="Italic"
             Foreground="Red"
             HorizontalAlignment="Right"
             Margin="0,1"
             Text="{Binding Path=ErrorContent}"/>
</DataTemplate>

这个数据模板的工作示例如下:

<TextBox x:Name="txtUsername"
         Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"
         Width="300"
         Margin="2" 
         Text="{Binding Path=Username,
             ValidatesOnDataErrors=True,
             UpdateSourceTrigger=PropertyChanged}"
         Validation.ErrorTemplate="{x:Null}"/>

<ContentPresenter Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" 
                  Content="{Binding ElementName=txtUsername,
                      Path=(Validation.Errors).CurrentItem}"/>

文本框的默认 ErrorTemplate(出现在其周围的红色边界)被新的错误模板替换,其中放置在文本框下方的内容演示者会将错误传达给用户 - 当然是更高级和更优雅模板。

如果你看过上面的代码,你可能已经猜到我正在尝试创建一个登录表单。

不幸的是,登录表单需要密码(然后是 PasswordBox)。 PasswordBox 不提供“密码”作为依赖属性。我不想违反 MVVM 指南,即尽可能避免代码落后,因此很想去 PasswordBoxAssistant 提到的 here 类。 否则这是一个很好的解决方案,保存一件事。它不允许我使用 Josh 的数据模板验证密码框。 我已经验证了我的 ViewModel 的密码属性不为空。该属性正在得到验证,因为我的“登录”按钮在没有用户填写密码的情况下没有启用。但是,我在此属性验证中设置的“输入密码”消息并未由位于 PasswordBox 下方的内容呈现器呈现。代码如下:

<Label Content="Password:" Grid.Column="0" Grid.Row="2" Margin="2" />

<PasswordBox x:Name="PasswordBox"
             Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"
             Margin="2"
             Validation.ErrorTemplate="{x:Null}" 
             ff:PasswordBoxAssistant.BindPassword="true"  
             ff:PasswordBoxAssistant.BoundPassword="{Binding Path=Password,
                 Mode=TwoWay,
                 UpdateSourceTrigger=PropertyChanged}"/>

<ContentPresenter Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2"
                  Content="{Binding ElementName=PasswordBox,
                  Path=(Validation.Errors).CurrentItem}"/>

不用说,上面代码中的ff代表命名空间引用:

xmlns:ff="clr-namespace:MyProject.UserViews"

我敢肯定,之所以会出现这个问题,是因为 Password 属性已被辅助类扩展。如果我放弃这种方法,我将不得不从 IDataErrorInfo 实现中删除 Password 属性,并且在登录按钮上单击必须验证它,向用户显示一个消息框。但并非不影响一致性。我不太了解依赖属性;有什么解决方法吗?以某种方式更改助手类会让我恢复红色错误消息吗?

【问题讨论】:

    标签: wpf mvvm passwordbox custom-error-handling


    【解决方案1】:

    我在您的密码绑定中没有看到ValidatesOnDataErrors=True,所以这可能是您的问题。默认情况下,它设置为 False,这意味着绑定不会提醒 UI 任何验证错误。


    也就是说,我认为Password 故意不是DependencyProperty,因为您确实不应该将密码存储为纯文本。

    通常我最终将PasswordBox.Password(或整个PasswordBox)作为CommandParameter 传递给我的LoginCommand,然后它可以获取数据并为它做任何事情。通常这意味着对它或其他东西进行散列并将其与存储的密码的散列进行比较以查看其是否相同。如果登录失败,我会将相关的错误消息写入我的 ViewModel 中的一个属性,该属性绑定到登录 UI。

    <Button Command="{Binding LoginCommand}" 
            CommandParameter="{Binding ElementName=MyPasswordBox, Path=Password}" />
    

    【讨论】:

    • 感谢您的回复。在我进入这个设计之前,我有这个选择。就我而言,登录模块更像是一种正式的设计,以保留传统方法。我的问题不是我可以使用另一种方式将密码传递给 ViewModel 的设计。我的问题是 PasswordBoxAssistant 如何阻止密码得到验证。
    • @James 您是否在密码绑定中设置了ValidatesOnDataErrors="True"
    • “也就是说,我在您的密码绑定中没有看到 ValidatesOnDataErrors=True,所以这可能是您的问题。” @Rachel 有问题。感谢您指出了这一点。把作业丢给你我很尴尬。
    • 我们可以停止这种“不在内存中存储密码”的业务吗?框的密码属性首先是一个字符串。所以这只是货物崇拜,因为该死的字符串已经在内存中,很容易阅读例如窥探!
    • @flq 这是真的,但是我认为PasswordBox.Password 是由用户输入的,并且仅作为应用程序的一部分存在于PasswordBox 对象的生命周期中,而类似于@ 987654333@ 在 User 对象的生命周期内存在,并且会在内存中停留更长时间。
    【解决方案2】:

    不确定这是否是您的问题,但您没有关闭 PasswordBox 的默认 ErrorTemplate。 (即没有Validation.ErrorTemplate="{x:Null}

    编辑:您可以使用Wpf Inspector 之类的东西来检查PasswordBoxValidation.Errors 的内容,以确保错误确实存在(或者甚至只是将ItemsControl 绑定到它)?

    【讨论】:

    • 感谢您对此进行标记,但这不是问题。因为不关闭默认错误模板会使默认错误模板出现。但什么都没有出现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多