Home > Blockchain >  Why is a WPF DataGridCell.Content a TextBlock instead of a TextBox after adding a new item programma
Why is a WPF DataGridCell.Content a TextBlock instead of a TextBox after adding a new item programma

Time:11-28

I want to copy-paste data from a Excel table into a WPF DataGrid. For this I have the following function, which gets called on the KeyDown event:

private void OnDataGridPasteData(object sender, KeyEventArgs e)
{
    if ((Keyboard.Modifiers != ModifierKeys.Control) || e.Key != Key.V)
        return;

    DataGrid dg = (DataGrid)sender;

    var pastedData = Clipboard.GetText();
    List<string> rowDataCombined = pastedData.Split(new string[] { Environment.NewLine }, StringSplitOptions.None).ToList();
    List<string[]> rowData = new List<string[]>();
    foreach (var row in rowDataCombined)
    {
        rowData.Add(row.Split(new string[] { "\t" }, StringSplitOptions.None));
    }

    int initialRowIdx = Math.Max(dg.Items.IndexOf(dg.SelectedItem), 0);
    int initialColIdx = dg.CurrentCell.Column.DisplayIndex;
    int maxColIdx = dg.Columns.Count;
    int maxRowIdx = initialRowIdx   rowData.Count;

    Type sourceType = dg.ItemsSource.GetType().GetGenericArguments()[0];
    var sourceList = (IList)dg.ItemsSource;

    for (int r = initialRowIdx; r < maxRowIdx; r  )
    {
        if(r > dg.Items.Count - 1)
        {
            var newRowObject = Activator.CreateInstance(sourceType);
            sourceList.Add(newRowObject);
        }

        DataGridRow dgRow = GetRowFromDataGrid(dg, r);
        if (dgRow == null)
            continue;

        for (int c = initialColIdx; c < maxColIdx; c  )
        {
            var presenter = FindVisualChild<DataGridCellsPresenter>(dgRow);
            // try to get the cell but it may possibly be virtualized
            var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(c);
            if (cell == null)
            {
                // now try to bring into view and retreive the cell
                dg.ScrollIntoView(dgRow, dg.Columns[c]);
                cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(c);
            }

            if(cell != null)
            {
                cell.Focus();
                if (!dg.BeginEdit()) //abort pasting if editing fails
                {
                    dg.CancelEdit();
                    return;
                }

                if(cell.Content is TextBox tb)
                {
                    tb.Text = rowData[r - initialRowIdx][c - initialColIdx];
                }
                if(!dg.CommitEdit()) // abort pasting if commit fails (e.g. wrong entry type)
                {
                    dg.CancelEdit();
                    return;
                }
            }
        }
    }
}

Notice the line if(cell.Content is TextBox tb):

  • For existing DataGridCells this will be true
  • For newly added items (line sourceList.Add(newRowObject);) this will be false. The content type then is a TextBlock
  • If I add all the needed rows in a while loop in the same manner as above before entering the for loops, it will also result to true. However, I want to abort the whole operation, if the pasting fails without having the extra rows afterwards.

The DataGrid has AutoGenerateColumns set to true. Generated cells seem to get a TextBlock initially as content, which is later changed to a TextBox. Question is, what triggers this change, and can I do it manually? Would I break anything, if I simply change the content to a TextBox in case it is a TextBlock?

CodePudding user response:

After searching a while I think I have found the issue. I have set the property DataGrid.CanUserAddRows to true by default. After editing the last row, it seems the DataGrid is therefore adding an empty row consisting of TextBlocks (until the user starts editing in the GUI) after the programmatic edits.

Setting the flag to false at the beginning of my paste function and back to true before returning fixes the problem.

  • Related