#c# #winforms #tabcontrol
#c# #winforms #tabcontrol
Вопрос:
Кажется, что элементы управления Handles
, которые еще не созданы, перемещаются в нижнюю часть Parent
Control
при TabControl
Padding
изменении свойства ‘s. Просматривая .NET
исходный код, Padding
делает вызов RecreateHandle();
, который, по-видимому, имеет к нему какое-то отношение.
Ниже приведен код, который иллюстрирует проблему. У каждого checkbox
есть соответствующий label
чуть ниже. Некоторые метки видны в начале, некоторые скрыты. person0
При установке флажка person0
метка отображается, но неправильно внизу. Вместо этого метка person0 должна отображаться прямо под флажком person0, в том порядке, в котором она была добавлена FlowLayoutPanel
. Смотрите скриншот.
В коде предусмотрена bool doWorkaround
опция, но должен быть лучший способ. Принудительное использование всего, Handles
что нужно создать CreateControl()
, также кажется неправильным.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication3 {
public class TabControl3 : TabControl {
private bool doWorkaround = false; // set to true to prevent the bug
protected override void OnFontChanged(EventArgs e) {
base.OnFontChanged(e);
int h = (int) this.Font.Height;
SetPadding(new Point(h, h)); // calling this causes the Controls to re-order incorrectly
}
///<summary>Setting the padding causes the TabControl to recreate all the handles, which causes the hidden handleless controls to rearrange.</summary>
public virtual void SetPadding(Point pt) {
// Workaround solution: remove all controls from tab pages and then add them back after.
int n = TabPages.Count;
Control[][] arr = null;
if (doWorkaround) {
arr = new Control[n][];
for (int i = 0; i < n; i ) {
TabPage tp = TabPages[i];
arr[i] = tp.Controls.Cast<Control>().ToArray();
tp.Controls.Clear();
}
}
this.Padding = pt; // in the .NET source code, setting Padding calls RecreateHandle()
if (doWorkaround) {
for (int i = 0; i < n; i )
TabPages[i].Controls.AddRange(arr[i]);
}
}
}
public class CheckBox2 : CheckBox {
public CheckBox2(String text, bool isChecked = false) : base() {
this.Text = text;
this.AutoSize = true;
this.Checked = isChecked;
}
}
public class Label2 : Label {
public Label2(String text) : base() {
this.Text = text;
this.AutoSize = true;
}
}
public class MyForm : Form {
TabControl tc = new TabControl3 { Dock = DockStyle.Fill };
public MyForm() {
this.Size = new System.Drawing.Size(600, 800);
this.StartPosition = FormStartPosition.CenterScreen;
TabPage tp1 = new TabPage("Page1");
FlowLayoutPanel p = new FlowLayoutPanel { FlowDirection = System.Windows.Forms.FlowDirection.TopDown, Dock = DockStyle.Fill };
p.Controls.Add(new CheckBox2("Person0"));
p.Controls.Add(new Label2("Person0") { Visible = false });
p.Controls.Add(new CheckBox2("Person1", true));
p.Controls.Add(new Label2("Person1"));
p.Controls.Add(new CheckBox2("Person2", true));
p.Controls.Add(new Label2("Person2"));
p.Controls.Add(new CheckBox2("Person3"));
p.Controls.Add(new Label2("Person3") { Visible = false });
p.Controls.Add(new CheckBox2("Person4"));
p.Controls.Add(new Label2("Person4") { Visible = false });
for (int i = 0; i < p.Controls.Count; i = 2) {
CheckBox cb = (CheckBox) p.Controls[i];
Label lb = (Label) p.Controls[i 1];
cb.CheckedChanged = delegate {
bool b = cb.Checked;
lb.Visible = b;
};
}
tp1.Controls.Add(p);
tc.TabPages.Add(tp1);
Controls.Add(tc);
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
this.Font = SystemFonts.MenuFont; // to trigger the bug
}
}
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyForm());
}
}
}
Я протестировал использование .NET 3.5, 4, 4.52
и увидел одинаковое поведение во всех трех.
Комментарии:
1. Переопределите
OnControlAdded
ваш TabControl и добавьтеbase.OnControlAdded(e); if (e.Control is TabPage) { e.Control.Controls.OfType<Control>().ToList().ForEach(c => c.CreateControl()); }
. Также добавьте, вOnFontChanged
(вы также можете использоватьOnLayout
), послеbase()
,if (!this.IsHandleCreated) return;
. Удалить всеSetPadding()
, кромеthis.Padding = pt;
2. В OnFontChanged, вероятно, лучше, если у вас есть:
var p = new Point(Font.Height, Font.Height); if (!Padding.Equals(p)) SetPadding(p);
3. Кстати, вы могли бы использовать TableLayoutPanel вместо FlowLayoutPanel 🙂 (или один UserControl вместо двух элементов управления)