Winforms - Any way of checking if layout is complete.

Unimatrix_001
Unimatrix_001 used Ask the Experts™
on
Hello,

I'm wanting to do some work once a control has finished sizing and laying out, I cannot do this through the layout event as this occurs before the layout is actually performed, but what I am doing is starting a timer when the layout event is raised and in the timer is where I am doing the required work. Now is there some way for me to check that the layout has actually finished within the timer function before I do the work...

Thank you,
Uni.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
hmmmm.... It's me again. Could you please let me know your actual use case?
For this question, it's not very difficult:
        private bool isLayoutChanged = false;
        private void panel1_Layout(object sender, LayoutEventArgs e)
        {
            isLayoutChanged = true;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (isLayoutChanged == true)
            {
                //Update the labels.
                label1.Text = "Panel 1 Height   : " + panel1.Size.Height;
                label2.Text = "Flow Panel Height: " + flowLayoutPanel1.Size.Height;
                isLayoutChanged = false;
            }
        }

Author

Commented:
You're getting all the questions today. ;) Ok - you're sort of there... The only issue with that code is this problem:

- Layout fires.
- Form STARTS layout process.
- Timer picks up layout event.
- Does work.
- Form FINISHES layout process.

See, the issue I think I have with using a timer is that it may be possible for the work within the timer to start before the form actually finishes laying things out. One way around it would be to set the timer interval to something high enough which would reduce the probability of this happening, but that way I'm essentially relying on the layout process taking a constant amount of time.

Thanks,
Uni

Commented:
what about this:
        private bool isLayoutChanged = false;
        private void panel1_Layout(object sender, LayoutEventArgs e)
        {
            isLayoutChanged = true;
            panel1.Refresh();
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            if (isLayoutChanged)
            {
                //Update the labels.
                label1.Text = "Panel 1 Height   : " + panel1.Size.Height;
                label2.Text = "Flow Panel Height: " + flowLayoutPanel1.Size.Height;
                isLayoutChanged = false;
            }
        }
Build an E-Commerce Site with Angular 5

Learn how to build an E-Commerce site with Angular 5, a JavaScript framework used by developers to build web, desktop, and mobile applications.

Commented:
Disregard it, not working.

Author

Commented:
Hehe - you beat me to it. ;)

Commented:
Hi, after some investigation, I have to say the timer might be the best way. If you can tell me the use case, we can discuss if Control_Add + Resized are enough to handle this. (You can put that code in  both Control Added and the timer actually)

Author

Commented:
Hi,

Unfortunately without going into detail Control_Add isn't enough. Consider the following:

- A panel called P1.
- A panel called P2.
- P2 is a child of P1.
- P2 has controls that resize for various reasons.

Now, for P1 the layout event fires when the child controls of P2 are resized, but if I only monitor the control add for P1, then it would only ever get fired once and that is when the form is first created.

Thanks,
Uni

Commented:
You are correct. Would you mind to share the use case with me?

Author

Commented:
I'm sorry - could you please explain what you mean by "use case"?

Thanks,
Uni

Commented:
I mean what do you want acutally. Why would you want to calculate the size immediatelay after the OnLayout

Author

Commented:
Well, I'm doing a custom scroll, but instead of a bar I'm using two buttons. They need to be enabled or disabled depending on the size of the child...

The real thing I'm after though is finding out if there is a way for me to check the layout engine has completed its task within the tick event.
Cant you just call
Application.DoEvents()
to make sure the layout has happened, and then do your work?

Author

Commented:
Where would I call that?
In your timer's tick event handler.

Author

Commented:
I'm not sure I follow:

- Layout even fires.
- Form STARTS layout process.
- Timer picks up layout event.
- Timer calls DoEvents
- ???
- Does work.
- Form FINISHES layout process.

After DoEvents is called, I still have to wait for the events to complete before continuing on with "Does Work", which seems to leave me back where I started?

Thanks,
Uni
OK. Another option:
Inherit the Panel class. Use this derived class instead of the panel in your form for the parent panel. Override the OnLayout event handler in this derived class, making sure a call is made to base.OnLayout. This way, your derived panel will do all that the actual panel class does, and then you can either do your work here or set a flag that others (such as the timer) can use to tell if layout is over.

Here is some sample code:
Trying to get the sample code in here again:
//code snippet in the form's designer
this.panel1 = new MyPanel(); //parent panel
this.panel2 = new System.Windows.Forms.Panel(); //child panel

//code snippet for custom panel
class MyPanel : System.Windows.Forms.Panel
{
  private bool _isLayoutOver;
  public bool IsLayoutOver
  {
    get { return _isLayoutOver; }
    set { _isLayoutOver = value; }
  }

  protected override void OnLayout(LayoutEventArgs levent)
  {
    _isLayoutOver = false;
    base.OnLayout(levent);
    _isLayoutOver = true;
  }
}

//code snippet for timer
private void timer1_Tick(object sender, EventArgs e)
{
  if (panel1.IsLayoutOver)
  {
    //do work here
    timer1.Enabled = false;
  }
}

Open in new window

Author

Commented:
Sudheesh,

Would this work without the use of a timer - as shown in the code below.

Thanks,
Uni
//code snippet in the form's designer
this.panel1 = new MyPanel(); //parent panel
this.panel2 = new System.Windows.Forms.Panel(); //child panel

//code snippet for custom panel
class MyPanel : System.Windows.Forms.Panel
{

  protected override void OnLayout(LayoutEventArgs levent)
  {
    base.OnLayout(levent);
    //Do work here.
  }

}

Open in new window

Yes, I had mentioned "you can either do your work here or set a flag". But, I would not prefer to do the "work" here since that would tie that logic into the custom panel control. From what I understand, this "work" is specific to an instance of the panel in your form, and not so generic. You can stick to using the timer and set a low interval of say 100 milli seconds.

One update to the timer tick event would be to have
timer1.Enabled = false;
before you execute the "work"
This will make sure the work doesnt get done multiple times.

Author

Commented:
Failing that, I could get MyPanel to raise a custom LayoutFinished event?
Yes, I guess that would do as well.

Author

Commented:
Thank you. :)

Commented:
So which one is the solution? The timer?

Author

Commented:
No,

http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_25102899.html?cid=748#26463190

inheriting the panel and handling the layout event of the custom panel.

Thanks,
Uni

Commented:
Nice to know : )
We need to have a custom panel, overriding the OnLayout event handler. At the end of this event handler, we can
1. directly do some work here
2. set a property (which the timer will look for on a timely basis) or
3. raise a LayoutFinished event which can then be handled in the form
I think Unimatrix_001 has gone for the 3rd option.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial