WPF DataGrid - 如何在验证错误后取消编辑行?

我想在用户单击 ✖ 按钮时取消连续更改。

http://img4.sycdn.imooc.com/64bbafbc0001d39503980231.jpg

源代码

private void CancelChangesButton_Click(object sender, RoutedEventArgs e)

{

    datagrid.CancelEdit();

}

CancelEdit() 效果很好,直到...我的 DateConverter 无法转换回字符串。当 ViewModel 的属性设置器抛出异常时,也会发生相同的行为。我在 DataGrid 中无能为力。唯一的方法是当光标位于红色单元格中时按 ESC 键。


我尝试其他事情:


datagrid.CancelEdit(DataGridEditingUnit.Row);

datagrid.CancelEdit(DataGridEditingUnit.Cell);

datagrid.CommitEdit();

datagrid.IsReadOnly = true;

// Add new item

什么都没发生。


所以我开始挖掘 .NET Framework 源代码,我发现了这一点:


public class DataGrid : MultiSelector

...

    public bool CancelEdit(DataGridEditingUnit editingUnit)

    {

        return EndEdit(CancelEditCommand, CurrentCellContainer, editingUnit, true);

    }

-> .NET 参考源

这里最重要的是 CurrentCellContainer,它从 CurrentCell 获取值。接下来,我发现CurrentCell正在跟随焦点。当我单击 ✖ 按钮时,CurrentCell 更改为操作列中的单元格,当我单击 DataGrid 外部时,CurrentCell 更改为 null。

因此,我必须将 CurrentCell 更改为带有验证错误的单元格,然后调用 CancelEdit()。我想的对吗?

如何查找所有存在验证错误的单元格?

还有其他方法可以取消编辑吗?


智慧大石
浏览 129回答 2
2回答

人到中年有点甜

我意识到我不需要找到存在验证错误的单元格。我需要做的就是在所有单元格上调用 CancelEdit() 。private void CancelChangesButton_Click(object sender, RoutedEventArgs e){&nbsp; &nbsp; var cc = dataGrid.CurrentCell;&nbsp; &nbsp; foreach (var col in datagrid.Columns)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; datagrid.CurrentCell = new DataGridCellInfo(datagrid.CurrentItem, col);&nbsp; &nbsp; &nbsp; &nbsp; datagrid.CancelEdit();&nbsp; &nbsp; }&nbsp; &nbsp; dataGrid.CurrentCell = cc;}它还可以与 DataGridTemplateColumn 一起使用。&nbsp;解决方案代码但是,如果您想查找哪些单元格包含验证错误,则需要更深入地研究。感谢@BionicCode,我找到了解决方案。可以得到可视化的DataGridRow:DataGridRow row = (DataGridRow)datagrid.ItemContainerGenerator.ContainerFromItem(item);然后你就可以检查错误:if (Validation.GetHasError(row))并且您还可以访问row.BindingGroup,其中包含该行中的所有绑定 (.BindingExpressions) 以及许多其他信息(IsDirty、ValidationErrors、ValidationRules、CancelEdit())但是,当您想要检查单元格中的错误时,事情就没那么容易了。不幸的是,DataGridCell 不包含有关错误的信息,Validation.GetHasError(cell)无法正常工作。您需要更深入地研究视觉树。private void CancelChangesCellsHavingError(){&nbsp; &nbsp; SomethingItem item = datagrid.CurrentItem as SomethingItem;&nbsp; &nbsp; DataGridRow row = (DataGridRow)datagrid.ItemContainerGenerator.ContainerFromItem(item);&nbsp; &nbsp; if (Validation.GetHasError(row))&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var cc = dataGrid.CurrentCell;&nbsp; &nbsp; &nbsp; &nbsp; foreach (DataGridColumn col in datagrid.Columns)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DataGridCell cell = (DataGridCell)col.GetCellContent(item).Parent;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; List<DependencyObject> errs = GetVisualChildrenHavingError(cell);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (errs != null)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; datagrid.CurrentCell = new DataGridCellInfo(item, col);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; datagrid.CancelEdit(DataGridEditingUnit.Cell);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; dataGrid.CurrentCell = cc;&nbsp; &nbsp; }}/// <summary>/// Returns all visual children that HasError. Return null if nothing is found./// </summary>public static List<DependencyObject> GetVisualChildrenHavingError(DependencyObject parent){&nbsp; &nbsp; List<DependencyObject> result = null;&nbsp; &nbsp; GetVisualChildrenHavingError(parent, ref result);&nbsp; &nbsp; return result;}private static void GetVisualChildrenHavingError(DependencyObject parent, ref List<DependencyObject> result){&nbsp; &nbsp; for (int childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);&nbsp; &nbsp; &nbsp; &nbsp; if (Validation.GetHasError(childElement))&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (result == null)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result = new List<DependencyObject>();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.Add(childElement);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; GetVisualChildrenHavingError(childElement, ref result);&nbsp; &nbsp; }}有用的链接 - 绑定、验证、DataGrid:

