当停靠更改时,UserControl 未在 FlowLayoutPanel 内呈现

发布于 2024-12-04 23:49:08 字数 1028 浏览 3 评论 0原文

当我将 UserControls 添加到 FlowLayoutPanel 时,它们会正确显示。当我在添加用户控件之前更改 Dock 或 Anchor 属性时,它们仍然会被添加,但不会呈现。

根据“如何:锚定和停靠子控件”,这应该是可能的。

  • 我可以看出控件已添加(尽管未绘制),因为添加足够的控件会导致出现垂直滚动条。
  • 将 UserControls 的“Dock”属性设置为“Left”或“None”将导致它们渲染,但其他选项都不会渲染。
  • 将 UserControls 上的“Anchor”属性设置为除 Top | 之外的任何内容左侧不渲染。
  • 在添加控件之前或之后设置 Dock 没有什么区别(添加、Dock 与 Dock、Add)。
  • FlowLayoutPanel 本身已停靠(填充),FlowDirection 设置为 TopDown,WrapContents 设置为 false,AutoScroll 设置为 true,否则为默认值。

我正在使用.NET 3.5。


在回答评论时,两条注释行是我尝试更改码头的位置。第二个位置肯定更有意义,但我尝试了另一个,因为它不会造成伤害。

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;
}

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

赠我空喜 2024-12-11 23:49:08

在进行任何需要渲染才能正确查看的修改之前,请尝试对控件使用 SuspendLayout 和 Resumelayout 函数。

的代码

您可以从 Designer.cs 中看到该特定控件

control.SuspendLayout();
{Your code for designer amendments}
control.resumeaLayout();

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();
葬心 2024-12-11 23:49:08

我想我可能已经找到了解决方法(阅读:肮脏的伎俩)... 这个答案帮助我指出了正确的方向方向。以下是您还链接到的 MS 文章的摘录:

对于垂直流动方向,FlowLayoutPanel 控件根据列中最宽的子控件计算隐含列的宽度。此列中具有 Anchor 或 Dock 属性的所有其他控件都会对齐或拉伸以适合此隐含列。

对于水平流向,该行为的工作方式类似。 FlowLayoutPanel 控件从行中最高的子控件计算隐含行的高度,并且该行中所有停靠或锚定的子控件都会对齐或调整大小以适合隐含行。

此页面没有特别提到您无法停靠/锚定最高/最宽的控件。但由于此控件定义了 FlowLayoutPanel 的布局行为,从而影响所有其他同级控件的显示方式,因此 Dock 和 Anchor 很可能无法对该“主控件”正常工作。尽管我找不到任何相关的官方文件,但我相信情况确实如此。

那么,我们有哪些选择呢?在运行时,我们可以在添加用户控件之前添加高度为 0 且 FlowLayoutPanel 客户区宽度为 0 的面板控件。您甚至可以将该面板的可见性设置为 false。订阅 FlowLayoutPanel 的某些调整大小/布局事件以保持该面板的大小将达到目的。但这在设计时效果不佳。事件不会触发,因此您无法真正按照您想要的外观设计表面。

我更喜欢一个在设计时也“正常工作”的解决方案。因此,这里尝试将“不可见”控件放在一起,以便在不存在其他控件的情况下将控件大小调整为零宽度。在设计时将其作为第一个控件放到 FlowLayoutPanel 上似乎可以提供所需的效果,并且随后放置在 FlowLayoutPanel 上的任何控件都可以锚定到右侧,而不会缩小到零宽度。唯一的问题是,一旦这个不可见的控件存在,我似乎就无法再通过 IDE 删除它了。它可能需要使用 ControlDesigner 进行一些特殊处理才能实现这一点。不过,它仍然可以在表单的设计器代码中删除。

该控件一旦放置到 FlowLayoutPanel 上,就会侦听其父控件的调整大小事件,并根据父控件的 ClientSize 调整自身大小。请谨慎使用,因为这可能包含我在玩这个的几个小时内没有遇到的陷阱。例如,我没有尝试放置比 FlowLayoutPanel 的客户区域更宽的控件。

作为旁注,仍然会失败的是尝试锚定到底部,但这不是问题的一部分;-)

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
  }
}

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
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文