Текстовые поля WPF DataGrid, сохраняющие предыдущие значения при множественных ошибках проверки
Я создал DataGrid
с двумя столбцами, которые оба используют TextBox
s для редактирования свойств ViewModel. Когда в обоих столбцах есть ошибки проверки, а значения свойств изменяются из ViewModel, переход в режим редактирования в одной из ячеек сохраняет ранее отредактированное значение.
Вот короткий пример:
Посмотреть
<Window ...>
<Window.DataContext>
<ViewModels:MainPresenter />
</Window.DataContext>
<DockPanel>
<Button Command="{Binding ResetValuesCommand}"
Margin="5" DockPanel.Dock="Top">Reset Values</Button>
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" Margin="5">
<DataGrid.Columns>
<DataGridTextColumn Header="Value 1"
Binding="{Binding Value1, ValidatesOnDataErrors=True}" />
<DataGridTextColumn Header="Value 2"
Binding="{Binding Value2, ValidatesOnDataErrors=True}" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Window>
ViewModel
public class MainPresenter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public IEnumerable<ItemPresenter> Items { get; }
= new ObservableCollection<ItemPresenter> {new ItemPresenter()};
public ICommand ResetValuesCommand => new ResetCommand(Items);
private class ResetCommand : ICommand
{
private readonly IEnumerable<ItemPresenter> _items;
public ResetCommand(IEnumerable<ItemPresenter> items) { _items = items; }
public void Execute(object parameter) => _items.ToList().ForEach(i => i.Reset());
public bool CanExecute(object parameter) => true;
public event EventHandler CanExecuteChanged { add { } remove { } }
}
}
public class ItemPresenter : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
public string Value1 { get; set; } = "A";
public string Value2 { get; set; } = "B";
public string this[string columnName] => "ERROR";
public string Error => "ERROR";
public void Reset()
{
Value1 = "A";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value1)));
Value2 = "B";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value2)));
}
}
Действия по воспроизведению
При запуске приложения оба столбца выделяются как недействительные.
- Дважды щелкните ячейку в столбце "Значение 1" (в настоящее время имеет значение "A") и измените ее, например, на "Z";
- Нажмите ввод (или эквивалент), чтобы зафиксировать изменение;
- Нажмите кнопку "Сбросить значения" (в результате чего ячейка, которую мы только что отредактировали, изменится на "A");
- Дважды щелкните ячейку в столбце "Значение 1" еще раз.
Ячейка в столбце "Значение 1" переходит в режим редактирования, и значение снова отображается как "Z".
Следует отметить одну вещь: это происходит только тогда, когда другие столбцы имеют ошибки проверки. Если это единственный столбец с ошибкой проверки, тогда TextBox в режиме редактирования будет правильно показывать "A" при входе в режим редактирования.
Частичное исправление
Как ни странно, явно устанавливая режим в привязках к TwoWay
(предположительно по умолчанию, поскольку это очевидное поведение) решает проблему.
Тем не менее, если я хочу некоторые пользовательские шаблоны ячеек (и заменить DataGridTextColumn
с DataGridTemplateColumns
s) но все еще использую TextBox
для редактирования:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" Margin="5">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Value 1">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type ViewModels:ItemPresenter}">
<TextBlock Text="{Binding Value1, ValidatesOnDataErrors=True}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate DataType="{x:Type ViewModels:ItemPresenter}">
<TextBox Text="{Binding Value1, ValidatesOnDataErrors=True}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Value 2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type ViewModels:ItemPresenter}">
<TextBlock Text="{Binding Value2, ValidatesOnDataErrors=True}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate DataType="{x:Type ViewModels:ItemPresenter}">
<TextBox Text="{Binding Value2, ValidatesOnDataErrors=True}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Я сталкиваюсь с той же проблемой, но явно устанавливаю режимы привязки TwoWay
не исправить это.
Я делаю что-то не так, что я упустил из виду? Или у кого-нибудь есть обходной путь для этого?
1 ответ
Я нашел обходной путь.
Если я перейду на использование INotifyDataErrorInfo
(доступно только в.NET 4.5 и выше), затем работает как положено.
ViewModel
public class ItemPresenter : INotifyPropertyChanged, INotifyDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public string Value1 { get; set; } = "A";
public string Value2 { get; set; } = "B";
public IEnumerable GetErrors(string propertyName) => new[] { "ERROR" };
public bool HasErrors => true;
public void Reset()
{
Value1 = "A";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value1)));
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Value1)));
Value2 = "B";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value2)));
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Value2)));
}
}
Посмотреть
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" Margin="5">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Value 1">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type ViewModels:ItemPresenter}">
<TextBlock Text="{Binding Value1}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate DataType="{x:Type ViewModels:ItemPresenter}">
<TextBox Text="{Binding Value1}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Value 2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type ViewModels:ItemPresenter}">
<TextBlock Text="{Binding Value2}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate DataType="{x:Type ViewModels:ItemPresenter}">
<TextBox Text="{Binding Value2}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>