问题
I have a form with a DataGridView
and I want to set the columns AutoSizeMode
to Fill
and the grids ColumnHeadersHeightSizeMode
to AutoSize
. My problem is that if the mouse cursor accidentally hovers the upper left cell of the grid when the form loads, the application throws an InvalidOperationException
.
This is what I should see when the form loads: (Note how the cursor is hovering the upper left cell).
This code will provoke the exception:
static class Program
{
[STAThread]
static void Main()
{
// Make sure the mouse will hover upper left cell when the form loads:
var form = new MyForm { StartPosition = FormStartPosition.Manual };
form.SetDesktopLocation(Cursor.Position.X - 30, Cursor.Position.Y - 40);
Application.Run(form);
}
class MyForm : Form
{
public MyForm()
{
var grid = new DataGridView { Dock = DockStyle.Fill };
grid.Columns.Add("ColumnName", "HeaderText");
// The form will load if I remove one of the two next lines:
grid.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
grid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
Controls.Add(grid);
}
}
}
In my configuration Visual Studio swallows the exception, so I have to run the application from Windows Explorer or command prompt to see the error.
This is the stacktrace:
System.InvalidOperationException: This operation cannot be performed while an auto-filled column is being resized.
at System.Windows.Forms.DataGridView.PerformLayoutPrivate(Boolean useRowShortcut, Boolean computeVisibleRows, Boolean invalidInAdjustFillingColumns, Boolean repositionEditingControl)
at System.Windows.Forms.DataGridView.SetColumnHeadersHeightInternal(Int32 columnHeadersHeight, Boolean invalidInAdjustFillingColumns)
at System.Windows.Forms.DataGridView.AutoResizeColumnHeadersHeight(Boolean fixedRowHeadersWidth, Boolean fixedColumnsWidth)
at System.Windows.Forms.DataGridView.OnColumnHeadersGlobalAutoSize()
at System.Windows.Forms.DataGridView.set_TopLeftHeaderCell(DataGridViewHeaderCell value)
at System.Windows.Forms.DataGridView.get_TopLeftHeaderCell()
at System.Windows.Forms.DataGridView.GetCellInternal(Int32 columnIndex, Int32 rowIndex)
at System.Windows.Forms.DataGridView.OnCellMouseEnter(DataGridViewCellEventArgs e)
at System.Windows.Forms.DataGridView.UpdateMouseEnteredCell(HitTestInfo hti, MouseEventArgs e)
at System.Windows.Forms.DataGridView.OnColumnWidthChanged(DataGridViewColumnEventArgs e)
at System.Windows.Forms.DataGridView.OnBandThicknessChanged(DataGridViewBand dataGridViewBand)
at System.Windows.Forms.DataGridViewBand.set_ThicknessInternal(Int32 value)
at System.Windows.Forms.DataGridView.AdjustFillingColumns()
at System.Windows.Forms.DataGridView.ComputeLayout()
at System.Windows.Forms.DataGridView.PerformLayoutPrivate(Boolean useRowShortcut, Boolean computeVisibleRows, Boolean invalidInAdjustFillingColumns, Boolean repositionEditingControl)
at System.Windows.Forms.DataGridView.OnHandleCreated(EventArgs e)
at System.Windows.Forms.Control.WmCreate(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.DataGridView.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
Two questions targets the same issue: Here and here, but the application still crashes when I apply the suggested answers.
Am I breaking some kind of best practice in the provided example? Has anyone come across this behavior before and know a workaround?
回答1:
This seems to be a bug - the code is trying to access dataGridView.TopLeftHeaderCell
, which when happens for the first time actually creates that cell and triggers some layout actions not expected at that moment.
With all that in mind, the fix is simple. We need to make sure that the TopLeftHeaderCell
is created before DataGridView
handle, by adding the following line (before addding the grid to Controls
for instance)
var topLeftHeaderCell = grid.TopLeftHeaderCell; // Make sure TopLeftHeaderCell is created
回答2:
Thank you, Ulf, for the excellent sample showing how to reproduce this. One of my clients reported this bug to me and your sample has been invaluable.
Taking Ivan's excellent answer one step further, creating your own grid inheriting from the DataGridView
should prevent this ridiculous bug permanently. Just be sure to always use the custom grid throughout your application.
public class Grid
: DataGridView
{
protected override void OnHandleCreated(EventArgs e)
{
// Touching the TopLeftHeaderCell here prevents
// System.InvalidOperationException:
// This operation cannot be performed while
// an auto-filled column is being resized.
var topLeftHeaderCell = TopLeftHeaderCell;
base.OnHandleCreated(e);
}
}
回答3:
You issue is related with the fact that main thread draw interface.
Actually you handle drawing (starting position) before application run is launched and before layout operation (controls) are all completly initialize inside suspend layout block.
Changing your approach solve the issue (no need to change your code, only add some pieces).
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyForm());
}
public class MyForm : Form
{
public MyForm()
{
this.InitializeComponents();
}
private void MyForm_Shown(object sender, EventArgs e)
{
this.SetDesktopLocation(Cursor.Position.X - 30, Cursor.Position.Y - 40);
}
private void InitializeComponents()
{
this.SuspendLayout();
this.StartPosition = FormStartPosition.Manual ;
var grid = new DataGridView { Dock = DockStyle.Fill };
grid.Columns.Add("ColumnName", "HeaderText");
// The form will load if I remove one of the two next lines:
grid.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
grid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
Controls.Add(grid);
this.Shown += new System.EventHandler(this.MyForm_Shown);
this.ResumeLayout(false);
}
}
}
来源:https://stackoverflow.com/questions/34344499/invalidoperationexception-this-operation-cannot-be-performed-while-an-auto-fill