Passwordbox验证.没有显示ErrorTemplate

本文关键字:显示 ErrorTemplate 验证 Passwordbox | 更新日期: 2023-09-27 18:13:42

我试图显示PasswordBox中的Validation.ErrorTemplate。然而,它并没有表现出来。在同一表单上,我有一个用户名TextBoxErrorTemplate正确显示。

数据面板中PasswordBox的Xaml:

<PasswordBox Grid.Row="3" DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=ContentControl}}">                    
  <PasswordBox.Style>
    <Style>
      <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
          <ControlTemplate>
            <DockPanel LastChildFill="True">
              <TextBlock DockPanel.Dock="Right" Foreground="Red" FontSize="14" FontWeight="Bold">*</TextBlock>
              <Border BorderBrush="Red" BorderThickness="1">
                <AdornedElementPlaceholder/>
              </Border>
            </DockPanel>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </PasswordBox.Style>
  <i:Interaction.Behaviors>
    <behavior:PasswordBoxBehaviorBinding SPassword="{Binding Path=Password, ValidatesOnNotifyDataErrors=True}" />
  </i:Interaction.Behaviors>
</PasswordBox>

下面是我正在使用的附加属性。

public class PasswordBoxBehaviorBinding : Behavior<PasswordBox>
{
    public SecureString SPassword
    {
        get { return (SecureString)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }
    public static readonly DependencyProperty PasswordProperty
        = DependencyProperty.Register(
            "SPassword", 
            typeof(SecureString), 
            typeof(PasswordBoxBehaviorBinding), 
            new PropertyMetadata(null));
    protected override void OnAttached()
    {
        AssociatedObject.PasswordChanged += AssociatedObject_PasswordChanged;                     
        base.OnAttached();
    }
    protected override void OnDetaching()
    {
        AssociatedObject.PasswordChanged += AssociatedObject_PasswordChanged;
        base.OnDetaching();
    }
    private void AssociatedObject_PasswordChanged(object sender, RoutedEventArgs e)
    {            
        var binding = BindingOperations.GetBindingExpression(this, PasswordProperty);
        if (binding != null)
        {
            if (binding.ResolvedSource != null)
            {
                PropertyInfo property = binding.ResolvedSource.GetType()
                    .GetProperty(binding.ParentBinding.Path.Path);
                if (property != null)
                {
                    property.SetValue(binding.ResolvedSource, AssociatedObject.SecurePassword);
                }
            }
        }
    }
}

我在基本视图模型中实现了INotifyDataError接口。

public class ViewModelBase : BindableBase, INotifyDataErrorInfo
{
    private IDictionary<string, List<string>> errors
        = new Dictionary<string, List<string>>();
    public bool HasErrors
    {
        get
        {
            return this.errors.Count > 0;
        }
    }
    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    public IEnumerable GetErrors(string propertyName)
    {
        if ( this.errors.ContainsKey(propertyName) )
        {
            return this.errors[propertyName];
        }
        return null;
    }
    public void AddError(string propertyName, string error)
    {
        this.errors[propertyName] = new List<string> { error };         
        this.RaiseErrorsChanged(propertyName);
    }
    public void RemoveError(string propertyName)
    {
        if (this.errors.ContainsKey(propertyName))
        {
            this.errors.Remove(propertyName);
        }
        this.RaiseErrorsChanged(propertyName);
    }
    private void RaiseErrorsChanged(string propertyName)
    {
        if (this.ErrorsChanged != null)
        {
            this.ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
        }
    }
}

Passwordbox验证.没有显示ErrorTemplate

问题是,错误是在承载验证错误发生的数据绑定属性的DependencyObject上引发的。在你的例子中,<behavior:PasswordBoxBehaviorBinding SPassword="{Binding Path=Password, ValidatesOnNotifyDataErrors=True}" />意味着你可以在行为中读取你的错误。

在这一点上,我还想建议不要对SPassword的绑定进行奇怪的修改。正常设置即可:
private void AssociatedObject_PasswordChanged(object sender, System.Windows.RoutedEventArgs e)
{
    SPassword = AssociatedObject.SecurePassword;
    // use debugger to verify, that the validation errors exist. Otherwise, no need for the following line of code
    var behaviorErrors = Validation.GetErrors(this);
}

不幸的是,我还没有发现如何以一种优雅的方式将Validation.Errors从附加行为提升到主机控件。因此,基本上,您的选择将以某种方式将错误从行为链绑定到密码盒,或者为您的属性创建一个额外的绑定,因为此绑定将使用相同的验证机制,从而将Validation.Errors设置在PasswordBox上。为了错误传播,我决定将视图模型Password绑定到PasswordBox.Tag

<PasswordBox Width="200" Height="100" Tag="{Binding Password,ValidatesOnNotifyDataErrors=True,Mode=OneWay}">
    <i:Interaction.Behaviors>
        <behavior:PasswordBoxBehaviorBinding SPassword="{Binding Password}"/>
    </i:Interaction.Behaviors>
</PasswordBox>

请注意,我从绑定行为中删除了绑定错误验证,因为它无论如何都没有用,并且我为Tag绑定添加了绑定错误验证。

还有一件事:我改变了SPassword属性,默认情况下是双向绑定:
public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register(
    "SPassword",
    typeof(SecureString),
    typeof(PasswordBoxBehaviorBinding),
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));