Search code examples
c#autoscrolltablelayoutpanel

Graphical glitch in auto-scrolling tablelayoutpanel


I have a WinForms form with a dynamically generated TableLayoutPanel. During runtime I add or remove rows, so I set a maximum size and set it to autoscroll. All the rows and columns are auto-sized, and I added padding for the vertical scrollbar so that it doesn't end up overlapping the cells (and thus forcing the creation of a horizontal scrollbar, using (on form creation):

tableLayoutPanel_dataLogs.Padding = new Padding(0, 0, SystemInformation.VerticalScrollBarWidth, 0);

When there is not enough data to force an autscroll, it looks like this:

Proper appearance without autscroll

The problem is, when it does add the autoscroll, it introduces this odd graphical glitch:

Glitch appears with autoscroll

The glitch is that white line on the right side under the checkmark image and on the border above the checkmark. I assume this must have something to do with the scrollbar appearance settings, but I'm not quite sure what. Any ideas?

EDIT: add code for tableLayoutPanel:

// 
// tableLayoutPanel_dataLogs
// 
this.tableLayoutPanel_dataLogs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
| System.Windows.Forms.AnchorStyles.Left) 
| System.Windows.Forms.AnchorStyles.Right)));
this.tableLayoutPanel_dataLogs.AutoSize = true;
this.tableLayoutPanel_dataLogs.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.tableLayoutPanel_dataLogs.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.InsetDouble;
this.tableLayoutPanel_dataLogs.ColumnCount = 7;
this.tableLayoutPanel_dataLogs.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel_dataLogs.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel_dataLogs.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel_dataLogs.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel_dataLogs.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel_dataLogs.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel_dataLogs.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel_dataLogs.Controls.Add(this.checkBox_getAllDataLogs, 0, 0);
this.tableLayoutPanel_dataLogs.Controls.Add(this.label1, 1, 0);
this.tableLayoutPanel_dataLogs.Controls.Add(this.label2, 3, 0);
this.tableLayoutPanel_dataLogs.Controls.Add(this.label3, 5, 0);
this.tableLayoutPanel_dataLogs.Controls.Add(this.label5, 4, 0);
this.tableLayoutPanel_dataLogs.Controls.Add(this.label4, 2, 0);
this.tableLayoutPanel_dataLogs.Controls.Add(this.pictureBox1, 6, 0);
this.tableLayoutPanel_dataLogs.Location = new System.Drawing.Point(6, 445);
this.tableLayoutPanel_dataLogs.MaximumSize = new System.Drawing.Size(600, 144);
this.tableLayoutPanel_dataLogs.MinimumSize = new System.Drawing.Size(400, 56);
this.tableLayoutPanel_dataLogs.Name = "tableLayoutPanel_dataLogs";
this.tableLayoutPanel_dataLogs.Padding = new System.Windows.Forms.Padding(0, 0, System.Windows.Forms.SystemInformation.VerticalScrollBarWidth, 0);
this.tableLayoutPanel_dataLogs.RowCount = 1;
this.tableLayoutPanel_dataLogs.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel_dataLogs.Size = new System.Drawing.Size(420, 56);
this.tableLayoutPanel_dataLogs.TabIndex = 65;
this.tableLayoutPanel_dataLogs.Visible = false;

And here's the code that adds rows:

tableLayoutPanel_dataLogs.Visible = false;

tableLayoutPanel_dataLogs.SuspendLayout();

