WPF / XAML - 验证错误显示两次

时间:2012-04-19 22:08:19

标签: wpf validation xaml

我有一个带有GroupBox的表单,其中有很多控件(Checkbox,TextBox和Combobox)。

表单绑定到在其属性上实现IDataErrorInfo的视图模型,当用户向控件输入无效值时,IDataInfo返回无效结果,并且控件被常用的红色框包围,并且错误消息显示在表单的底部。

问题是,GroupBox旨在表示一组强制值。用户需要检查组中的至少一个复选框。如果不这样做不是单个控件的错误,那就是组中的错误。所以我在GroupBox中添加了一个BindingGroup,并添加了ValidationRule,如果没有选择任何内容则返回错误。这很好。如果未选择任何内容,则GroupBox将被常用的红色框包围,并且错误消息将显示在表单的底部。

我的问题是,如果GroupBox中的一个控件未通过验证,我会得到两个红色框 - 一个围绕控件,一个围绕GroupBox。我在表单底部的列表中收到两条错误消息。

如何防止BindingGroup报告组中包含的所有内容的错误?

编辑:

一个简单的例子 - 这不会显示Validation.Errors,但是当包含的TextBox出现时,您可以看到StackPanel突出显示为验证失败。

XAML:

<Window
        x:Class="BugHunt5.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:BugHunt5"
        Title="MainWindow"
        Height="350"
        Width="525"
        >
    <GroupBox 
            Margin="20"
            Header="This is my group"
            x:Name="MyGroupBox"
            >
        <StackPanel>
            <StackPanel.BindingGroup>
                <BindingGroup NotifyOnValidationError="True">
                </BindingGroup>
            </StackPanel.BindingGroup>
            <TextBox 
                    Height="30"
                    Width="100"
                    >
                <TextBox.Text>
                    <Binding
                            NotifyOnValidationError="True"
                            ValidatesOnDataErrors="True"
                            Path="MyString"
                            UpdateSourceTrigger="PropertyChanged"
                            >
                        <Binding.ValidationRules>
                            <local:NoDecimalsValidationRule ValidatesOnTargetUpdated="True"/>
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>
            </TextBox>
        </StackPanel>
    </GroupBox>
</Window>

C#:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = new ViewModel("This should be an integer");
    }
}
public class ViewModel
{
    public string MyString
    { get; set; }
    public ViewModel(string mystring)
    { this.MyString = mystring; }
}
public class NoDecimalsValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value,
        System.Globalization.CultureInfo cultureInfo)
    {
        string myString = value as string;
        int result;
        if (!Int32.TryParse(myString, out result))
            return new ValidationResult(false, "Must enter integer");
        return new ValidationResult(true, null);
    }
}

2 个答案:

答案 0 :(得分:1)

<强> ViewModel.cs

public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool checked1, checked2;
    private string myString;

    public bool Checked1
    {
        get { return this.checked1; }
        set { this.SetValue(ref this.checked1, value, "Checked1"); }
    }

    public bool Checked2
    {
        get { return this.checked2; }
        set { this.SetValue(ref this.checked2, value, "Checked2"); }
    }

    public string MyString
    {
        get { return this.myString; }
        set { this.SetValue(ref this.myString, value, "MyString"); }
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
            handler(this, e);
    }

    private void SetValue<T>(ref T field, T value, string propertyName)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
    }

    public string Error
    {
        get
        {
            return this.checked1 == false && this.checked2 == false ? "Must check one value." : string.Empty;
        }
    }

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            switch (propertyName)
            {
                case "MyString":
                    int result;
                    return int.TryParse(this.myString, out result) ? string.Empty : "Must enter integer.";
                default:
                    return string.Empty;
            }
        }
    }
}

<强> MainWindow.xaml

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication"
        Title="MainWindow" Height="350" Width="525">
    <GroupBox Header="This is my group">
        <GroupBox.DataContext>
            <local:ViewModel MyString="This should be an integer"/>
        </GroupBox.DataContext>
        <StackPanel>
            <StackPanel.BindingGroup>
                <BindingGroup x:Name="checkedBindingGroup">
                    <BindingGroup.ValidationRules>
                        <DataErrorValidationRule ValidationStep="ConvertedProposedValue"/>
                    </BindingGroup.ValidationRules>
                </BindingGroup>
            </StackPanel.BindingGroup>
            <CheckBox IsChecked="{Binding Checked1, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" Binding.SourceUpdated="OnCheckedSourceUpdated" Content="Checked1"/>
            <CheckBox IsChecked="{Binding Checked2, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" Binding.SourceUpdated="OnCheckedSourceUpdated" Content="Checked2"/>
            <TextBox Text="{Binding MyString, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, BindingGroupName=Dummy}"/>
        </StackPanel>
    </GroupBox>
</Window>

<强> MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void OnCheckedSourceUpdated(object sender, DataTransferEventArgs e)
    {
        this.checkedBindingGroup.ValidateWithoutUpdate();
    }
}

关键词:

  • 在'MyString'上设置BindingGroupName绑定到某个虚拟值,因此它不包含在父BindingGroup中。
  • 'Checked1'和'Checked2'绑定必须将NotifyOnSourceUpdated设置为true,并将事件处理程序添加到Binding.SourceUpdated事件中,其中必须显式调用BindingGroup验证。
  • BindingGroup中DataErrorValidationRule的ValidationStep必须是ConvertedProposedValue或RawProposedValue,以便BindingGroup.ValidateWithoutUpdate()执行验证逻辑。

答案 1 :(得分:0)

我曾经遇到过类似的问题,但似乎没有合乎逻辑的方法(因为它会导致像矛盾一样)。

但是,您可以做的是使用与该组相关联的复选框或面板的附加属性Validation.ErrorTemplate进行修补,以删除红色轮廓。