WinForm - 复制具有自己行为的 TabControl
WinForm - Copy TabControl with OWN behaviour
Copy TabControl Tab 是的,我已经检查过了。
我正在尝试复制一个 tabcontol,但此选项卡应该有它自己的 'behaviour'。
TabControl tc = TC_Fields;
TabPage tpOld = tc.TabPages[0];
TabPage tpNew = new TabPage();
fields += 1;
tpNew.Name = "Field_" + fields;
tpNew.Text = "Field-" + fields;
foreach (Control c in tpOld.Controls)
{
Control cNew = (Control) Activator.CreateInstance(c.GetType());
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(c);
foreach (PropertyDescriptor entry in pdc)
{
object val = entry.GetValue(c);
if (entry.Name == "Name")
{
val = (String) val + fields;
}
entry.SetValue(cNew, val);
}
tpNew.Controls.Add(cNew);
}
tc.TabPages.Add(tpNew);
我试过上面的代码,所以它给新控件一个新的 "id",但我仍然无法单击新选项卡中的控件,它们只模仿第一个选项卡中的内容.
有没有一种方法可以复制所有控件而不模仿它们被复制的控件?
您的代码至少有两个与实际问题无关的问题。让我们先修复其中两个:
您设置了所有属性,甚至是您不应该设置的属性;最值得注意的是你不应该触摸 "WindowTarget"
属性!它仅供内部使用,弄乱它至少会停止某些控件的工作。例如,我发现我无法检查 CheckBox
。
如果原始 TabPage
也是 current 控件,您的代码只会创建 visible 控件.所有其他页面上的所有控件都是不可见的,如果您尝试克隆任何其他 TabPage
所有控件将被添加得很好但保持 invisible.
让我们添加一个过滤器:
if(entry.Name != "WindowTarget") entry.SetValue(cNew, val);
并使新控件可见:
cNew.Visible = true;
tpNew.Controls.Add(cNew);
请注意,这是一个简单的解决方案,因为它还会使那些原本不可见的控件可见。您可以改为显示要克隆的页面或制作不可见控件的列表;用他们的名字你可以找到克隆的对应物..
最后一个问题也很重要,但超出了问题范围:代码仅直接在页面上克隆控件,而不是任何嵌套控件(如 GroupBox
中的 RadioButtons
!)。为此,您必须编写 递归 版本!
- 现在是真正的问题:如何为克隆的控件提供它们自己的行为?
克隆所有属性后,它们就像新添加的控件一样,即它们根本没有事件处理程序。
所以你需要
- 为新控件的事件编写新的事件处理程序
- 联系他们
虽然这并不难,但问题很多..
我们知道命名方案,因此我们可以知道要为哪些控件提供事件。事件名称可以自由选择,但要将它们连接起来,我们需要知道我们正在处理的是哪个新控件..
这是将 Button cb_hello
的 第一个 克隆与 Click
事件挂钩的示例:
if (cNew.Name == "cb_hello1") cNew.Click += buttonNew_Click;
活动中的代码演示了更多问题:
private void buttonNew_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
Console.WriteLine(btn.Name + " says hiho");
btn.Parent.Controls["panel41"].BackColor = Color.ForestGreen;
((RadioButton)btn.Parent.Controls["radioButton11"]).Checked = true;
}
虽然将 sender
转换为 Button
很正常,但是当我们尝试访问我们刚刚克隆的任何控件时,我们 运行 遇到了麻烦:
由于它们是动态创建的,我们无法使用变量访问它们中的任何一个。相反,我们需要使用 TabPage
的 Controls
collection 来找到它们。
我们可以设置 BackColor
或继承自 Control
的任何其他 属性 但要设置 RadioButton.Checked
我们需要转换为 RadioButton
;我们需要从 Controls
collection 访问控件,例如通过它的 Name
.
升级到 recursive cloning 后,您将需要使用 Controls.Find(name, true)
来包含嵌套控件..
如您所见,它可以完成,但比编写原始控件需要更多的努力,而且代码感觉有些脆弱,因为我们引入了隐藏的依赖项:所有这些对 cloned[ 的引用=102=] 名称依赖 原始 名称!
最后说明:虽然常规属性会被克隆,但 data
和 structure
容器不会,即所有 Items
、ListViewItems
或 Rows
, Columns
collections etc, etc not cloned!
TabControl tc = TC_Fields;
TabPage tpOld = tc.SelectedTab;
TabPage tpNew = new TabPage();
fields += 1;
tpNew.Name = "Field_" + fields;
tpNew.Text = "Field-" + fields;
foreach (Control c in tpOld.Controls)
{
Control cNew = (Control) Activator.CreateInstance(c.GetType());
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(c);
foreach (PropertyDescriptor entry in pdc)
{
object val = entry.GetValue(c);
if (entry.Name == "Name")
{
val = (String) val + fields;
}
else if (entry.Name == "Location" || entry.Name == "Text" || entry.Name == "Bounds" || entry.Name == "Enabled"
|| entry.Name == "Visible" || entry.Name == "Checked" || entry.Name == "CheckState")
{
//Nothing to do, but do continue!
}
else if (entry.Name == "Controls")
{
Control.ControlCollection controllsInside = (Control.ControlCollection) val;
foreach (Control controllInside in controllsInside)
{
Control cNewInside = (Control) Activator.CreateInstance(controllInside.GetType());
PropertyDescriptorCollection pdcInside = TypeDescriptor.GetProperties(controllInside);
foreach (PropertyDescriptor entryInside in pdcInside)
{
object valInside = entryInside.GetValue(controllInside);
if (entryInside.Name == "Name")
{
valInside = (String) valInside + fields;
}
else if (entryInside.Name == "Location" || entryInside.Name == "Text" || entryInside.Name == "Bounds" || entryInside.Name == "Enabled"
|| entryInside.Name == "Visible" || entryInside.Name == "Checked" || entryInside.Name == "CheckState")
{
//Nothing to do, but do continue!
}
else
{
continue;
}
entryInside.SetValue(cNewInside, valInside);
}
cNew.Controls.Add(cNewInside);
}
}
else
{
continue;
}
entry.SetValue(cNew, val);
}
tpNew.Controls.Add(cNew);
}
tc.TabPages.Add(tpNew);
TC_Fields.SelectedIndex = fields - 1;
这也能解决问题:) @TaW 已经把解决方案放在那里了,但这里有一些复制粘贴代码:)
Copy TabControl Tab 是的,我已经检查过了。
我正在尝试复制一个 tabcontol,但此选项卡应该有它自己的 'behaviour'。
TabControl tc = TC_Fields;
TabPage tpOld = tc.TabPages[0];
TabPage tpNew = new TabPage();
fields += 1;
tpNew.Name = "Field_" + fields;
tpNew.Text = "Field-" + fields;
foreach (Control c in tpOld.Controls)
{
Control cNew = (Control) Activator.CreateInstance(c.GetType());
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(c);
foreach (PropertyDescriptor entry in pdc)
{
object val = entry.GetValue(c);
if (entry.Name == "Name")
{
val = (String) val + fields;
}
entry.SetValue(cNew, val);
}
tpNew.Controls.Add(cNew);
}
tc.TabPages.Add(tpNew);
我试过上面的代码,所以它给新控件一个新的 "id",但我仍然无法单击新选项卡中的控件,它们只模仿第一个选项卡中的内容.
有没有一种方法可以复制所有控件而不模仿它们被复制的控件?
您的代码至少有两个与实际问题无关的问题。让我们先修复其中两个:
您设置了所有属性,甚至是您不应该设置的属性;最值得注意的是你不应该触摸
"WindowTarget"
属性!它仅供内部使用,弄乱它至少会停止某些控件的工作。例如,我发现我无法检查CheckBox
。如果原始
TabPage
也是 current 控件,您的代码只会创建 visible 控件.所有其他页面上的所有控件都是不可见的,如果您尝试克隆任何其他TabPage
所有控件将被添加得很好但保持 invisible.
让我们添加一个过滤器:
if(entry.Name != "WindowTarget") entry.SetValue(cNew, val);
并使新控件可见:
cNew.Visible = true;
tpNew.Controls.Add(cNew);
请注意,这是一个简单的解决方案,因为它还会使那些原本不可见的控件可见。您可以改为显示要克隆的页面或制作不可见控件的列表;用他们的名字你可以找到克隆的对应物..
最后一个问题也很重要,但超出了问题范围:代码仅直接在页面上克隆控件,而不是任何嵌套控件(如 GroupBox
中的 RadioButtons
!)。为此,您必须编写 递归 版本!
- 现在是真正的问题:如何为克隆的控件提供它们自己的行为?
克隆所有属性后,它们就像新添加的控件一样,即它们根本没有事件处理程序。
所以你需要
- 为新控件的事件编写新的事件处理程序
- 联系他们
虽然这并不难,但问题很多..
我们知道命名方案,因此我们可以知道要为哪些控件提供事件。事件名称可以自由选择,但要将它们连接起来,我们需要知道我们正在处理的是哪个新控件..
这是将 Button cb_hello
的 第一个 克隆与 Click
事件挂钩的示例:
if (cNew.Name == "cb_hello1") cNew.Click += buttonNew_Click;
活动中的代码演示了更多问题:
private void buttonNew_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
Console.WriteLine(btn.Name + " says hiho");
btn.Parent.Controls["panel41"].BackColor = Color.ForestGreen;
((RadioButton)btn.Parent.Controls["radioButton11"]).Checked = true;
}
虽然将 sender
转换为 Button
很正常,但是当我们尝试访问我们刚刚克隆的任何控件时,我们 运行 遇到了麻烦:
由于它们是动态创建的,我们无法使用变量访问它们中的任何一个。相反,我们需要使用
TabPage
的Controls
collection 来找到它们。我们可以设置
BackColor
或继承自Control
的任何其他 属性 但要设置RadioButton.Checked
我们需要转换为RadioButton
;我们需要从Controls
collection 访问控件,例如通过它的Name
.升级到 recursive cloning 后,您将需要使用
Controls.Find(name, true)
来包含嵌套控件..
如您所见,它可以完成,但比编写原始控件需要更多的努力,而且代码感觉有些脆弱,因为我们引入了隐藏的依赖项:所有这些对 cloned[ 的引用=102=] 名称依赖 原始 名称!
最后说明:虽然常规属性会被克隆,但 data
和 structure
容器不会,即所有 Items
、ListViewItems
或 Rows
, Columns
collections etc, etc not cloned!
TabControl tc = TC_Fields;
TabPage tpOld = tc.SelectedTab;
TabPage tpNew = new TabPage();
fields += 1;
tpNew.Name = "Field_" + fields;
tpNew.Text = "Field-" + fields;
foreach (Control c in tpOld.Controls)
{
Control cNew = (Control) Activator.CreateInstance(c.GetType());
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(c);
foreach (PropertyDescriptor entry in pdc)
{
object val = entry.GetValue(c);
if (entry.Name == "Name")
{
val = (String) val + fields;
}
else if (entry.Name == "Location" || entry.Name == "Text" || entry.Name == "Bounds" || entry.Name == "Enabled"
|| entry.Name == "Visible" || entry.Name == "Checked" || entry.Name == "CheckState")
{
//Nothing to do, but do continue!
}
else if (entry.Name == "Controls")
{
Control.ControlCollection controllsInside = (Control.ControlCollection) val;
foreach (Control controllInside in controllsInside)
{
Control cNewInside = (Control) Activator.CreateInstance(controllInside.GetType());
PropertyDescriptorCollection pdcInside = TypeDescriptor.GetProperties(controllInside);
foreach (PropertyDescriptor entryInside in pdcInside)
{
object valInside = entryInside.GetValue(controllInside);
if (entryInside.Name == "Name")
{
valInside = (String) valInside + fields;
}
else if (entryInside.Name == "Location" || entryInside.Name == "Text" || entryInside.Name == "Bounds" || entryInside.Name == "Enabled"
|| entryInside.Name == "Visible" || entryInside.Name == "Checked" || entryInside.Name == "CheckState")
{
//Nothing to do, but do continue!
}
else
{
continue;
}
entryInside.SetValue(cNewInside, valInside);
}
cNew.Controls.Add(cNewInside);
}
}
else
{
continue;
}
entry.SetValue(cNew, val);
}
tpNew.Controls.Add(cNew);
}
tc.TabPages.Add(tpNew);
TC_Fields.SelectedIndex = fields - 1;
这也能解决问题:) @TaW 已经把解决方案放在那里了,但这里有一些复制粘贴代码:)