为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
智慧大石 2023-07-22 18:30:04
我想在用户单击 ✖ 按钮时取消连续更改。源代码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()。我想的对吗?如何查找所有存在验证错误的单元格?还有其他方法可以取消编辑吗?
查看完整描述

2 回答

?
人到中年有点甜

TA贡献1895条经验 获得超7个赞

我意识到我不需要找到存在验证错误的单元格。我需要做的就是在所有单元格上调用 CancelEdit() 。


private void CancelChangesButton_Click(object sender, RoutedEventArgs e)

{

    var cc = dataGrid.CurrentCell;

    foreach (var col in datagrid.Columns)

    {

        datagrid.CurrentCell = new DataGridCellInfo(datagrid.CurrentItem, col);

        datagrid.CancelEdit();

    }

    dataGrid.CurrentCell = cc;

}

它还可以与 DataGridTemplateColumn 一起使用。 解决方案代码

但是,如果您想查找哪些单元格包含验证错误,则需要更深入地研究。感谢@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()

{

    SomethingItem item = datagrid.CurrentItem as SomethingItem;


    DataGridRow row = (DataGridRow)datagrid.ItemContainerGenerator.ContainerFromItem(item);


    if (Validation.GetHasError(row))

    {

        var cc = dataGrid.CurrentCell;

        foreach (DataGridColumn col in datagrid.Columns)

        {

            DataGridCell cell = (DataGridCell)col.GetCellContent(item).Parent;

            List<DependencyObject> errs = GetVisualChildrenHavingError(cell);

            if (errs != null)

            {

                datagrid.CurrentCell = new DataGridCellInfo(item, col);

                datagrid.CancelEdit(DataGridEditingUnit.Cell);

            }

        }

        dataGrid.CurrentCell = cc;

    }

}


/// <summary>

/// Returns all visual children that HasError. Return null if nothing is found.

/// </summary>

public static List<DependencyObject> GetVisualChildrenHavingError(DependencyObject parent)

{

    List<DependencyObject> result = null;

    GetVisualChildrenHavingError(parent, ref result);

    return result;

}


private static void GetVisualChildrenHavingError(DependencyObject parent, ref List<DependencyObject> result)

{

    for (int childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)

    {

        DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);


        if (Validation.GetHasError(childElement))

        {

            if (result == null)

                result = new List<DependencyObject>();


            result.Add(childElement);

        }


        GetVisualChildrenHavingError(childElement, ref result);

    }

}

有用的链接 - 绑定、验证、DataGrid:


查看完整回答
反对 回复 2023-07-22
?
慕斯王

TA贡献1864条经验 获得超2个赞

红细胞显示验证错误。只要存在验证错误,您就无法取消编辑模式(除非用户按Escape键)。

唯一的解决方案是通过简单地恢复输入来手动解决错误。

算法如下:

  1. 获取当前单元格

  2. 获取当前cell的容器

  3. 检查容器(单元)是否存在验证错误。如果是,则继续步骤 4,否则只需取消编辑(跳至步骤 9)

  4. TextBox获取单元格模板的编辑

  5. TextBox.Text识别编辑属性的绑定源(数据项)属性

  6. 获取属性的值(使用反射来实现通用行为)

  7. 恢复内容

  8. 将键盘焦点移回编辑TextBox,这将触发重新验证并定义取消的目标单元格。

  9. 取消编辑

执行:

private void CancelChangesButton_Click(object sender, RoutedEventArgs e)

{

  DependencyObject cellItemContainer = this.datagrid.ItemContainerGenerator.ContainerFromItem(

    (this.datagrid.CurrentCell.Item as SomethingItem));


  // If the current cell has validation errors find the edit TextBox child control

  if (Validation.GetHasError(cellItemContainer) && TryFindChildElement(cellItemContainer, out TextBox editTextBox))

  {

    // Get the property name of he binding source

    var propertyName = (editTextBox.BindingGroup.BindingExpressions.FirstOrDefault() as BindingExpression)?.ResolvedSourcePropertyName ?? string.Empty;


    // Use reflection to get the value of the binding source

    object value = this.datagrid.CurrentCell.Item.GetType().GetProperty(propertyName).GetValue(this.datagrid.CurrentCell.Item);


    // Check which ToString() to invoke

    editTextBox.Text = value is DateTime date 

      ? date.ToShortDateString() 

      : value.ToString();


    // Trigger validation and define which cell to cancel the edit

    // This is required because the edit TexBox lost focus

    Keyboard.Focus(editTextBox);

  }


  this.datagrid.CancelEdit();

}


// Traverses the visual tree to find a child element of type TElement

private bool TryFindVisualChild<TChild>(DependencyObject parent, out TChild resultElement) where TChild : DependencyObject

{

  resultElement = null;

  for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)

  {

    DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);


    if (childElement is Popup popup)

    {

      childElement = popup.Child;

    }


    if (childElement is TChild)

    {

      resultElement = childElement as TChild;

      return true;

    }


    if (TryFindVisualChild(childElement, out resultElement))

    {

      return true;

    }

  }


  return false;

}


查看完整回答
反对 回复 2023-07-22
  • 2 回答
  • 0 关注
  • 124 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信