0

I'm trying to set the Height of the form while I'm resizing it, if a condition is met. I have it set to only allow the width of the form to be altered manually using the code provided by in this answer.

I have a FlowLayoutPanel displaying a collection of PictureBox controls, each with a fixed Height of 50 pixels. Initially, the form's Height is 38 (Size.Height - ClientSize.Height) + 50 + 6 (Margin.Top + Margin.Bottom of an image) = 94.

If the controls overflow, by default the FlowLayoutPanel pushes them down onto a new line. What I want to do is resize the form when this happens, or when the form width is manually changed which might cause the controls to jump to the next line.

The following code works, and is called whenever a new control is added to the FlowLayoutPanel (itemPanel):

private void ResizeForm()
{
    if (itemPanel.Controls.Count < 1) return;

    var lastElement = itemPanel.Controls[itemPanel.Controls.Count - 1];

    // The Form is the correct size, no need to resize it:
    if (lastElement.Bottom + lastElement.Margin.Bottom == itemPanel.Height) return;

    Height = 38 + lastElement.Bottom + lastElement.Margin.Bottom;
}

However, when called within my SizeChange event, this method causes the Form to "flash" between the initial Height and the new Height:

private void MainForm_SizeChanged(object sender, EventArgs e)
{
    ResizeForm();
}

I'm guessing the reason is because setting Height will fire the SizeChange event again, but I don't know how to resolve this issue. When I print out the values of lastElement.Bottom + lastElement.Margin.Bottom and itemPanel.Height after setting the Height, they are identical, but the code is still somehow reaching that point.

In a nutshell, I want only the form Width to be manually altered, but the Height of the form to change when items are added or the Width is changed, so that all controls inside the FlowLayoutPanel can be viewed.

Community
  • 1
  • 1
driima
  • 623
  • 1
  • 11
  • 28

2 Answers2

1

However, when called within my SizeChange event, this method causes the Form to "flash" between the initial Height and the new Height

Basically any of the stock "resize" events for your Form will occur too late for you to change the size without it being noticeable.

You'll want to trap the WM_SIZING message:

Sent to a window that the user is resizing. By processing this message, an application can monitor the size and position of the drag rectangle and, if needed, change its size or position.

This will allow you to change the size of the Form before it has actually been updated on the screen.

It would look something like this:

public partial class Form1 : Form
{

    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    enum HitTest
    {
        Caption = 2,
        Transparent = -1,
        Nowhere = 0,
        Client = 1,
        Left = 10,
        Right = 11,
        Top = 12,
        TopLeft = 13,
        TopRight = 14,
        Bottom = 15,
        BottomLeft = 16,
        BottomRight = 17,
        Border = 18
    }

    private const int WM_SIZING = 0x214;
    private const int WM_NCHITTEST = 0x84;

    public Form1()
    {
        InitializeComponent();
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        switch (m.Msg)
        {
            case WM_NCHITTEST:
                var result = (HitTest)m.Result.ToInt32();
                if (result == HitTest.Top || result == HitTest.Bottom)
                    m.Result = new IntPtr((int)HitTest.Caption);
                if (result == HitTest.TopLeft || result == HitTest.BottomLeft)
                    m.Result = new IntPtr((int)HitTest.Left);
                if (result == HitTest.TopRight || result == HitTest.BottomRight)
                    m.Result = new IntPtr((int)HitTest.Right);
                break;

            case WM_SIZING:
                // Retrieve the "proposed" size of the Form in "rc":
                RECT rc = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));

                // ... do something with "rc" ...

                // this is your code (slightly modified):
                if (itemPanel.Controls.Count > 0)
                {
                    var lastElement = itemPanel.Controls[itemPanel.Controls.Count - 1];

                    if (lastElement.Bottom + lastElement.Margin.Bottom != itemPanel.Height)
                    {
                        int Height = 38 + lastElement.Bottom + lastElement.Margin.Bottom;
                        rc.Bottom = rc.Top + Height; // <--- use "Height" to update the "rc" struct
                    }
                }

                // Put the updated "rc" back into message structure:
                Marshal.StructureToPtr(rc, m.LParam, true);
                break;
        }
    }

}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • 1
    This is hilarious. I decided to give it another try before seeing if I had any answers on here, and the solution I came up with is exactly this! I was just about to answer my own question when I saw you did this, so I'll accept your answer instead. Thanks! – driima Jul 10 '15 at 11:18
-1

Give this a try:

private void ResizeForm()
{
    this.SuspendLayout(); // Suspends the layout logic until ResumeLayout() is called (below)

    if (itemPanel.Controls.Count < 1) return;

    var lastElement = itemPanel.Controls[itemPanel.Controls.Count - 1];

    // The Form is the correct size, no need to resize it:
    if (lastElement.Bottom + lastElement.Margin.Bottom == itemPanel.Height) return;

    Height = 38 + lastElement.Bottom + lastElement.Margin.Bottom;

    this.ResumeLayout(); // ADD THIS AS WELL
}
jaredbaszler
  • 3,941
  • 2
  • 32
  • 40