0

I have created a TableLayoutPanel that I update in the code to add/remove rows. In order to avoid flickering, I use double buffering as answered here: How to avoid flickering in TableLayoutPanel in c#.net

The problem is that, with this double buffering, the TableLayoutPannel does not always update itself when I remove some control. If I open another window and go back to the container of the TableLayoutPanel, then it is updated and work as it should. You can see a video of the problem here: TableLayoutPannel double buffering update issues.

Here is the code that run when a row is deleted:

 private void FiringEvent_DeleteRequest(object sender, EventArgs e)
 {
      FiringEvent senderEvent = (FiringEvent)sender;
      ActionLayoutPanel.Controls.Remove(senderEvent.LabelTimeControl);
      ActionLayoutPanel.Controls.Remove(senderEvent.LabelActionControl);
      ActionLayoutPanel.Controls.Remove(senderEvent.LabelDataControl);
      ActionLayoutPanel.Controls.Remove(senderEvent.ButtonControl);
      ActionLayoutPanel.RowCount--;
      firingEventList.Remove(firingEvent);
      updateUI();
 }

 private void updateUI()
 {
      ActionLayoutPanel.SuspendLayout();
      /* Sort all event by DateTime and order them in the table */
      firingEventList.Sort((x, y) => DateTime.Compare(x.eventTime, y.eventTime));
      for (int i = 0; i < firingEventList.Count; i++)
      {
           ActionLayoutPanel.SetRow(firingEventList[i].LabelTimeControl, i + 2);
           ActionLayoutPanel.SetColumn(firingEventList[i].LabelTimeControl, 0);
           ActionLayoutPanel.SetRow(firingEventList[i].LabelActionControl, i + 2);
           ActionLayoutPanel.SetColumn(firingEventList[i].LabelActionControl, 1);
           ActionLayoutPanel.SetRow(firingEventList[i].LabelDataControl, i + 2);
           ActionLayoutPanel.SetColumn(firingEventList[i].LabelDataControl, 2);
           ActionLayoutPanel.SetRow(firingEventList[i].ButtonControl, i + 2);
           ActionLayoutPanel.SetColumn(firingEventList[i].ButtonControl, 3);
     }
     ActionLayoutPanel.ResumeLayout();
 }

ActionLayoutPanel is a a "DBLayoutPanel" object:

public partial class DBLayoutPanel : TableLayoutPanel
{
    public DBLayoutPanel()
    {
        InitializeComponent();
        SetStyle(ControlStyles.AllPaintingInWmPaint |
          ControlStyles.OptimizedDoubleBuffer |
          ControlStyles.UserPaint, true);
    }

    public DBLayoutPanel(IContainer container)
    {
        container.Add(this);
        InitializeComponent();
        SetStyle(ControlStyles.AllPaintingInWmPaint |
          ControlStyles.OptimizedDoubleBuffer |
          ControlStyles.UserPaint, true);
    }
}

One solution to fix this issue is to revert the double buffering layout panel to normal TableLayoutPanel but then I have the flickering issue, so this is not really a good option.

I also tried to update the layout panel using the Update() method but it does not seem to work either. I don't understand why it update and work well when I click on another form and click on the one containing the table layout again.

P.S: The forms are handled using WeifenLuo.WinFormsUI.Docking/DockContent

  • That's not double-buffering. Use `ActionLayoutPanel.SuspendLayout(); [...] ActionLayoutPanel.ResumeLayout(true);` -- To double-buffer it, if you don't want to create a Custom Control, add `ActionLayoutPanel.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(ActionLayoutPanel, true);` in the Form Constructor. May not help much here. – Jimi Aug 17 '21 at 16:10
  • It seems that the problem is that this double buffering is clashing with the solution of this post, which I have implemented in my code while I was trying to solve the flickering problem: https://stackoverflow.com/questions/487661/how-do-i-suspend-painting-for-a-control-and-its-children. – Antoine Boré Aug 17 '21 at 16:12
  • Maybe try forcing the form to update like this: this.Invalidate(); –  Aug 17 '21 at 16:12
  • @Jimi What I mean by double-buffering is that ActionLayoutPanel is an herited class of TableLayoutPanel. The constructor of this class is class this method: SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true); This is the solution described in the first link. – Antoine Boré Aug 17 '21 at 16:15
  • There's no reference to the `OptimizedDoubleBuffer` Style in the first link. You don't need the Constructor that treats the TableLayoutPanel as a Component. You can keep `AllPaintingInWmPaint`. -- Set the `SuspendLayout() / ResumeLayout(true)` part as described and read the notes here: [Remove Row inside TableLayoutPanel makes a layout problem](https://stackoverflow.com/a/55225457/7444103) -- I suggest to create a UserControl that contains the Controls needed, so you add/remove just one child Control to/from your TLP. – Jimi Aug 17 '21 at 16:24
  • 1
    You can also use the `SuspendLayout()` / `ResumeLayout(false)` / `PerformLayout()` pattern. -- Double buffering doesn't cause that problem. Try with a standard TableLayoutPanel and just set the DoubleBuffered property using reflection as shown above, for testing. – Jimi Aug 17 '21 at 16:27

0 Answers0