Индикатор ошибки DataGridRow не работает с INotifyDataErrorInfo
Я пытаюсь обновить 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://stackru.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
Вот отчет, который я отправил в MS: https://connect.microsoft.com/VisualStudio/feedback/details/807728/datagridrow-error-indicator-not-working-with-inotifydataerrorinfo
1 ответ
Я знаю, что говорил вам прежде, чтобы установить режим привязки на 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));
}
}