Мне нужно реализовать функцию «копировать значение в буфер обмена» для WPF DataGrid, которая должна быть доступна через все общие каналы: щелкните правой кнопкой мыши элемент контекстного меню; пункт контекстного меню клавиши меню; Ctrl + C горячая клавиша. Содержимое сетки данных поступает из привязки данных, но поскольку команда копирования предназначена только для просмотра, она полностью реализована на уровне представления, а не в модели представления. Поэтому я не использую для этого ICommand, а только обработчики событий в коде.

SelectionUnit DataGrid имеет значение FullRow, но клавиши навигации со стрелками по-прежнему работают, и прямоугольник фокуса можно увидеть для отдельных ячеек. Таким образом, можно сфокусировать одну ячейку, пока выделена вся строка. Для этой команды мне нужно определить ячейку в фокусе.

Пункт меню уже довольно сложный. Его событие Click дает мне экземпляр MenuItem, из которого я могу перейти к ContextMenu и далее к DataGrid. Вот и все, без щелчка на ячейке. Я уже знаю, как получить DataGrid, единовременно виден только один из них.

Теперь мне нужно выяснить, какая ячейка сфокусирована. Но я даже не могу получить список выбранных ячеек или строк или даже всех строк. Все свойства, которые я могу найти, просто указывают на некоторые фрагменты информации, а DataGrid.Rows просто не существует. Я мог бы просканировать все визуальное дерево в поисках некоторой целенаправленной DataGridCell, но это, вероятно, не очень эффективно.

Любые идеи?

Позже мне также понадобится вторая функция «копировать выбранные строки в буфер обмена», которая копирует значения из всех ячеек во всех выбранных строках в формате, который можно вставить в Excel (строки с разделителями табуляции).

1
ygoe 24 Мар 2014 в 14:27

1 ответ

Лучший ответ

Вспомогательная функция:

    /// <summary>
    /// Look for child element
    /// </summary>
    /// <typeparam name="childItem">Child Item</typeparam>
    /// <param name="obj">Dependency Object</param>
    /// <returns>The child or null</returns>
    private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
    {
        if (obj == null)
        {
            return null;
        }

        int childCount = VisualTreeHelper.GetChildrenCount(obj);

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);

            if (child != null && child is childItem)
            {
                return (childItem)child;
            }
            else
            {
                childItem childOfChild = this.FindVisualChild<childItem>(child);

                if (childOfChild != null)
                {
                    return childOfChild;
                }
            }
        }

        return null;
    }

После загрузки DataGrid вы можете вызвать это:

    /// <summary>
    /// Get the cell of the datagrid.
    /// </summary>
    /// <param name="dataGrid">The data grid in question</param>
    /// <param name="cellInfo">The cell information for a row of that datagrid</param>
    /// <param name="cellIndex">The row index of the cell to find. </param>
    /// <returns>The cell or null</returns>
    private DataGridCell TryToFindGridCell(DataGrid dataGrid, DataGridCellInfo cellInfo, int cellIndex = -1)
    {
        DataGridRow row;
        DataGridCell result = null;

        if (cellIndex < 0)
        {
            row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem(cellInfo.Item);
        }
        else
        {
            row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(cellIndex);
        }

        if (row != null)
        {
            int columnIndex = dataGrid.Columns.IndexOf(cellInfo.Column);

            if (columnIndex > -1)
            {
                DataGridCellsPresenter presenter = this.FindVisualChild<DataGridCellsPresenter>(row);

                if (presenter != null)
                {
                    result = presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex) as DataGridCell;
                }
                else
                {
                    result = null;
                }
            }
        }

        return result;
    }

Затем, когда вы получите свою ячейку, просто проверьте свойство IsFocused.

2
ouflak 24 Мар 2014 в 14:39
1
Спасибо. Это решило поиск содержимого сфокусированной ячейки. К сожалению, я обнаружил, что выделенная ячейка фокусируется только в том случае, если строка не была выбрана раньше. При щелчке правой кнопкой мыши по другой ячейке в той же строке фокус не перемещается, и я получаю не ту ячейку. Но это проблема моего первоначального вопроса.
 – 
ygoe
24 Мар 2014 в 15:58
Интересный. Интересно, не могли ли вы отменить выбор строк ..? Возможно, мы рассмотрим это чуть позже.
 – 
ouflak
24 Мар 2014 в 16:02
Тем временем я изменил этот код. Я использую в качестве основы только выделенную ячейку, если нажата клавиша «Приложения», а затем открываю контекстное меню прямо под ячейкой. При щелчке правой кнопкой мыши я использую e.OriginalSource и нахожу родительский элемент DataGridCell в визуальном дереве. В этом случае я также открываю контекстное меню прямо под ячейкой. Только если ячейка не может быть найдена, используется позиция мыши.
 – 
ygoe
24 Мар 2014 в 23:50