使用IDataErrorInfo和自定义错误模板验证转换器时,WPF不会更新错误消息

时间:2016-06-06 07:24:27

标签: c# wpf validation idataerrorinfo

这个问题与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));
            }
        }
    }
}

0 个答案:

没有答案