我遇到了类似的问题,请参阅C# Winforms Tabpage Size and ClientSize wrong:
我创建了一个UserControl
包含TabControl
. s上的内容TabPage
是根据 的宽度排列的TabPage
,但是查询到的 TabPage 的Size
,只要 TabPage 至少没有显示一次就错了ClientSize
。DisplayRectangle.Size
正如您在问题中所写的那样,您已经发现了同样的问题。
我尝试了什么,都没有成功:
- 强制创建每个句柄
TabPage
- 以编程方式选择每个
TabPage
visible = true
在每个设置TabPage
- 呼吁
PerformLayout
每个TabPage
- 其他一些我不再记得的东西
我什至试图找出 .net 在内部做了什么以及如何解决它以强制更新每个 TabPage,但我放弃了,因为它太复杂且耗时。
最终奏效的方法:
我的进一步分析是在我的函数中添加调试打印UpdateLayout
(由 UC 的Layout
事件调用),打印 TabControl 和每个 TabPage 的Size
、ClientSize
和DisplayRectangle.Size
。突然间,我的 UC 表现得如愿以偿。
这些值的查询显然会触发一些更新 TabPages 的 .net 函数。进一步的测试表明,在 UC 的事件中查询就足够TabControl.DisplayRectangle.Size
Layout
了,因此您只需在代码中添加 1 行:
var oSize = i_oTabControl.DisplayRectangle.Size;
并避免它可能被编译器优化掉:
[MethodImpl (MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public static void TabControlForceUpdateOfTabpageSize (this TabControl i_oTabControl)
{
if (i_oTabControl == null)
return;
var oSize = i_oTabControl.DisplayRectangle.Size;
}
我不得不承认它在你的情况下可能不起作用,因为我也看到它在另一个测试项目中失败了。我还在寻找原因。
注意:测试和开发是在 VS 2015 Update 3 中使用 .Net 4.6 完成的。解决方案已确认在调试和发布版本中工作。
编辑:
我花了更多时间来测试我的解决方案。我还再次调试了.net源代码,最后发现那里大致发生了什么。
我的解决方案之所以奏效,是因为多种不同的原因:
- 被
TabControl
放置在一个UserControl
.
- 使用
UserControl
的尺寸与设计时的尺寸不同。这会引发Layout
UC 事件。
- 大小变化发生在 中
InitializeComponent()
,这意味着 UC 还没有父级。
- 分别
TabPage
我没有使用它自己的大小,而是 的大小SelectedTab
,因为我已经发现不可见标签页的大小没有正确更新,并且所有 TabPages 具有相同的大小(AutoScroll = false
)。
- 标签页上的控件以编程方式放置,而不是通过锚定,使用所选标签页的大小(参见 4.)
工作原理如下:
- 目标是强制布局 the
TabControl
和 it's TabPage
s。
- 仅当它实际上可见时才会这样做,请参阅 ScrollableControl.OnVisibleChanged()`。
- 只有 TabPage 或 TabControl 是可见的,如果它本身设置为可见并且所有父级都可见,请参阅
Control.GetVisibleCore()
。如果将控件添加到表单中,则表单在显示时变为可见,因此当表单的 ctor 运行时,控件是不可见的。但是如果控件还没有添加到窗体中,窗体的不可见性就变得无关紧要,因为它不是控件的父级。
现在你会说:“但是我的表单是可见的,所以它应该可以工作。”
嗯,是。在我的情况下,UserControl 被放置在另一个 TabControl 的未选中 TabPage 上,这意味着 UC 的父级不可见。但是当 UC 的大小发生变化时(参见 2. 和 3.),UC 没有父级,并且 UCLayout
事件中的解决方法可能会强制更新TabControl
和 selected TabPage
。如果解决方法甚至出现在the和 all的Layout
事件中,那么所有标签页都有正确的大小值(至少在一些循环之后)。 TabControl
TabPage
解决方法也可以通过一个小技巧(经过测试)为您工作:
从其父级中删除 TabControl,循环浏览所有标签页,然后稍后将其添加回父级。
var oParent = tabctrlU.Parent;
oParent.Controls.Remove (tabctrlU);
var tabStart = tabctrlU.SelectedTab;
foreach (TabPage tab in tabctrlU.TabPages)
tabctrlU.SelectedTab = tab;
tabctrlU.SelectedTab = tabStart;
oParent.Controls.Add (tabctrlU);
这启用了强制更新。
但是:它不适用于将来在不可见选项卡上调整大小。现在,每次调整大小都会触发Layout
事件,因此您可以在那里添加一些代码。实际上我不会每次都删除并重新添加选项卡控件,这将是愚蠢的。所以我不得不承认这个解决方案不太适用于你的情况。但是您(和其他人)现在有了一些如何处理它的想法。
正如您自己已经找到了解决方案:如果您不想将 移动Panel1
到第一个 TabPage,您可以记住Panel1
ctor 及其父 TabPage 的大小和位置,并Panel1
根据(新)以编程方式调整大小选定的 TabPage 移动到该页面后的大小。