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 betrue
- For newly added items (line
sourceList.Add(newRowObject);
) this will be false. The content type then is aTextBlock
- If I add all the needed rows in a
while
loop in the same manner as above before entering thefor
loops, it will also result totrue
. 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.