Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StackOverflowException with DataGridView

This is an odd one. I have a DataGridView. I'm setting its DataSource with a List containing objects of my own custom class. There are about 50,000 items in the list. I defined all of the columns I wanted to be visible in the Designer and set AutoGenerateColumns to false.

As soon as I set the DataSource to my list, it is immediately populated correctly. I can scroll up and down, select different rows. Everything is good. But when I scroll all of the way down and then let the window containing the DataGridView lose focus everything freezes up and after a short while the stack overflows as such:

System.Drawing.dll!System.Drawing.SafeNativeMethods.Gdip.GdipDeleteGraphics(System.Runtime.InteropServices.HandleRef graphics) + 0x2a bytes 
    System.Drawing.dll!System.Drawing.Graphics.Dispose(bool disposing) + 0x56 bytes 
    System.Drawing.dll!System.Drawing.Graphics.Dispose() + 0x12 bytes   
    System.Drawing.dll!System.Drawing.Font.GetHeight() + 0xc8 bytes 
    System.Drawing.dll!System.Drawing.Font.Height.get() + 0xb bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRow() + 0x44 bytes    
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.Clone() + 0x44 bytes  
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRowCollection.this[int].get(int index) + 0xa8 bytes   
    System.Windows.Forms.dll!System.Windows.Forms.DataGridView.DataGridViewAccessibleObject.GetChild(int index) + 0xbd bytes    
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x76 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes 
    ...

For some reason the DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() method is calling itself into oblivion. The whole stack seems rather strange to me. Why would Font.Height.get() ever call the DataGridViewRow?

EDIT:

I was asked for some code. This is the designer generated code for the DataGridView and its columns:

    // 
    // dataGridView
    // 
    this.dataGridView.AllowUserToAddRows = false;
    this.dataGridView.AllowUserToDeleteRows = false;
    this.dataGridView.AllowUserToOrderColumns = true;
    this.dataGridView.AllowUserToResizeRows = false;
    this.dataGridView.BackgroundColor = System.Drawing.SystemColors.Window;
    this.dataGridView.BorderStyle = System.Windows.Forms.BorderStyle.None;
    this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
    this.dataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
    this.Date,
    this.Type,
    this.Job,
    this.Mix,
    this.Entry});
    this.dataGridView.Location = new System.Drawing.Point(8, 96);
    this.dataGridView.Name = "dataGridView";
    this.dataGridView.ReadOnly = true;
    this.dataGridView.RowHeadersVisible = false;
    this.dataGridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
    this.dataGridView.Size = new System.Drawing.Size(1152, 504);
    this.dataGridView.TabIndex = 10;
    this.dataGridView.SelectionChanged += new System.EventHandler(this.dataGridView_SelectionChanged);
    // 
    // Date
    // 
    this.Date.DataPropertyName = "FormattedTime";
    this.Date.HeaderText = "Date/Time";
    this.Date.Name = "Date";
    this.Date.ReadOnly = true;
    // 
    // Type
    // 
    this.Type.DataPropertyName = "FormattedType";
    this.Type.FillWeight = 60F;
    this.Type.HeaderText = "Type";
    this.Type.Name = "Type";
    this.Type.ReadOnly = true;
    this.Type.Width = 60;
    // 
    // Job
    // 
    this.Job.DataPropertyName = "Job";
    this.Job.FillWeight = 80F;
    this.Job.HeaderText = "Job No.";
    this.Job.Name = "Job";
    this.Job.ReadOnly = true;
    this.Job.Width = 80;
    // 
    // Mix
    // 
    this.Mix.DataPropertyName = "Mix";
    this.Mix.FillWeight = 80F;
    this.Mix.HeaderText = "Mix No.";
    this.Mix.Name = "Mix";
    this.Mix.ReadOnly = true;
    this.Mix.Width = 80;
    // 
    // Entry
    // 
    this.Entry.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
    this.Entry.DataPropertyName = "FormattedSummary";
    this.Entry.HeaderText = "Entry";
    this.Entry.Name = "Entry";
    this.Entry.ReadOnly = true;

When time comes around to populate the grid view, I simply do a:

dataGridView.DataSource = myList;
like image 637
Frank Weindel Avatar asked Jan 03 '13 19:01

Frank Weindel


2 Answers

Fix

No fix is known at this time.

Workaround

Disabling the Tablet PC Input Service (a.k.a. tabtip.exe) from the Services panel in Control Panel.

Details

A few weeks ago I contacted Microsoft Developer support about this issue. I corresponded with a support engineer on the WinForms team and we figured out that this issue was in some way caused by Accessibility.dll being loaded into my application whenever Microsoft's Tablet PC Input Service (tabtip.exe) was running. This service is present on at least all Windows 7 Enterprise installations but is generally only running if you have a tablet PC or have ever installed any sort of pen input device on your PC. I had used a Wacom Bamboo pen tablet on my PC in the past so it made sense why this service was running. Note that it was determined that neither the method I used to populate the DataGridView nor any of the properties I set had anything to do with the issue.

Accessibility.dll is a Microsoft library that allows certain peripherals (pen tablets and especially assistive devices) to more easily interact and get extra information about forms and the controls contained on forms. I'm not 100% sure on this but I believe that this library is loaded into every running Windows process automatically if such a peripheral is installed.

After examining a dump that I had provided, the Microsoft engineer was puzzled by the code path that the DataGridView decided to go down and found that Accessibility.dll was the culprit that allowed let it happen. He admitted that DataGridView should not be doing this and that this seemed like a problem on their end. However, he was unable to reproduce the problem on his end even after turning on the Tablet PC Input Service on both his PC and a freshly made Windows 7 VM. So while he was able to identify the key players that caused the problem on my PC he was not able to find root cause and hence unable to pursue it further.

This may be attributed to my particular pen tablet device (Wacom) or something else all together. Its unknown. If anyone else encounters this problem, please contact me. The engineer invited me to contact him if I was ever able to narrow down the cause. For now, just keeping the service off prevents the issue.

like image 63
Frank Weindel Avatar answered Sep 25 '22 23:09

Frank Weindel


I have encountered a very similar scenario of a StackOverflowsException in DataGridView.

Conditions:

  1. A WinForm hosting a datagridview control. DataGridView properties ReadOnly and VirtualMode set to true.
  2. VirtualMode's OnCellValueNeeded implemented as per MSFT documentation (http://msdn.microsoft.com/en-us/library/2b177d6d.aspx)
  3. Load a sizable amount of data (15 columns x 1 x 10^5 rows)
  4. Behavior of datagridview is normal (i.e.: responsiveness of GUI, row and cell selection, vertical & horizontal scrolling) if displaying only 10 Columns X 30 rows [300 cells]. When I maximize the display such that the number of displayed cells is much larger, a StackOverflowsException in DataGridView is fired.
  5. I, too, have an attached Wacom tablet to my PC.
  6. The problem disappears if I stop the Tablet PC Input Service (tabtip.exe)

So...in my case, indeed, Franks workaround is in the right direction and supports the idea that 1. the Tablet PC Input Service is a culprit and 2. the number of displayed (active) cells in the screen has an effect on firing the exception.

like image 24
user2551479 Avatar answered Sep 24 '22 23:09

user2551479