我试图更新我的ViewModel以使用INotifyDataErrorInfo而不是IDataErrorInfo并遇到以下问题:
当前编辑字段的验证似乎正常工作,但行级错误指示器不会出现,直到我在字段上结束编辑并出现错误,然后开始重新编辑它。之后,错误指示器消失,修复验证错误后的事件。
换句话说: 第一次编辑行时,TextBox轮廓正确变为红色,但行指示符不会出现。重新编辑行会导致行指示符出现。修复验证错误会导致字段轮廓消失,但会留下感叹号。
请注意,IDataErrorInfo似乎工作正常。它的INotifyDataErrorInfo我遇到了麻烦。
解决方案的一半:将绑定更改为TwoWay会导致行指示符正确显示,但它仍然不想消失。
以下是观点:
<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.com/questions/18113718/wpf-datagridrow-inotifydataerrorinfo-as-tooltip-buggy?answertab=active 原始问题的副本,但没有答案:http://bolding-techaswere1.blogspot.com.au/2013/08/wpf-datagridrow-inotifydataerrorinfo-as.html
修改
这是我的测试源代码: https://github.com/dcrowe/WPF-DataGrid-Validation-Issue/tree/master/DataGrid%20Validation%20Issue
答案 0 :(得分:1)
我知道我之前告诉过你将Binding Mode设置为TwoWay并且这是正确的,尽管你还必须小心如何定义验证规则。如果你想在这两者之间找到平衡,一切都会正常工作。
这是一个例子,一切都很好。就像我提到的那样,我无法用你的例子来表明自己,我错过了一些细节,无法重现你的问题,因此这里有一个简短的例子。
<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));
}
}