Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Windows forms DataGridView Cell text disappears when added programatically

I am developing a Windows forms application that includes a DataGridView. This DataGridView has 3 columns, all of which are simply text cells:

  • Timestamp
  • Connection
  • Message

The issue I'm running into is that when I add a row (programmatically), I'm finding that the text disappears if it is too long. To be specific, if the text exceeds 4563 characters in length, then the text disappears.

I know that the DataGridViewTextBoxColumn class has a property call MaxInputLength that can limit the number of characters entered. But according the the Documentation, it only affects text that is input manually by the user. I, however, am inputting this text programmatically.

Just to make sure though, I set this property very high but the disappearing text issue still arises when I pass the 4563 character limit.

One thing I have noticed is that the text is still there (i.e. the scroll bar along the bottom can still be scrolled as though the text is still there) but I cannot see the text itself. I can also edit the text.

I can add characters until the 4563 limit but as soon as I pass that, the text disappears. If I press backspace to return to exactly 4563 characters, the text reappears.

I am developing this using .NET 4.0, since I have to support Windows XP.

like image 249
darrunategui Avatar asked Mar 10 '14 17:03

darrunategui


1 Answers

Here's the short answer that will probably disappoint you: It's a reported bug and verified by Microsoft, closed as "Not important enough to fix". There may be more instances of it, but it's been known since at least 2011 DataGridView control shows blank cell if large string is entered and column resized to max. The "workaround" is to just limit the size of the cells width, but for you that may not be satisfactory.

However, curiosity got the best of me so I started looking into it a little deeper; Here's the first observation worth mentioning:

enter image description here

If you look at the series of pictures, you'll notice I replicated your problem with the default font size/style and the specific number 5460. What's so special about 5460? Well, nothing in particular, except that as your character threshold crosses it the ContentBounds and Width of the column passes 32767. What's so special about 32767? Other than being the default MaxInputLength of a DataGridViewTextBoxCell, it's the upper limit of a signed short or Int16 (2^15-1). I highly doubt it's a coincidence the issue is occurring here, though not cause of anything to do with MaxInputLength per se. I'd be willing to bet you first noticed the issue at 4563 characters because your font size expanded the width to 32767 as well.

The next question, is why? I'm not really sure. I started following the rabbit hole and disassembled some of the .NET 4.0 DataGridView* libraries to find out. It's a pretty massive and complicated control, and I haven't been able to draw any definite conclusions, but one thing I found that's worth noting is the absolute maximum width a column can assume is 65536, the value of an UNsigned Int16 (2^16):

enter image description here

You see this check in a lot of private internal places when adding or resizing a column, and I tested it. The size won't go larger

This is ironic for two reasons. For one, using the default settings, you can only display 10922 characters (65536 / 6 pixels per character) in a column despite the editing input length being 32767 characters, and programmatically arbitrary.

Second, why would this issue start cropping up at exactly the max of the signed variant of the columns max width? Hmmmm. This is totally a guess, but I think somewhere along the line the max value for whatever renders the text was set as a regular short instead of an unsigned short... or something along those lines. I have my suspicions of the PaintPrivate() method in the implementation of DataGridViewTextBoxCell(), so if you're feeling frisky, maybe put a microscope to it. You'll need an IL disassembler to see this stuff that's not exposed publicly. Specifically, this part of the code I have suspicions of:

  if (text != null && (paint && !flag2 || computeContentBounds))
  {
    int y = cellStyle.WrapMode == DataGridViewTriState.True ? 1 : 2;
    rectangle3.Offset(0, y);
    // ISSUE: explicit reference operation
    // ISSUE: variable of a reference type
    Rectangle& local = @rectangle3;
    // ISSUE: explicit reference operation
    int width = (^local).Width;
    // ISSUE: explicit reference operation
    (^local).Width = width;
    rectangle3.Height -= y + 1;
    if (rectangle3.Width > 0 && rectangle3.Height > 0)
    {
      TextFormatFlags cellStyleAlignment = DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(this.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode);
      if (paint)
      {
        if (DataGridViewCell.PaintContentForeground(paintParts))
        {
          if ((cellStyleAlignment & TextFormatFlags.SingleLine) != TextFormatFlags.Default)
            cellStyleAlignment |= TextFormatFlags.EndEllipsis;
          TextRenderer.DrawText((IDeviceContext) graphics, text, cellStyle.Font, rectangle3, flag3 ? cellStyle.SelectionForeColor : cellStyle.ForeColor, cellStyleAlignment);
        }
      }
      else
        rectangle1 = DataGridViewUtilities.GetTextBounds(rectangle3, text, cellStyleAlignment, cellStyle);
    }

Sorry for the book!

TL;DR USE A SMALL ASS FONT IF YOU WANT TO PACK CHARACTERS INTO HUGE CELLS.

like image 184
B L Avatar answered Nov 03 '22 10:11

B L