for (int i = 0; i < logCount; i++)
{
    //code that generates the data to populated not shown
    //...
    //created currentSerial, currentDateTime, currentEntriesCount

    tableLayoutPanel_dataLogs.RowCount++;
    tableLayoutPanel_dataLogs.RowStyles.Add(new RowStyle(SizeType.AutoSize));
    string row = (tableLayoutPanel_dataLogs.RowCount - 1).ToString("D2");
    string cbName = ControlNames.checkBoxSelectedName + row;
    tableLayoutPanel_dataLogs.Controls.Add(new CheckBox { Name = cbName, Text = String.Empty, Anchor = AnchorStyles.None, AutoSize = true }, 0, tableLayoutPanel_dataLogs.RowCount - 1);
    CheckBox cb = this.Controls.Find(cbName, true).First() as CheckBox;
    checkBoxes.Add(cb.Name,cb);
    cb.CheckedChanged += new System.EventHandler(this.checkBox_getAnyDataLog_CheckedChanged);

    tableLayoutPanel_dataLogs.Controls.Add(new Label() { Name = ControlNames.labelNumberName + row, Text = (tableLayoutPanel_dataLogs.RowCount - 1).ToString(), Anchor = AnchorStyles.None, Font = new System.Drawing.Font("Microsoft Sans Serif", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))), AutoSize = true }, 1, tableLayoutPanel_dataLogs.RowCount - 1);
    tableLayoutPanel_dataLogs.Controls.Add(new Label() { Name = ControlNames.labelSerialName + row, Text = currentSerial, Anchor = AnchorStyles.None, Font = new System.Drawing.Font("Microsoft Sans Serif", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))), AutoSize = true }, 2, tableLayoutPanel_dataLogs.RowCount - 1);
    tableLayoutPanel_dataLogs.Controls.Add(new Label() { Name = ControlNames.labelDateTimeName + row, Text = currentDateTime.ToString("MM/dd/yy HH:mm"), Anchor = AnchorStyles.None, Font = new System.Drawing.Font("Microsoft Sans Serif", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))), AutoSize = true }, 3, tableLayoutPanel_dataLogs.RowCount - 1);
    tableLayoutPanel_dataLogs.Controls.Add(new Label() { Name = ControlNames.labelEntriesName + row, Text = currentEntriesCount, Anchor = AnchorStyles.None, Font = new System.Drawing.Font("Microsoft Sans Serif", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))), AutoSize = true }, 4, tableLayoutPanel_dataLogs.RowCount - 1);

    string tbName = ControlNames.textBoxFileNameName + row;
    tableLayoutPanel_dataLogs.Controls.Add(new TextBox() { Name = tbName, Text = String.Empty, Enabled = false, Anchor = AnchorStyles.Left, Font = new System.Drawing.Font("Microsoft Sans Serif", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))), AutoSize = true }, 5, tableLayoutPanel_dataLogs.RowCount - 1);
    TextBox tb = this.Controls.Find(tbName, true).First() as TextBox;
    tb.TextChanged += new System.EventHandler(this.textBoxFileName_TextChanged);

    tableLayoutPanel_dataLogs.Controls.Add(new PictureBox() { Name = ControlNames.pictureBoxName + row, Image = global::DataKey_Application.Properties.Resources.check_small, Text = "", Enabled = false, Visible = false, Margin = new System.Windows.Forms.Padding(0, 0, 0, 0), SizeMode = PictureBoxSizeMode.AutoSize, Anchor = AnchorStyles.Left }, 6, tableLayoutPanel_dataLogs.RowCount - 1);
    }

tableLayoutPanel_dataLogs.ResumeLayout(); 
tableLayoutPanel_dataLogs.PerformLayout();

Solution

  • OK, time to excavate this and answer my own question (again). I came back to this issue because my tableLayoutPanel didn't resize properly when going from a long list (e.g. autoscroll needed) back to a small list. I took an idea from @Bioukh from here and dropped the tableLayoutPanel into a panel. The panel defines the max size of the tableLayoutPanel, which is docked inside with Dock = top and has autoscroll disabled. Just before I draw the table, I check its preferred height and enable autoscroll on the panel if it's going to be too big. When I clear the table, I also disable autoscroll on the panel. This ensures both that the table is displayed with a scrollbar when it should be (without a graphical glitch), and that the panel scrollbar disappears when the table is small.

    After drawing the table:

    tableLayoutPanel_dataLogs.ResumeLayout(); 
    tableLayoutPanel_dataLogs.PerformLayout();
    if (tableLayoutPanel_dataLogs.GetPreferredSize(new Size()).Height > panel1.Size.Height) 
        panel1.AutoScroll = true;
    tableLayoutPanel_dataLogs.Visible = true;
    

    Clearing the table:

    tableLayoutPanel_dataLogs.SuspendLayout();
    
    TableLayoutControlCollection controls = tableLayoutPanel_dataLogs.Controls;
    for (int i = controls.Count - 1; i > 0; i--)
    {
        if (tableLayoutPanel_dataLogs.GetCellPosition(controls[i]).Row != 0)
        {
            Control control = controls[i];
            if (control.Name.Contains(ControlNames.checkBoxSelectedName)) ((CheckBox)control).CheckedChanged -= new System.EventHandler(this.checkBox_getAnyDataLog_CheckedChanged);
            else if (control.Name.Contains(ControlNames.textBoxFileNameName)) ((TextBox)control).TextChanged -= new System.EventHandler(this.textBoxFileName_TextChanged);
            controls.Remove(control);
            control.Dispose();
        }
    }
    
    while (tableLayoutPanel_dataLogs.RowCount > 1)
    {
        int row = tableLayoutPanel_dataLogs.RowCount - 1;
        tableLayoutPanel_dataLogs.RowStyles.RemoveAt(row);
        tableLayoutPanel_dataLogs.RowCount--;
    }
    tableLayoutPanel_dataLogs.Size = new System.Drawing.Size(420, 56);
    tableLayoutPanel_dataLogs.Visible = false;
    tableLayoutPanel_dataLogs.ResumeLayout();
    tableLayoutPanel_dataLogs.PerformLayout();
    panel1.AutoScroll = false;
    panel1.PerformLayout();
    checkBoxes.Clear();