DataGridRow错误指示器不能与INotifyDataErrorInfo一起工作。

问题描述 投票:0回答:1

我正试图更新我的ViewModel以使用INotifyDataErrorInfo而不是IDataErrorInfo,并遇到了以下问题。

当前编辑字段的验证似乎正确工作,但行级错误指示器不会出现,直到我结束对有错误的字段的编辑,然后开始重新编辑它。之后,错误指示器消失,事件后修复验证错误。

换一种方式:我第一次编辑行时,TextBox的轮廓正确地变成了红色,但行指示器没有出现。重新编辑该行,导致行指示器出现。修复验证错误会导致字段轮廓消失,但留下了感叹号。

注意,IDataErrorInfo似乎可以正常工作。我遇到问题的是INotifyDataErrorInfo。

解决了一半的问题。将绑定改为双向,行指示器就会正确显示 但它还是不想消失。

这是视图。

<DataGrid ItemsSource="{Binding Items, ValidatesOnNotifyDataErrors=True}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Name, ValidatesOnNotifyDataErrors=True,Mode=TwoWay}" />
    </DataGrid.Columns>
</DataGrid>

这是视图模型

public class Item : INotifyDataErrorInfo, INotifyPropertyChanged
{
    Dictionary<string, IEnumerable<string>> _errors = new Dictionary<string,IEnumerable<string>>();
    string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                ValidateProperty("Name", value);
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string p)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(p));
    }

    private void ValidateProperty(string p, object value)
    {
        if (p == "Name")
        {
            if (string.IsNullOrWhiteSpace((string)value))
                _errors["Name"] = new[] { "Name is required." };
            else
                _errors["Name"] = new string[0];
        }

        if (ErrorsChanged != null)
            ErrorsChanged(this, new DataErrorsChangedEventArgs(null));
    }

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public System.Collections.IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
            return _errors.Values.SelectMany(es2 => es2);

        IEnumerable<string> es;
        _errors.TryGetValue(propertyName ?? "", out es);
        return es;
    }

    public bool HasErrors
    {
        get
        {
            var e = _errors.Values.Any(es => es.Any());
            return e;
        }
    }
}

这个问题好像在SO上已经有人问过了 但被原作者删除了。https:/stackoverflow.comquestions18113718wpf-datagridrow-inotifydataerrorinfo-as-tooltip-buggy?answerertab=active。原问题的副本,但这里没有答案。http:/bolding-techaswere1.blogspot.com.au201308wpf-datagridrow-inotifydataerrorinfo-as.html。

编辑。

这是我的测试源码。https:/github.comdcroweWPF-DataGrid-Validation-IssuetreemasterDataGrid%20Validation%20Issue。

这是我提交给MS的报告。https:/connect.microsoft.comVisualStudiofeedbackdetails807728datagridrow-error-indicator-not-working-with-inotifydataerrorinfo。

c# .net wpf .net-4.5 inotifydataerrorinfo
1个回答
1
投票

我知道我之前告诉过你要将绑定模式设置为双向,这是对的,不过你也要注意如何定义你的验证规则。如果你能在这两者之间找到平衡,一切都会很好。

这里有一个例子,所有的工作都很好。就像我提到的,我不能确定自己与你的例子,我缺少一些细节,以便能够重现你的问题,因此这里是一个简短的例子。

<Grid>
    <DataGrid
       CanUserAddRows="True"
       AutoGenerateColumns="False"  
       ItemsSource="{Binding Pricelist}" >
       <DataGrid.Columns>
            <DataGridTextColumn
               Header="Price" 
               Width="60" 
               Binding="{Binding Price, 
                         Mode=TwoWay, 
                         UpdateSourceTrigger=PropertyChanged,
                         ValidatesOnDataErrors=True}">
            </DataGridTextColumn>
       </DataGrid.Columns>
    </DataGrid>
</Grid>

这就是ViewModel的样子。

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private ObservableCollection<MyProduct> priceList;

    public MainWindow()
    {
        InitializeComponent();
        Pricelist = new ObservableCollection<MyProduct>();
        this.DataContext = this;
    }

    public ObservableCollection<MyProduct> Pricelist
    {
        get
        {
            return this.priceList;
        }

        set
        {
            this.priceList = value; 
            if (PropertyChanged != null) 
                PropertyChanged(this, new PropertyChangedEventArgs("PriceList"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class MyProduct : INotifyPropertyChanged, IDataErrorInfo
{
    private string _price;
    public string Price
    {
        get
        {
            return _price;
        }
        set
        {
            _price = value;

            this.RaisePropertyChanged("Price");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;

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

    protected void RaisePropertyChanged(String propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    public string Error
    {
        get
        {
            return string.Empty;
        }
    }

    public string this[string columnName]
    {
        get
        {
            string result = null;
            switch (columnName)
            {
                case "Price":
                    {
                        decimal temdecimal = 0.00m;

                        if (Price != null && !decimal.TryParse(Price, out temdecimal))
                        {
                            result = "Price is invalid";
                        }
                        break;
                    }
                default:
                    {
                        break;
                    }
            }
            return result;
        }
    }
}

在我的例子中,验证可能允许NULL作为Price属性的值,但它不允许string.Empty和任何其他包含字母的文本。

我想如果你改变你的例子的验证,它也会为你工作。

我希望我的回答能帮助到你。如果你觉得这个答案有帮助,请随时标记这个答案或投票。

你这边的例子应该可以运行的很好,应该可以达到你的要求。

EDIT2:

INotifyDataErrorInfo变得简单了。

<TextBox Text="{Binding LastName, Mode=TwoWay, NotifyOnValidationError=true }" />

<Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23" />

这是点击处理程序

private void ValidateButton_Click(object sender, RoutedEventArgs e)
{
   owner.FireValidation();
}

这是实现INotifyDataErrorInfo的类。

public class Owner : INotifyPropertyChanged, INotifyDataErrorInfo
{
   public Owner()
   {
      FailedRules = new Dictionary<string, string>();
   }

   private Dictionary<string, string> FailedRules
   {
      get;
      set;
   }

   public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

   public IEnumerable GetErrors(string propertyName)
   {
      if (FailedRules.ContainsKey(propertyName))
         return FailedRules[propertyName];
      else
         return FailedRules.Values;
   }

   internal void FireValidation()
   {
      if (lastName.Length > 20)
      {
         if (!FailedRules.ContainsKey("LastName"))
            FailedRules.Add("LastName", "Last name cannot have more than 20 characters");
      }
      else
      {
         if (FailedRules.ContainsKey("LastName"))
            FailedRules.Remove("LastName");
      }

      NotifyErrorsChanged("LastName");
   }

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

   private void NotifyErrorsChanged(string propertyName)
   {
      if (ErrorsChanged != null)
         ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
   }
}
© www.soinside.com 2019 - 2024. All rights reserved.