UserControl not rendering within FlowLayoutPanel when dock changed
Asked Answered
C

2

7

When I add my UserControls to a FlowLayoutPanel, they display properly. When I change the Dock or Anchor properties on the UserControls before adding them, they are still added but do not render.

According to "How to: Anchor and Dock Child Controls" this should be possible.

  • I can tell that the controls are added (despite not drawing) because adding enough of them causes a vertical scrollbar to appear.
  • Setting the "Dock" property of the UserControls to "Left" or "None" will cause them to render, but none of the other options.
  • Setting the "Anchor" property on the UserControls to anything but Top | Left does not render.
  • Setting the dock before or after adding the control makes no difference (Add, Dock vs. Dock, Add).
  • The FlowLayoutPanel is itself is docked (Fill), has FlowDirection set to TopDown, has WrapContents set to false, has AutoScroll set to true, and is otherwise default.

I am using .NET 3.5.


In answer to a comment, the two commented lines are the locations I tried to change the dock. The second spot definitely makes more sense, but I tried the other because it couldn't hurt.

public void CreateObjectControl( object o )
{
    ObjectControl oc = new ObjectControl();

    oc.MyObject = o;

    //This was a spot I mentioned:
    //oc.Dock = DockStyle.Fill;

    ObjectDictionary.Add( o, oc );
    flowLayoutPanel1.Controls.Add( oc );

    //This is the other spot I mentioned:
    oc.Dock = DockStyle.Fill;
}
Conceive answered 15/9, 2011 at 19:51 Comment(10)
The Dock of what you are changing ?Garaway
@Tigran: I edited the post for clarification. I am changing the dock on the UserControls that I am adding to the FlowLayoutPanel.Conceive
How can you set dock/fill on the user controls before adding them?Implode
Are you adding them at Design mode or at runtime (through code)?Royster
@Hans Passant: Updated with code snippet to answer your comment.Conceive
@Arun: I am adding them during runtime. I edited in the code.Conceive
In the MSDN example in the link, the FlowLayoutPanel is set to fixed width. You said you have yours as "DockStyle.Fill". Would you want to change it?Royster
Are you sure the controls "won't render" OR only the content isn't visible. I mean are you certain that your UserControl beheaves correctly when being resized? Sometimes Controls in your UserControl move to some "hidden" place during resize.Ko
I think the Size of the UserControl is somehow becoming 0,0 when Dock is set to DockStyle.Fill. I think this may be due to the UserControl having no intrinsic size. I am quite confused here myself.Prebo
It has nothing to do with your UserControl. It also happens if you use a standard Panel control, and the behaviour is still there with .net461 in VS2015.Scholium
V
0

try using SuspendLayout and Resumelayout function for the controls before making any amendments which need rendering for proper viewing.

You could see the code from Designer.cs for that particular control

Syntax

control.SuspendLayout();
{Your code for designer amendments}
control.resumeaLayout();
Veinstone answered 28/11, 2014 at 7:23 Comment(0)
S
0

I think I may have found a workaround (read: dirty trick) ... this answer helped to point me in the right direction. Here's an excerpt from the MS article that you also linked to:

For vertical flow directions, the FlowLayoutPanel control calculates the width of an implied column from the widest child control in the column. All other controls in this column with Anchor or Dock properties are aligned or stretched to fit this implied column.

The behavior works in a similar way for horizontal flow directions. The FlowLayoutPanel control calculates the height of an implied row from the tallest child control in the row, and all docked or anchored child controls in this row are aligned or sized to fit the implied row.

This page does not specifically mention that you can't Dock/Anchor the tallest/widest control. But as this control defines the layout behaviour of the FlowLayoutPanel, and thus influences the way all other sibling controls are displayed, it is well possible that Dock and Anchor don't work properly for that 'master control'. Even though I can't find any official documentation regarding that, I believe it to be the case.

So, which options do we have? At runtime, we could add a panel control of height 0 and width of the FlowLayoutPanel client area before you add your usercontrol. You can even set that panel's visibility to false. Subscribing to some Resize/Layout events of the FlowLayoutPanel to keep that panel's size will to the trick. But this does not play nicely at design time. The events won't fire and thus you can't really design the surface the way you want it to look.

