Проверка пароля.Шаблон ошибки не отображается

#c# #wpf

#c# #wpf

Вопрос:

Я пытаюсь показать Validation.ErrorTemplate из PasswordBox . Однако это не проявляется. В той же форме у меня есть имя пользователя, TextBox и ErrorTemplate оно отображается правильно.

Xaml для поля пароля в datatempalte:

 <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 интерфейс в базовой viewmodel.

 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));
        }
    }
}
  

Комментарии:

1. попробуйте <ControlTemplate><AdornerDecorator><DockPanel/></AdornerDecorator></ControlTemplate>

2. @Nathan, спасибо за предложение. Однако это не решило проблему.

3. Какие обходные пути вы пробовали и каковы результаты? Вы пробовали устанавливать DataContext в коде после InitializeComponent? Вы пытались создать простой загрузочный проект, чтобы показывать ошибки пользовательской проверки и изменять его шаг за шагом в соответствии с вашим текущим кодом? Вы пробовали добавить триггер при проверке. Имеет ошибку, чтобы увидеть, появляется ли ошибка проверки по умолчанию?

4. @Nathan, я пытался отладить его с помощью Visual Studio. Одна вещь, которую я заметил, была в текстовом поле для имени пользователя, если есть ошибка, выполняется проверка нового свойства. Ошибки, которые содержат ошибку проверки. Для поля пароля нет свойства для ошибок проверки.

5. И вы пробовали устанавливать ValidatesOnDataErrors в значение true?

Ответ №1:

Проблема в том, что ошибки возникают на 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 . Я решил привязать viewmodel 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 свойство на привязку twoway по умолчанию:

 public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register(
    "SPassword",
    typeof(SecureString),
    typeof(PasswordBoxBehaviorBinding),
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
  

В противном случае убедитесь, что установлен соответствующий режим привязки.