【问题标题】:Red border doesn't disappear with INotifyDataErrorInfo红色边框不会随着 INotifyDataErrorInfo 消失
【发布时间】:2014-12-23 12:58:34
【问题描述】:

我正在尝试实现 INotifyDataErrorInfo,但在尝试验证 ObservableCollection 属性时没有成功。

问题是,如果集合错误,我得到红色边框,但如果我更正集合,红色边框就不会消失了。

有人知道如何解决这个问题吗?

我设置了一个小样本来演示这个问题:

<Window x:Class="Validation.ValidationWindow3"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="ValidationWindow3" Height="300" Width="300">
<DockPanel LastChildFill="True">
    <TextBlock DockPanel.Dock="Top">Click button Add two times.<LineBreak/>
        => Red border should appear.<LineBreak/>
        <LineBreak/>
        Select second line in listbox then click button Remove.<LineBreak/>
        =>Red border should disappear.</TextBlock>
    <Button DockPanel.Dock="Bottom" Click="OnOk">Ok</Button>
    <Button DockPanel.Dock="Top" Click="OnAdd">Add</Button>
    <Button DockPanel.Dock="Top" Click="OnRemove">Remove</Button>
    <ListBox ItemsSource="{Binding ListOfNumbers, NotifyOnValidationError=True}" SelectedItem="{Binding SelectedNumber}" />
</DockPanel>

后面的代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace Validation
{
/// <summary>
/// Interaction logic for ValidationWindow2.xaml
/// </summary>
public partial class ValidationWindow3 : Window, INotifyDataErrorInfo
{
    public ObservableCollection<int> ListOfNumbers
    {
        get { return (ObservableCollection<int>)GetValue(ListOfNumbersProperty); }
        set { SetValue(ListOfNumbersProperty, value); }
    }
    public static readonly DependencyProperty ListOfNumbersProperty =
        DependencyProperty.Register("ListOfNumbers", typeof(ObservableCollection<int>), typeof(ValidationWindow3), new PropertyMetadata(null, OnPropertyChanged));



    public int SelectedNumber
    {
        get { return (int)GetValue(SelectedNumberProperty); }
        set { SetValue(SelectedNumberProperty, value); }
    }
    public static readonly DependencyProperty SelectedNumberProperty =
        DependencyProperty.Register("SelectedNumber", typeof(int), typeof(ValidationWindow3), new PropertyMetadata(-1));



    public ValidationWindow3()
    {
        InitializeComponent();
        ListOfNumbers = new ObservableCollection<int>();
        DataContext = this;
    }

    private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ValidationWindow3 instance = d as ValidationWindow3;
        ObservableCollection<int> coll = (ObservableCollection<int>)e.NewValue;
        coll.CollectionChanged += instance.coll_CollectionChanged;
    }

    void coll_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        CheckProperty("ListOfNumbers");
    }



    private void OnOk(object sender, RoutedEventArgs e)
    {
        if(HasErrors)
        {
            IEnumerable list = GetErrors(null);
            string msg = "";
            foreach(var item in list)
            {
                msg += item.ToString();
            }
            MessageBox.Show(msg);
            return;
        }
        DialogResult = true;
    }


    void CheckProperty([CallerMemberName] string propertyName = "")
    {
        bool isValid = true;
        string msg = null;

        switch(propertyName)
        {
        case "ListOfNumbers":
            msg = "Only even numbers allowed!";
            foreach(int item in ListOfNumbers)
            {
                if(item % 2 > 0)
                {
                    isValid = false;
                }
            }
            break;
        default:
            break;
        }
        if(!isValid)
        {
            AddError(propertyName, msg);
        }
        else if(msg != null)
        {
            RemoveError(propertyName, msg);
        }
    }

    // Adds the specified error to the errors collection if it is not 
    // already present, inserting it in the first position if isWarning is 
    // false. Raises the ErrorsChanged event if the collection changes. 
    public void AddError(string propertyName, string error, bool isWarning=false)
    {
        if(!errors.ContainsKey(propertyName))
            errors[propertyName] = new List<string>();

        if(!errors[propertyName].Contains(error))
        {
            if(isWarning)
                errors[propertyName].Add(error);
            else
                errors[propertyName].Insert(0, error);
            RaiseErrorsChanged(propertyName);
        }
    }

    // Removes the specified error from the errors collection if it is
    // present. Raises the ErrorsChanged event if the collection changes.
    public void RemoveError(string propertyName, string error)
    {
        if(errors.ContainsKey(propertyName) &&
            errors[propertyName].Contains(error))
        {
            errors[propertyName].Remove(error);
            if(errors[propertyName].Count == 0)
                errors.Remove(propertyName);
            RaiseErrorsChanged(propertyName);
        }
    }

    public void RaiseErrorsChanged(string propertyName)
    {
        if(ErrorsChanged != null)
            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
    }




    #region INotifyDataErrorInfo Members

    private Dictionary<String, List<String>> errors =
        new Dictionary<string, List<string>>();

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public System.Collections.IEnumerable GetErrors(string propertyName)
    {
        if (errors.Count < 1)
        {
            return null;
        }
        if(String.IsNullOrEmpty(propertyName))
        {
            return errors.SelectMany(err => err.Value.ToList());
        }
        if(!errors.ContainsKey(propertyName))
            return null;
        return errors[propertyName];
    }

    public bool HasErrors
    {
        get { return errors.Count > 0; }
    }

    #endregion

    static int _nextNumber = 0;

    private void OnAdd(object sender, RoutedEventArgs e)
    {
        ListOfNumbers.Add(_nextNumber++);
    }

    private void OnRemove(object sender, RoutedEventArgs e)
    {
        ListOfNumbers.Remove(SelectedNumber);
    }
}
}

