2

我刚刚发现 TabPages 在被选中之前不会使用 TabControl 调整大小。这对我来说是一个大问题,因为我有一个固定在所有四个边缘上的面板,并且通过设置它的Parent属性在填充停靠到可调整大小的表单的 TabControl 上的几个页面中的两个页面之间共享。因此,如果我选择一个从不包含面板的选项卡,调整表单大小,然后选择一个包含它但目前不包含的选项卡,则面板不会调整大小。

我发现填充对接面板而不是锚定它可以正常工作,但如果可能的话,我宁愿避免这种情况,因为共享面板不会延伸到页面的边缘并且它的大小可能会有所不同,所以我必须弄乱每个页面的填充大小,而不是简单地改变面板的大小。

我已经在 VB 和 C# 中对此进行了测试,它们的行为方式都相同。我认为为这个已知错误提供的解决方案可能会奏效,但不幸的是它没有。

还有什么我可以做的,比如强制页面调整大小,来解决这个问题吗?

编辑- 在 VS 2010 中重现此内容的步骤:

  • 将 TabControl 添加到可调整大小的表单,填充停靠并添加第三个选项卡。

  • 将面板添加到第三个选项卡,在两侧留出足够的空间,将其锚定到所有四个边缘并将其背景颜色设置为与 tabPage 不同的颜色,以便您可以在运行时看到它。

  • 将这行代码添加到SelectedIndexChanged事件中:

VB

If TabControl1.SelectedIndex > 0 Then Panel1.Parent = TabControl1.SelectedTab  

C#

if (tabControl1.SelectedIndex > 0) panel1.Parent = tabControl1.SelectedTab;
  • 在选择第一个选项卡时运行/调试表单并调整其大小。

  • 选择第二个选项卡——你会看到面板没有调整大小。

4

2 回答 2

1

我遇到了类似的问题,请参阅C# Winforms Tabpage Size and ClientSize wrong
我创建了一个UserControl包含TabControl. s上的内容TabPage是根据 的宽度排列的TabPage,但是查询到的 TabPage 的Size,只要 TabPage 至少没有显示一次就错了ClientSizeDisplayRectangle.Size正如您在问题中所写的那样,您已经发现了同样的问题。

我尝试了什么,都没有成功:

  • 强制创建每个句柄TabPage
  • 以编程方式选择每个TabPage
  • visible = true在每个设置TabPage
  • 呼吁PerformLayout每个TabPage
  • 其他一些我不再记得的东西

我什至试图找出 .net 在内部做了什么以及如何解决它以强制更新每个 TabPage,但我放弃了,因为它太复杂且耗时。

最终奏效的方法:
我的进一步分析是在我的函数中添加调试打印UpdateLayout(由 UC 的Layout事件调用),打印 TabControl 和每个 TabPage 的SizeClientSizeDisplayRectangle.Size。突然间,我的 UC 表现得如愿以偿。
这些值的查询显然会触发一些更新 TabPages 的 .net 函数。进一步的测试表明,在 UC 的事件中查询就足够TabControl.DisplayRectangle.SizeLayout了,因此您只需在代码中添加 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源代码,最后发现那里大致发生了什么。

我的解决方案之所以奏效,是因为多种不同的原因:

  1. TabControl放置在一个UserControl.
  2. 使用UserControl的尺寸与设计时的尺寸不同。这会引发LayoutUC 事件。
  3. 大小变化发生在 中InitializeComponent(),这意味着 UC 还没有父级。
  4. 分别TabPage我没有使用它自己的大小,而是 的大小SelectedTab,因为我已经发现不可见标签页的大小没有正确更新,并且所有 TabPages 具有相同的大小(AutoScroll = false)。
  5. 标签页上的控件以编程方式放置,而不是通过锚定,使用所选标​​签页的大小(参见 4.)

工作原理如下:

  • 目标是强制布局 theTabControl和 it's TabPages。
  • 仅当它实际上可见时才会这样做,请参阅 ScrollableControl.OnVisibleChanged()`。
  • 只有 TabPage 或 TabControl 是可见的,如果它本身设置为可见并且所有父级都可见,请参阅Control.GetVisibleCore()。如果将控件添加到表单中,则表单在显示时变为可见,因此当表单的 ctor 运行时,控件是不可见的。但是如果控件还没有添加到窗体中,窗体的不可见性就变得无关紧要,因为它不是控件的父级。

现在你会说:“但是我的表单可见的,所以它应该可以工作。”
嗯,是。在我的情况下,UserControl 被放置在另一个 TabControl 的未选中 TabPage 上,这意味着 UC 的父级不可见。但是当 UC 的大小发生变化时(参见 2. 和 3.),UC 没有父级,并且 UCLayout事件中的解决方法可能会强制更新TabControl和 selected TabPage。如果解决方法甚至出现在the和 all的Layout事件中,那么所有标签页都有正确的大小值(至少在一些循环之后)。 TabControlTabPage

解决方法也可以通过一个小技巧(经过测试)为您工作:
从其父级中删除 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,您可以记住Panel1ctor 及其父 TabPage 的大小和位置,并Panel1根据(新)以编程方式调整大小选定的 TabPage 移动到该页面后的大小。

于 2019-06-03T12:45:46.657 回答
0

作为一种解决方法,Hans Passant 建议我将面板放在第一个 TabPage(隐藏)上,然后在事件处理程序中设置其 Visible 属性。正如我解释的那样,这不太行得通——因为它不仅仅是启动时的“一次性”问题;每当在选择非面板显示选项卡时调整表单大小时都会发生这种情况,然后选择当前不包含面板的面板显示选项卡 - 但它确实为我指明了正确的方向。也就是根据选中的选项卡来设置面板的Visible属性,不管面板是否显示在上面,总是把面板移动到选中的选项卡上——EG:

Private Sub TabControl1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles TabControl1.SelectedIndexChanged
    Panel1.Visible = (TabControl1.SelectedTab Is TabPage2) OrElse (TabControl1.SelectedTab Is TabPage3)
    Panel1.Parent = TabControl1.SelectedTab
End Sub
于 2012-07-02T13:23:12.253 回答