I'd prefer a solution that "just works" at design time as well. So, here's an attempt at an "invisible" control that I put together, to fix the controls resizing to zero width if no other control is present. Dropping this as first control onto the FlowLayoutPanel at design time seems to provide the desired effect, and any control subsequently placed on the FlowLayoutPanel is anchorable to the right without shrinking to zero width. The only problem is that, once this invisible control is there, it seems I can't remove it anymore via the IDE. It probably needs some special treatment using a ControlDesigner to achieve that. It can still be removed in the form's designer code though.

This control, once placed onto the FlowLayoutPanel, will listen for resize events of it's parent control, and resize itself according to the ClientSize of the parent control. Use with caution, as this may contain pitfalls that didn't occur to me during the few hours I played with this. For example, I didn't try placing controls that were wider than the FlowLayoutPanel's client area.

As a side note, what will still fail is trying to anchor to the bottom, but that wasn't part of the question ;-)

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;

namespace ControlTest
{
  public sealed class InvisibleControl : Control
  {
    public InvisibleControl()
    {
      TabStop = false;
    }

    #region public interface

    // Reduce the temptation ...
    public new AnchorStyles Anchor
    {
      get { return base.Anchor; }
      set { base.Anchor = AnchorStyles.None; }
    }
    public new DockStyle Dock
    {
      get { return base.Dock; }
      set { base.Dock = DockStyle.None; }
    }

    // We don't ever want to move away from (0,0)
    public new Point Location
    {
      get { return base.Location; }
      set { base.Location = Point.Empty; }
    }

    // Horizontal or vertical orientation?
    private Orientation _orientation = Orientation.Horizontal;
    [DefaultValue(typeof(Orientation), "Horizontal")]
    public Orientation Orientation
    {
      get { return _orientation; }
      set
      {
        if (_orientation == value) return;
        _orientation = value;
        ChangeSize();
      }
    }

    #endregion

    #region overrides of default behaviour

    // We don't want any margin around us
    protected override Padding DefaultMargin => Padding.Empty;

    // Clean up parent references
    protected override void Dispose(bool disposing)
    {
      if (disposing)
        SetParent(null);
      base.Dispose(disposing);
    }

    // This seems to be needed for IDE support, as OnParentChanged does not seem
    // to fire if the control is dropped onto a surface for the first time
    protected override void OnHandleCreated(EventArgs e)
    {
      base.OnHandleCreated(e);
      ChangeSize();
    }

    // Make sure we don't inadvertantly paint anything
    protected override void OnPaint(PaintEventArgs e) { }
    protected override void OnPaintBackground(PaintEventArgs pevent) { }

    // If the parent changes, we need to:
    // A) Unsubscribe from the previous parent's Resize event, if applicable
    // B) Subscribe to the new parent's Resize event
    // C) Resize our control according to the new parent dimensions
    protected override void OnParentChanged(EventArgs e)
    {
      base.OnParentChanged(e);
      // Perform A+B
      SetParent(Parent);
      // Perform C
      ChangeSize();
    }

    // We don't really want to be resized, so deal with it
    protected override void OnResize(EventArgs e)
    {
      base.OnResize(e);
      ChangeSize();
    }

    #endregion

    #region private stuff

    // Make this a default handler signature with optional params, so that this can
    // directly subscribe to the parent resize event, but also be called without parameters
    private void ChangeSize(object sender = null, EventArgs e = null)
    {
      Rectangle client = Parent?.ClientRectangle ?? new Rectangle(0, 0, 10, 10);
      Size proposedSize = _orientation == Orientation.Horizontal
        ? new Size(client.Width, 0)
        : new Size(0, client.Height);
      if (!Size.Equals(proposedSize)) Size = proposedSize;
    }

    // Handles reparenting
    private Control boundParent;
    private void SetParent(Control parent)
    {
      if (boundParent != null)
        boundParent.Resize -= ChangeSize;
      boundParent = parent;
      if (boundParent != null)
        boundParent.Resize += ChangeSize;
    }

    #endregion
  }
}
Scholium answered 28/4, 2016 at 14:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.