我想我可能已经找到了一种解决方法(阅读:肮脏的把戏)......这个答案帮助我指出了正确的方向。这是您还链接到的MS 文章的摘录:
对于垂直流向,FlowLayoutPanel 控件根据列中最宽的子控件计算隐含列的宽度。此列中具有 Anchor 或 Dock 属性的所有其他控件都对齐或拉伸以适合此隐含列。
对于水平流向,该行为以类似的方式起作用。FlowLayoutPanel 控件从该行中最高的子控件计算隐含行的高度,并且该行中所有停靠或锚定的子控件对齐或调整大小以适合隐含行。
这个页面没有特别提到你不能停靠/锚定最高/最宽的控件。但是,由于该控件定义了 FlowLayoutPanel 的布局行为,因此会影响所有其他同级控件的显示方式,因此 Dock 和 Anchor 很可能无法针对该“主控件”正常工作。即使我找不到任何有关此的官方文档,但我相信确实如此。
那么,我们有哪些选择?在运行时,我们可以在添加用户控件之前添加一个高度为 0 和宽度为 FlowLayoutPanel 客户区的面板控件。您甚至可以将该面板的可见性设置为 false。订阅 FlowLayoutPanel 的一些 Resize/Layout 事件以保持该面板的大小将达到目的。但这在设计时并不能很好地发挥作用。事件不会触发,因此您无法真正按照您希望的方式设计表面。
我更喜欢在设计时也能“正常工作”的解决方案。因此,这是我放在一起的“不可见”控件的尝试,如果不存在其他控件,则将控件的大小调整为零宽度。在设计时将其作为第一个控件放到 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
}
}