编辑:

我发现验证本身没有问题。这似乎是与 ListBox 相关的问题。如果我将一个额外的 TextBox 绑定到 ListOfNumbers 我可以看到这个 TextBox 上的边框工作正常。

这是我添加的:

        <TextBox DockPanel.Dock="Top" Text="{Binding ListOfNumbers, NotifyOnValidationError=True}" />

那么为什么ListBox上的红色边框不对呢?

【问题讨论】:

  • 也没用。
  • 会不会只是默认ErrorTemplate的问题?我验证了在 CollectionChanged 上验证了集合,使用“ListOfNumbers”引发了 ErrorsChanged,并且调用了 HasErrors 并返回 false。所以框架应该知道没有错误。
  • 我正在测试您的示例,当您选择剩余的对象时,可以看到红色边框消失了。在 selectednumber 更改之前,不会引发类似 HasErrors 的问题。您可以强制使用解决方法,例如“SelectedNumber = ListOfNumbers.FirstOrDefault()。
  • 我认为这是 WPF 中的项目控件的错误,与新的INotifyDataErrorInfo 界面不兼容,请尝试旧的IDataErrorInfo

标签: c# .net validation xaml


【解决方案1】:

我也有这个问题。我通过删除 Validation.ErrorTemplate 解决了这个问题:

<Style>
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <AdornedElementPlaceholder/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

并在绑定元素的DataTemplate中添加错误提示,如下:

 <TextBlock Text="!" FontWeight="Bold" Foreground="Red" Margin="5" Visibility="{Binding HasErrors, Converter={StaticResource BoolToVisibilityConverter}}"/>

【讨论】:

    【解决方案2】:

    我想我找到了原因:

    ListBox 正在验证 SelectedItem。因此,如果我删除此项目,则绑定到 SelectedItem 的变量 SelectedNumber 的值不再包含在集合中。这给了我红框。

    我认为这不是 ListBox 的正确行为,但如果我牢记这一点,我可以根据具体情况使用一些变通方法:

    1. 删除 SelectedItem 后,将 SelectedItem 设置为集合中的其他项目,或设置为 -1。
    2. 使用 SelectedIndex 代替 SelectedItem,因为这样不存在这样的问题。
    3. 为 SelectedItem 绑定使用 Mode=OneWay

    【讨论】:

      猜你喜欢
      • 2014-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多