#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));
В противном случае убедитесь, что установлен соответствующий режим привязки.