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

Posted

技术标签:

【中文标题】停靠更改时,UserControl 未在 FlowLayoutPanel 中呈现【英文标题】:UserControl not rendering within FlowLayoutPanel when dock changed 【发布时间】:2011-11-18 04:45:27 【问题描述】:

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

根据“How to: Anchor and Dock Child Controls”,这应该是可能的。

我可以看出添加了控件(尽管没有绘制),因为添加足够多的控件会导致出现垂直滚动条。 将 UserControls 的“Dock”属性设置为“Left”或“None”将导致它们呈现,但不会呈现其他选项。 将 UserControls 上的“Anchor”属性设置为除 Top |左侧不渲染。 在添加控件之前或之后设置停靠没有区别(添加、停靠与停靠、添加)。 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;

【问题讨论】:

你正在改变什么的码头? @Tigran:我编辑了帖子以进行澄清。我正在更改要添加到 FlowLayoutPanel 的 UserControls 上的停靠栏。 如何在用户控件添加之前设置停靠/填充? 您是在设计模式还是在运行时(通过代码)添加它们? @Hans Passant:更新了代码 sn-p 以回答您的评论。 【参考方案1】:

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

您可以从 Designer.cs 中查看该特定控件的代码

语法

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

【讨论】:

【参考方案2】:

我想我可能找到了一种解决方法(阅读:肮脏的把戏)...this answer 帮助我指出了正确的方向。以下是您还链接到的MS article 的摘录:

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

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

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

那么,我们有哪些选择?在运行时,我们可以在添加用户控件之前添加一个高度为 0 和宽度为 FlowLayoutPanel 客户区的面板控件。您甚至可以将该面板的可见性设置为 false。订阅 FlowLayoutPanel 的一些 Resize/Layout 事件以保持该面板的大小将达到目的。但这在设计时并不能很好地发挥作用。事件不会触发,因此您无法真正按照您希望的方式设计表面。

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

这个控件,一旦放到 FlowLayoutPanel 上,就会监听它的父控件的 resize 事件,并根据父控件的 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
  

【讨论】:

以上是关于停靠更改时,UserControl 未在 FlowLayoutPanel 中呈现的主要内容,如果未能解决你的问题,请参考以下文章

使用 UserControl 创建具有设计时支持的 ToolBox 组件

使用自定义 WinForms 控件,我可以更改嵌套控件停靠在里面的矩形吗?

UserControl在wpf中使用父元素?

标签页 Windows 窗体内的用户控件停靠栏

DockManager如何停靠 z

以编程方式更改停靠标题