这个问题与Validation Rule not updating correctly with 2 validation rules有关,但我没有使用ValidationRule,而是使用IDataErrorInfo接口。
考虑以下示例,其中用户必须使用RadioButton选择任一选项,然后验证取决于选择。 如果选择了选项1,则输入的字符串必须以A开头,如果选择了选项2,则必须以B开头。
如果用户更改了该选项,则必须重新验证TextBox。作为链接问题中提供的答案,在错误模板中使用CurrentItem.ErrorContent确实有效,因为错误消息已更新。 (我只使用CurrentItem.ErrorContent,(Validation.Errors).CurrentItem.ErrorContent什么都不显示,但这是我猜的另一个问题。)
要重现,请选择选项1并键入任何不以A或B开头的内容(例如VVVVV)。现在选择选项2。 使用自定义转换器绑定错误消息(显示所有错误消息)仍然表示该字符串必须以A开头,而错误模板中的另一个绑定正确表明该字符串必须以B开头。
根据那里提供的答案,这是因为ValidationRule阻止了更新的实际属性,因此NotifyPropertyChanged将不会执行任何操作。但是,这里的属性已更新。
是否有原因WPF不再调用转换器(错误或功能)? 有什么方法可以强制再次调用转换器,从而更新第一条错误消息吗?
ValidationDemo.xaml:
<Window x:Class="ValidationIssue2.ValidationDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:validationIssue2="clr-namespace:ValidationIssue2"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<validationIssue2:ValidationViewModel />
</Window.DataContext>
<Window.Resources>
<validationIssue2:ValidationErrorsToStringConverter x:Key="ValErrToString" />
<ControlTemplate x:Key="ErrorTemplate">
<Border BorderBrush="Red" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<AdornedElementPlaceholder />
<TextBlock Text="{Binding Converter={StaticResource ValErrToString}}" Background="White" />
<Label> / </Label>
<TextBlock Text="{Binding CurrentItem.ErrorContent}" Background="White" />
</StackPanel>
</Border>
</ControlTemplate>
</Window.Resources>
<StackPanel Orientation="Vertical">
<StackPanel>
<RadioButton IsChecked="{Binding Option1}">Option 1</RadioButton>
<RadioButton IsChecked="{Binding Option2}">Option 2</RadioButton>
</StackPanel>
<TextBox Validation.ErrorTemplate="{StaticResource ErrorTemplate}" Width="100" HorizontalAlignment="Left"
Text="{Binding ValidateThis, ValidatesOnDataErrors=True}"></TextBox>
</StackPanel>
</Window>
ValidationDemo.xaml.cs:
using System.Windows;
namespace ValidationIssue2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class ValidationDemo : Window
{
public ValidationDemo()
{
InitializeComponent();
}
}
}
ValidationErrorsToStringConverter.cs:
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace ValidationIssue2
{
[ValueConversion(typeof(ReadOnlyObservableCollection<ValidationError>), typeof(string))]
internal class ValidationErrorsToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var errorCollection = value as ReadOnlyObservableCollection<ValidationError>;
if (errorCollection == null)
{
return DependencyProperty.UnsetValue;
}
return String.Join(", ", errorCollection.Select(e => e.ErrorContent.ToString()));
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
ValidationViewModel.cs:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ValidationIssue2
{
class ValidationViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private bool option1;
private bool option2;
private string validateThis;
public event PropertyChangedEventHandler PropertyChanged;
public bool Option1
{
get
{
return option1;
}
set
{
option1 = value;
OnPropertyChanged();
// since this does influece text validation
OnPropertyChanged("");
}
}
public bool Option2
{
get
{
return option2;
}
set
{
option2 = value;
OnPropertyChanged();
// since this does influece text validation
OnPropertyChanged("");
}
}
public string Error
{
get { return String.Empty; }
}
public string ValidateThis
{
get
{
return validateThis;
}
set
{
validateThis = value;
OnPropertyChanged();
}
}
public string this[string columnName]
{
get
{
switch (columnName)
{
case "ValidateThis":
if (Option1)
{
return ValidateThis.StartsWith("A") ? String.Empty : "Must start with A";
}
if (Option2)
{
return ValidateThis.StartsWith("B") ? String.Empty : "Must start with B";
}
return String.Empty;
default:
return String.Empty;
}
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}