I have implemented a DataGridView
on my form and successfully implemented VirtualMode
. This retrieves cell data from a local cache and all appears to be working correctly when populating the grid/paging etc. I handle the DataGridView.CellValueNeeded
event to populate the cells.
On the DataGridView, I have the AutoSizeColumnsMode
property set to DataGridViewAutoSizeColumnsMode.DisplayedCells
. I have noticed that in using VirtualMode the DataGridView does not appear to respect the AutoSizeColumnsMode after populating the cells. I have examined this article but not found a solution.
What I would ultimately like to do is to not rely on the AutoSizeColumnsMode
property, but rather call the .AutoResizeColumn()
method somewhere to resize, so I initially autosize the column, but then allow the user to resize.
I have tried the following with limited or no success:
Set DataGridView.AutoSizeColumnsMode
to .None
. Then in my
.CellValueNeeded
handler
private void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
// ... Get cell value from cache
dataGridView.AutoResizeColumn(e.ColumnIndex, DataGridViewAutoSizeColumnMode.DisplayedCells);
}
This throws a StackOverFlowException
presumably because it
repeatedly raises .CellValueNeeded
.
Tried exactly the same thing except in the .CellFormatting
event handler. Got the same StackOverFlowException
.
Tried with and without DataGridView.SuspendLayout/ResumeLayout
:
private void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
// ... Get cell value from cache
dataGridView.CellValueNeeded -= dataGridView_CellValueNeeded;
dataGridView.AutoResizeColumn(e.ColumnIndex, DataGridViewAutoSizeColumnMode.DisplayedCells);
dataGridView.CellValueNeeded += dataGridView_CellValueNeeded;
}
This gives all blank cells, so no use.
This one actually somewhat works, for a reason I don't understand:
private void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
// ... Get cell value from cache
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
}
It resizes the columns correctly, but it seems odd to have to repeatedly call it on every cell value needed. Also, I cannot immediately set it to .None right after or it will StackOverFlowException
again. Thus, I cannot allow user to resize columns.
Calling .UpdateCellValue()
as mentioned in the article from my .CellValueNeeded
handler throws StackOverFlowException
as well.
So is it possible to call .AutoResizeColumn()
somewhere where it won't raise .CellValueNeeded
until it overflows? Since #4 seems to have the ability to perform the autosize function it seems as though I could manually call it from somewhere as well.
I think this may be the solution, although I am still interested in hearing what others have to say.
I continued to look at some of the other events raised by the DataGridView
and found the .RowPostPaint
event. I created the following handler:
private void dataGridView_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
if (dataGridView.AllowUserToResizeColumns) //So not to run unnecessarily
{
return;
}
var lastIndex = dataGridView.Rows.GetLastRow(DataGridViewElementStates.Displayed);
if (e.RowIndex == lastIndex) //Only do on the last displayed row
{
dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells);
dataGridView.AllowUserToResizeColumns = true; // User has control from here on
}
}
This accomplishes the autosizing of the columns upon the initial data-load, then allows the user to re-size from there. It only does this once, so way better than on every cell value needed. I must set dataGridView.AllowUserToResizeColumns = false
prior to initial data loading.
This seems to fit the bill. The user will see nicely fit columns upon initial load and can tweak from there, and most of my data is comparable length from row to row, so should not truncate or waste space after that in most cases.