我正试图更新我的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。
我知道我之前告诉过你要将绑定模式设置为双向,这是对的,不过你也要注意如何定义你的验证规则。如果你能在这两者之间找到平衡,一切都会很好。
这里有一个例子,所有的工作都很好。就像我提到的,我不能确定自己与你的例子,我缺少一些细节,以便能够重现你的问题,因此这里是一个简短的例子。
<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));
}
}