慕斯王

红细胞显示验证错误。只要存在验证错误,您就无法取消编辑模式(除非用户按Escape键)。唯一的解决方案是通过简单地恢复输入来手动解决错误。算法如下:获取当前单元格获取当前cell的容器检查容器(单元)是否存在验证错误。如果是,则继续步骤 4,否则只需取消编辑(跳至步骤 9)TextBox获取单元格模板的编辑TextBox.Text识别编辑属性的绑定源(数据项)属性获取属性的值(使用反射来实现通用行为)恢复内容将键盘焦点移回编辑TextBox,这将触发重新验证并定义取消的目标单元格。取消编辑执行:private void CancelChangesButton_Click(object sender, RoutedEventArgs e){&nbsp; DependencyObject cellItemContainer = this.datagrid.ItemContainerGenerator.ContainerFromItem(&nbsp; &nbsp; (this.datagrid.CurrentCell.Item as SomethingItem));&nbsp; // If the current cell has validation errors find the edit TextBox child control&nbsp; if (Validation.GetHasError(cellItemContainer) && TryFindChildElement(cellItemContainer, out TextBox editTextBox))&nbsp; {&nbsp; &nbsp; // Get the property name of he binding source&nbsp; &nbsp; var propertyName = (editTextBox.BindingGroup.BindingExpressions.FirstOrDefault() as BindingExpression)?.ResolvedSourcePropertyName ?? string.Empty;&nbsp; &nbsp; // Use reflection to get the value of the binding source&nbsp; &nbsp; object value = this.datagrid.CurrentCell.Item.GetType().GetProperty(propertyName).GetValue(this.datagrid.CurrentCell.Item);&nbsp; &nbsp; // Check which ToString() to invoke&nbsp; &nbsp; editTextBox.Text = value is DateTime date&nbsp;&nbsp; &nbsp; &nbsp; ? date.ToShortDateString()&nbsp;&nbsp; &nbsp; &nbsp; : value.ToString();&nbsp; &nbsp; // Trigger validation and define which cell to cancel the edit&nbsp; &nbsp; // This is required because the edit TexBox lost focus&nbsp; &nbsp; Keyboard.Focus(editTextBox);&nbsp; }&nbsp; this.datagrid.CancelEdit();}// Traverses the visual tree to find a child element of type TElementprivate bool TryFindVisualChild<TChild>(DependencyObject parent, out TChild resultElement) where TChild : DependencyObject{&nbsp; resultElement = null;&nbsp; for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)&nbsp; {&nbsp; &nbsp; DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);&nbsp; &nbsp; if (childElement is Popup popup)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; childElement = popup.Child;&nbsp; &nbsp; }&nbsp; &nbsp; if (childElement is TChild)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; resultElement = childElement as TChild;&nbsp; &nbsp; &nbsp; return true;&nbsp; &nbsp; }&nbsp; &nbsp; if (TryFindVisualChild(childElement, out resultElement))&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; return true;&nbsp; &nbsp; }&nbsp; }&nbsp; return false;}
打开App,查看更多内容
随时随地看视频慕课网APP