如何基于另一个 UserControl 使用 WinForms 创建一个 UserControl?
How to create a UserControl with WinForms based on another UserControl?
我在下面添加了一些屏幕截图和重现步骤。
我的数据模型有一个基础 class,我们称它为 CommonThing
,它有很多属性。然后有几个具有附加属性的 class 的特殊版本。我现在想创建一个 UI 来将数据输入到这个模型中。
我创建了一个 UserControl
,其中包含所有通用属性的控件,如下所示:
internal partial class CommonThingControl : UserControl {
public CommonThingControl() {
InitializeComponent();
}
// Controller code
}
这还会添加一个由 GUI 设计器填充的 CommonThingControl.Designer.cs
。
我现在将 SpecialFooThingControl
创建为 UserControl
并将 class header 更改为:
internal partial class SpecialFooThingControl : CommonThingControl {
// implementation
}
当我现在在 GUI 设计器中打开 SpecialFooThingControl
时,我看到了 CommonThingControl
的控件,但它们都被锁定了。然而,我在 CommonThingControl
中有一个 TableLayoutPanel
我想添加东西,但我不能改变任何东西,当我尝试将控件拖到 TableLayoutPanel
时,鼠标光标变成 "No parking"标志和VS不让我。当我将 TableLayoutPanel
的访问器设置为 public
.
时甚至会发生这种情况
我可以通过文档大纲将控件移动到TableLayoutPanel
,但是当我重建项目时,它从UI中消失了。
UI 设计器无法实现我想要实现的目标吗?我是否必须手动设置它,还是我忘记了一些额外的步骤?
这是我所做的:
首先,创建一个用户控件,这个很简单。我创建了一个 table 布局面板,我将其设置为 Protected
因为我想添加到它。
这是 FooControl.cs
的代码:
namespace GuiTest {
public partial class FooControl : UserControl {
public FooControl() {
InitializeComponent();
}
}
}
和FooControl.Designer.cs
:
namespace GuiTest {
partial class FooControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.label1 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.textBox1, 1, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 1;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(360, 28);
this.tableLayoutPanel1.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(3, 7);
this.label1.Margin = new System.Windows.Forms.Padding(3, 7, 3, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(25, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Foo";
//
// textBox1
//
this.textBox1.Dock = System.Windows.Forms.DockStyle.Top;
this.textBox1.Location = new System.Drawing.Point(34, 3);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(323, 20);
this.textBox1.TabIndex = 1;
//
// FooControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.tableLayoutPanel1);
this.Name = "FooControl";
this.Size = new System.Drawing.Size(360, 28);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
protected System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox1;
}
}
现在,我添加了 BarControl
:
现在我在 BarControl.cs
中将扩展名从 UserControl
更改为 FooControl
:
namespace GuiTest {
public partial class BarControl : FooControl {
public BarControl() {
InitializeComponent();
}
}
}
BarControl.Designer.cs
:
namespace GuiTest {
partial class BarControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
}
#endregion
}
}
结果是这样的:
由于控件的可访问性,您无法更新它们。
为此,请进入 CommonThingControl.Designer.cs,例如将 private Button MyButton1
更改为 protected Button MyButton1
CommonThingControl
中的控件被锁定的原因是子class SpecialFooThingControl
无法访问它们(它们是私有范围的)。
但是在 CommonThingControl
的设计器中,您可以做的是更改它们的修饰符 属性,这样您就可以 consume/modify 在您的子 class 中使用它们].您需要在每个单独的控件上执行此操作,并且可以简单地更改属性 window 中的下拉列表。您可能可以在 commonthingcontrol.designer.cs
文件中更改它们,但我也很谨慎,因为该文件是自动生成的,如果您不小心,更改很容易被覆盖。
我建议使用 Protected
之类的东西来确保它们对子控件可见,但防止它们在继承链之外被滥用。
编辑
所以进一步研究表明这是 TableLayoutPanel
的限制。摘自 https://msdn.microsoft.com/en-us/library/ms171689.aspx:
Avoid Visual Inheritance The TableLayoutPanel control does not support
visual inheritance in the Windows Forms Designer. A TableLayoutPanel
control in a derived class appears as "locked" at design time.
我在下面添加了一些屏幕截图和重现步骤。
我的数据模型有一个基础 class,我们称它为 CommonThing
,它有很多属性。然后有几个具有附加属性的 class 的特殊版本。我现在想创建一个 UI 来将数据输入到这个模型中。
我创建了一个 UserControl
,其中包含所有通用属性的控件,如下所示:
internal partial class CommonThingControl : UserControl {
public CommonThingControl() {
InitializeComponent();
}
// Controller code
}
这还会添加一个由 GUI 设计器填充的 CommonThingControl.Designer.cs
。
我现在将 SpecialFooThingControl
创建为 UserControl
并将 class header 更改为:
internal partial class SpecialFooThingControl : CommonThingControl {
// implementation
}
当我现在在 GUI 设计器中打开 SpecialFooThingControl
时,我看到了 CommonThingControl
的控件,但它们都被锁定了。然而,我在 CommonThingControl
中有一个 TableLayoutPanel
我想添加东西,但我不能改变任何东西,当我尝试将控件拖到 TableLayoutPanel
时,鼠标光标变成 "No parking"标志和VS不让我。当我将 TableLayoutPanel
的访问器设置为 public
.
我可以通过文档大纲将控件移动到TableLayoutPanel
,但是当我重建项目时,它从UI中消失了。
UI 设计器无法实现我想要实现的目标吗?我是否必须手动设置它,还是我忘记了一些额外的步骤?
这是我所做的:
首先,创建一个用户控件,这个很简单。我创建了一个 table 布局面板,我将其设置为 Protected
因为我想添加到它。
这是 FooControl.cs
的代码:
namespace GuiTest {
public partial class FooControl : UserControl {
public FooControl() {
InitializeComponent();
}
}
}
和FooControl.Designer.cs
:
namespace GuiTest {
partial class FooControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.label1 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.textBox1, 1, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 1;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(360, 28);
this.tableLayoutPanel1.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(3, 7);
this.label1.Margin = new System.Windows.Forms.Padding(3, 7, 3, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(25, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Foo";
//
// textBox1
//
this.textBox1.Dock = System.Windows.Forms.DockStyle.Top;
this.textBox1.Location = new System.Drawing.Point(34, 3);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(323, 20);
this.textBox1.TabIndex = 1;
//
// FooControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.tableLayoutPanel1);
this.Name = "FooControl";
this.Size = new System.Drawing.Size(360, 28);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
protected System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox1;
}
}
现在,我添加了 BarControl
:
现在我在 BarControl.cs
中将扩展名从 UserControl
更改为 FooControl
:
namespace GuiTest {
public partial class BarControl : FooControl {
public BarControl() {
InitializeComponent();
}
}
}
BarControl.Designer.cs
:
namespace GuiTest {
partial class BarControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
}
#endregion
}
}
结果是这样的:
由于控件的可访问性,您无法更新它们。
为此,请进入 CommonThingControl.Designer.cs,例如将 private Button MyButton1
更改为 protected Button MyButton1
CommonThingControl
中的控件被锁定的原因是子class SpecialFooThingControl
无法访问它们(它们是私有范围的)。
但是在 CommonThingControl
的设计器中,您可以做的是更改它们的修饰符 属性,这样您就可以 consume/modify 在您的子 class 中使用它们].您需要在每个单独的控件上执行此操作,并且可以简单地更改属性 window 中的下拉列表。您可能可以在 commonthingcontrol.designer.cs
文件中更改它们,但我也很谨慎,因为该文件是自动生成的,如果您不小心,更改很容易被覆盖。
我建议使用 Protected
之类的东西来确保它们对子控件可见,但防止它们在继承链之外被滥用。
编辑
所以进一步研究表明这是 TableLayoutPanel
的限制。摘自 https://msdn.microsoft.com/en-us/library/ms171689.aspx:
Avoid Visual Inheritance The TableLayoutPanel control does not support visual inheritance in the Windows Forms Designer. A TableLayoutPanel control in a derived class appears as "locked" at design time.