创建自定义 Windows 窗体控件的最佳方法是啥?
Posted
技术标签:
【中文标题】创建自定义 Windows 窗体控件的最佳方法是啥?【英文标题】:What is the best way to create customized Windows Form controls?创建自定义 Windows 窗体控件的最佳方法是什么? 【发布时间】:2016-05-23 19:56:29 【问题描述】:我正在制作 C# Windows 窗体应用程序,该应用程序具有许多使用相同属性和样式的许多文本框和标签的窗体。
我没有更改每个文本框和每个标签的属性,而是创建了一个名为 MyTextBox
的类,它继承自 System.Windows.Forms.TextBox
,然后在类构造函数中更改了它的属性,如下所示:
class MyTextBox:TextBox
public MyTextBox()
this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.Font = new System.Drawing.Font("Bookman Old Style", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.ForeColor = System.Drawing.Color.Blue;
this.Size = new System.Drawing.Size(257, 23);
构建项目类后出现在工具箱中,并通过在我的表单上创建此类实例,它工作正常。
问题是,当我更改 MyTextBox
类中的任何属性并重建项目时,更改不适用于已经实例化的对象,当我查看设计器代码时,我发现 IDE 复制了 MyTextBox
中的所有属性类到设计器代码,所以我必须在对类代码进行任何更改后重新创建所有实例。
private void InitializeComponent()
this.MyTextBox1 = new WindowsFormsApplication9.MyTextBox();
this.SuspendLayout();
//
// MyTextBox1
//
this.MyTextBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.MyTextBox1.Font = new System.Drawing.Font("Bookman Old Style", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.MyTextBox1.ForeColor = System.Drawing.Color.Blue;
this.MyTextBox1.Location = new System.Drawing.Point(67, 43);
有什么办法可以解决这个问题?我希望对应用于所有已实例化对象的类代码进行任何更改,而无需重新创建它们,或者如果有更好的方法请提供帮助。
假设我需要控制MyTextBox
实例的5 个属性,例如ForeColor
,默认Width
,默认字体Style
,默认字体size
和BorderStyle
。除了Width
属性之外,所有这些属性都不应具有除默认值以外的其他值。
【问题讨论】:
您可以在覆盖的OnCreateControl
中设置属性。该代码应在设计器生成代码之后运行。但请记住,您在设计器中设置的任何内容都将被这些属性覆盖。
当您从现有的控件类创建自己的控件类时,您通常需要做的一件事是替换基类属性。并给他们一个新的 [DefaultValue] 属性。使用 override 或 new 关键字。
好吧,假设我需要控制 MyTextBox 实例的 5 个属性,例如 ForeColor、默认宽度、默认字体样式、默认字体大小和边框样式。除 Width 属性外,所有这些属性都不应该有其他值比默认值。因此,在您的 ForeColor 示例中,值将是 Color.Color3 但对于 Width 它将是第二个值
我将选择A
和B
两个属性,其中A
是不可更改的。所以A
不能在实例中更改,并且始终具有默认值。但是关于B
,它有一个默认值,如果您在实例中不触摸它,更改类中的默认值将导致更改所有未触摸值的实例中的值。但是如果在某个实例中您更改了B
,则更改默认值不会导致该特定实例中B
的更改。好吗?
当然,这正是我想要的
【参考方案1】:
首先,您应该为构造函数中的属性提供合适的默认值。然后您应该覆盖或隐藏属性并使用以下属性之一装饰它们:
[DefaultValue]
仅当主题的值与您在属性中设置的默认值不同时,设计器才会序列化这些属性。
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
设计器不会序列化这些属性的值。
代码
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
public partial class MyTextBox : TextBox
public MyTextBox()
this.ForeColor = Color.Red;
this.Font = new Font("Tahoma", 9, FontStyle.Italic);
this.Width = 200;
[DefaultValue(typeof(Color), "Red")]
public override Color ForeColor
get return base.ForeColor;
set base.ForeColor = value;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override Font Font
get return base.Font;
set base.Font = value;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Size Size
get return base.Size;
set base.Size = value;
在上面的示例中,我创建了一个自定义的TextBox
,具有以下功能:
ForeColor
的默认值为Color.Red
。如果您在控件实例中更改ForeColor
的值,则该值将被序列化。如果更改类中的默认值,则只有其ForeColor
未触及的实例将使用新的默认值,其他实例将使用其ForeColor
值。
Font
的默认值是new Font("Tahoma", 9, FontStyle.Italic)
,由于我们告诉设计者不要序列化Font
属性,如果更改不同实例的值,属性的新值将不会保存,所有实例都会使用MyTextBox
的构造函数中设置的默认值。
为了设置默认Width
,用户无法使用设计器更改它,我覆盖了Size
,并说设计器不对其进行序列化,因此宽度将设置为我在构造函数中设置的默认Width
。
【讨论】:
工作正常,但有一个问题。在构建项目后,创建更多 MyTextBox 实例使设计人员将 ForeColor 的当前值保存在表单设计器代码中,因此 MyTextBox 类代码中的任何更改都不适用。同样,在更改其中一个实例的 ForeColor 值时也会发生这种情况,设计器不仅会保存受影响的实例,还会保存所有实例的当前值 通过在构造函数中为ForeColor
属性和this.ForeColor = Color.Red;
设置[DefaultValue(typeof(Color), "Red")]
,我们使它以这种方式工作:1 如果您更改ForeColor
一个实例,该值将被序列化,不再关注默认值。 2 如果您更改控件中的默认值(在属性和构造函数中),其ForeColor
属性未触及的所有控件都将使用新的默认值。
答案的重点在第一段,如果您仔细阅读,您可以决定如何处理属性。
是的,这很好用。非常感谢。这是我的错误,我没有同时更改构造函数和属性中的值,所以它以某种方式产生了问题,但是当同时更改它们时,它工作得非常好和稳定。非常感谢您的耐心等待。你是天才!【参考方案2】:
设置文件应该做你想做的事。在您的项目中创建一个新的设置文件(如果尚不存在),然后添加一个新设置,如下所示:
然后在您的自定义TextBox
中覆盖OnCreateControl
protected override void OnCreateControl()
base.OnCreateControl();
ForeColor = Settings.Default.TextBox_ForeColor;
现在您可以在设置文件中更改ForeColor
,更改将级联到您的自定义TextBox
的所有实例。对Font
、Size
等遵循相同的模式,只需确保在设置文件中设置正确的Type
。
【讨论】:
以上是关于创建自定义 Windows 窗体控件的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章