C# Winforms Tabpage Size 和 ClientSize 错误
C# Winforms Tabpage Size and ClientSize wrong
我创建了一个用户控件,其中包含一个带有 2 个 TabPage 的 TabControl。在用户控件的 Layout
事件中以编程方式调整两个 TabPage 上子控件的位置。
问题是,调用事件时,第二个 TabPage 尚未绘制,因此此 TabPage 的 Size 和 ClientSize 错误。
我该如何解决这个问题?
我已经尝试使用 var oHandle = tabpage.Handle
和 tabctrl.SelectedTab = each tabpage
的循环来强制创建 TabPage,但这没有帮助。
编辑#1
我发现第一个 "bug" 在 VS Designer 中观察到:
当您在表单上拖动 TabControl
然后在设计器中调整它的大小时,当前可见的 TabPage
的大小会更新。但是所有其他标签页的大小都不是;它们保持不变,直到对任何控件进行任何其他更改(已测试!)。
我承认这种情况很少见,所以尺寸通常会更新,但我认为这是 TabControl
中的设计缺陷。
当 TabControl 在运行时调整大小时,这个设计缺陷变得非常重要!这是 repro 的最小示例(没有 UC,只是表单中的 TabControl):
Form1.cs:
public Form1 ()
{
InitializeComponent ();
Debug.Print ("ctor before resize");
Debug.Print ("TC: " + tabControl1.Size);
Debug.Print ("T1: " + tabPage1.Size);
Debug.Print ("T2: " + tabPage2.Size);
tabControl1.Size = tabControl1.Size + new Size (10, 10);
Debug.Print ("ctor after resize");
Debug.Print ("TC: " + tabControl1.Size);
Debug.Print ("T1: " + tabPage1.Size);
Debug.Print ("T2: " + tabPage2.Size);
}
private void Form1_Load (object sender, EventArgs e)
{
... same as ctor, prints adapted ("load before/after resize)
}
private void Form1_Layout (object sender, LayoutEventArgs e)
{
Debug.Print ("Layout");
}
private void button1_Click (object sender, EventArgs e)
{
... same as ctor, prints adapted ("button before/after resize)
}
Form1.Designer.cs:(删除无关部分)
private void InitializeComponent ()
{
this.tabControl1 = new System.Windows.Forms.TabControl ();
this.tabPage1 = new System.Windows.Forms.TabPage ();
this.tabPage2 = new System.Windows.Forms.TabPage ();
this.button1 = new System.Windows.Forms.Button ();
this.tabControl1.SuspendLayout ();
this.SuspendLayout ();
//
this.tabControl1.Controls.Add (this.tabPage1);
this.tabControl1.Controls.Add (this.tabPage2);
this.tabControl1.Size = new System.Drawing.Size (300, 120);
//
this.tabPage1.Size = new System.Drawing.Size (292, 91);
//
this.tabPage2.Size = new System.Drawing.Size (292, 91);
//
this.button1.Click += new System.EventHandler (this.button1_Click);
//
this.AutoScaleDimensions = new System.Drawing.SizeF (96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size (384, 262);
this.Controls.Add (this.tabControl1);
this.Controls.Add (this.button1);
this.Load += new System.EventHandler (this.Form1_Load);
this.tabControl1.ResumeLayout (false);
this.ResumeLayout (false);
}
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.TabPage tabPage2;
private System.Windows.Forms.Button button1;
调试打印:
ctor before resize
TC: {Width=300, Height=120}
T1: {Width=292, Height=91}
T2: {Width=292, Height=91}
Layout
Layout
ctor after resize
TC: {Width=310, Height=130}
T1: {Width=292, Height=91} (wrong)
T2: {Width=292, Height=91} (wrong)
Load before resize
TC: {Width=310, Height=130}
T1: {Width=302, Height=101} (now correct because updated after ctor)
T2: {Width=302, Height=101} (now correct because updated after ctor)
Layout
Layout
Load after resize
TC: {Width=320, Height=140}
T1: {Width=312, Height=111} (correct because visible)
T2: {Width=302, Height=101} (wrong again)
Layout
(TabPage1 selected, TabPage2 is not updated)
button before resize
TC: {Width=320, Height=140}
T1: {Width=312, Height=111}
T2: {Width=302, Height=101} (still wrong: TabPage2 HAS NOT BEEN UPDATED WHILE THE UI-THREAD WAS IDLE)
Layout
Layout
button after resize
TC: {Width=330, Height=150}
T1: {Width=322, Height=121}
T2: {Width=302, Height=101} (even more wrong)
(TabPage1 selected, TabPage2 is not updated)
button before resize
TC: {Width=330, Height=150}
T1: {Width=322, Height=121}
T2: {Width=302, Height=101} (still wrong)
Layout
Layout
button after resize
TC: {Width=340, Height=160}
T1: {Width=332, Height=131}
T2: {Width=302, Height=101} (again more wrong)
(TabPage2 selected, now TabPage1 is not updated)
button before resize
TC: {Width=340, Height=160}
T1: {Width=332, Height=131}
T2: {Width=332, Height=131} (now correct because visible)
Layout
Layout
button after resize
TC: {Width=350, Height=170}
T1: {Width=332, Height=131} (now wrong)
T2: {Width=342, Height=141} (still correct because visible)
基于这种行为,我目前唯一的解决方案是在每次调用 tabControl1_SelectedIndexChanged (..)
.
时调用我的 UC 的 UpdateLayout()
函数
编辑 #2
编辑 #1 的 "solution" 不起作用,因为:
如果 TabPage 的宽度很小,则页面上的控件垂直排列,导致相应 TabPage 的高度较大。 UC的整体高度取决于TabControl的高度,TabControl的高度取决于所有TabPages,所以UpdateLayout()
需要所有TabPages的尺寸正确,否则稍后选择另一个标签时UC高度会再次变化, 但它在设计时应该已经正确了。
我找到了一个非常简单的解决方案来获得正确的 TabPages 大小:
所有 TabPages 都属于同一个 TabControl,因此所有 TabPages 具有相同的大小,无论相应 TabPage 的 Size
属性 是什么。
此外,总有一个报告正确大小的 TabPage,即当前选定的 TabPage。
因此每个 TabPage 的大小是 TabControl.SelectedTab.Size
.
编辑:
不幸的是,如果整个 TabControl
不可见,这将不起作用,可能是因为它被放置在当前未被选中的另一个 TabControl
的 TabPage
上。
但是,我找到了另一个解决方案,我将其发布为 answer on this question Can I force a TabControl's TabPages to resize *before* they're selected?,这实际上或多或少是我的重复问题。
该答案的相关部分:
[...]
You have to add just 1 line to your code:
var oSize = i_oTabControl.DisplayRectangle.Size;
and avoid that it's probably optimized away by the compiler:
[MethodImpl (MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public static void TabControlForceUpdateOfTabpageSize (this TabControl i_oTabControl)
{
if (i_oTabControl == null)
return;
var oSize = i_oTabControl.DisplayRectangle.Size;
}
[...]
我创建了一个用户控件,其中包含一个带有 2 个 TabPage 的 TabControl。在用户控件的 Layout
事件中以编程方式调整两个 TabPage 上子控件的位置。
问题是,调用事件时,第二个 TabPage 尚未绘制,因此此 TabPage 的 Size 和 ClientSize 错误。
我该如何解决这个问题?
我已经尝试使用 var oHandle = tabpage.Handle
和 tabctrl.SelectedTab = each tabpage
的循环来强制创建 TabPage,但这没有帮助。
编辑#1
我发现第一个 "bug" 在 VS Designer 中观察到:
当您在表单上拖动 TabControl
然后在设计器中调整它的大小时,当前可见的 TabPage
的大小会更新。但是所有其他标签页的大小都不是;它们保持不变,直到对任何控件进行任何其他更改(已测试!)。
我承认这种情况很少见,所以尺寸通常会更新,但我认为这是 TabControl
中的设计缺陷。
当 TabControl 在运行时调整大小时,这个设计缺陷变得非常重要!这是 repro 的最小示例(没有 UC,只是表单中的 TabControl):
Form1.cs:
public Form1 ()
{
InitializeComponent ();
Debug.Print ("ctor before resize");
Debug.Print ("TC: " + tabControl1.Size);
Debug.Print ("T1: " + tabPage1.Size);
Debug.Print ("T2: " + tabPage2.Size);
tabControl1.Size = tabControl1.Size + new Size (10, 10);
Debug.Print ("ctor after resize");
Debug.Print ("TC: " + tabControl1.Size);
Debug.Print ("T1: " + tabPage1.Size);
Debug.Print ("T2: " + tabPage2.Size);
}
private void Form1_Load (object sender, EventArgs e)
{
... same as ctor, prints adapted ("load before/after resize)
}
private void Form1_Layout (object sender, LayoutEventArgs e)
{
Debug.Print ("Layout");
}
private void button1_Click (object sender, EventArgs e)
{
... same as ctor, prints adapted ("button before/after resize)
}
Form1.Designer.cs:(删除无关部分)
private void InitializeComponent ()
{
this.tabControl1 = new System.Windows.Forms.TabControl ();
this.tabPage1 = new System.Windows.Forms.TabPage ();
this.tabPage2 = new System.Windows.Forms.TabPage ();
this.button1 = new System.Windows.Forms.Button ();
this.tabControl1.SuspendLayout ();
this.SuspendLayout ();
//
this.tabControl1.Controls.Add (this.tabPage1);
this.tabControl1.Controls.Add (this.tabPage2);
this.tabControl1.Size = new System.Drawing.Size (300, 120);
//
this.tabPage1.Size = new System.Drawing.Size (292, 91);
//
this.tabPage2.Size = new System.Drawing.Size (292, 91);
//
this.button1.Click += new System.EventHandler (this.button1_Click);
//
this.AutoScaleDimensions = new System.Drawing.SizeF (96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size (384, 262);
this.Controls.Add (this.tabControl1);
this.Controls.Add (this.button1);
this.Load += new System.EventHandler (this.Form1_Load);
this.tabControl1.ResumeLayout (false);
this.ResumeLayout (false);
}
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.TabPage tabPage2;
private System.Windows.Forms.Button button1;
调试打印:
ctor before resize
TC: {Width=300, Height=120}
T1: {Width=292, Height=91}
T2: {Width=292, Height=91}
Layout
Layout
ctor after resize
TC: {Width=310, Height=130}
T1: {Width=292, Height=91} (wrong)
T2: {Width=292, Height=91} (wrong)
Load before resize
TC: {Width=310, Height=130}
T1: {Width=302, Height=101} (now correct because updated after ctor)
T2: {Width=302, Height=101} (now correct because updated after ctor)
Layout
Layout
Load after resize
TC: {Width=320, Height=140}
T1: {Width=312, Height=111} (correct because visible)
T2: {Width=302, Height=101} (wrong again)
Layout
(TabPage1 selected, TabPage2 is not updated)
button before resize
TC: {Width=320, Height=140}
T1: {Width=312, Height=111}
T2: {Width=302, Height=101} (still wrong: TabPage2 HAS NOT BEEN UPDATED WHILE THE UI-THREAD WAS IDLE)
Layout
Layout
button after resize
TC: {Width=330, Height=150}
T1: {Width=322, Height=121}
T2: {Width=302, Height=101} (even more wrong)
(TabPage1 selected, TabPage2 is not updated)
button before resize
TC: {Width=330, Height=150}
T1: {Width=322, Height=121}
T2: {Width=302, Height=101} (still wrong)
Layout
Layout
button after resize
TC: {Width=340, Height=160}
T1: {Width=332, Height=131}
T2: {Width=302, Height=101} (again more wrong)
(TabPage2 selected, now TabPage1 is not updated)
button before resize
TC: {Width=340, Height=160}
T1: {Width=332, Height=131}
T2: {Width=332, Height=131} (now correct because visible)
Layout
Layout
button after resize
TC: {Width=350, Height=170}
T1: {Width=332, Height=131} (now wrong)
T2: {Width=342, Height=141} (still correct because visible)
基于这种行为,我目前唯一的解决方案是在每次调用 tabControl1_SelectedIndexChanged (..)
.
UpdateLayout()
函数
编辑 #2
编辑 #1 的 "solution" 不起作用,因为:
如果 TabPage 的宽度很小,则页面上的控件垂直排列,导致相应 TabPage 的高度较大。 UC的整体高度取决于TabControl的高度,TabControl的高度取决于所有TabPages,所以UpdateLayout()
需要所有TabPages的尺寸正确,否则稍后选择另一个标签时UC高度会再次变化, 但它在设计时应该已经正确了。
我找到了一个非常简单的解决方案来获得正确的 TabPages 大小:
所有 TabPages 都属于同一个 TabControl,因此所有 TabPages 具有相同的大小,无论相应 TabPage 的 Size
属性 是什么。
此外,总有一个报告正确大小的 TabPage,即当前选定的 TabPage。
因此每个 TabPage 的大小是 TabControl.SelectedTab.Size
.
编辑:
不幸的是,如果整个 TabControl
不可见,这将不起作用,可能是因为它被放置在当前未被选中的另一个 TabControl
的 TabPage
上。
但是,我找到了另一个解决方案,我将其发布为 answer on this question Can I force a TabControl's TabPages to resize *before* they're selected?,这实际上或多或少是我的重复问题。
该答案的相关部分:
[...]
You have to add just 1 line to your code:var oSize = i_oTabControl.DisplayRectangle.Size;
and avoid that it's probably optimized away by the compiler:
[MethodImpl (MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public static void TabControlForceUpdateOfTabpageSize (this TabControl i_oTabControl) { if (i_oTabControl == null) return; var oSize = i_oTabControl.DisplayRectangle.Size; }
[...]