在C#中的参数'UserControl'构造函数

叫我疯了,但我喜欢喜欢带参数的构造函数(如果需要的话)的types,而不是没有参数的构造函数,然后设置属性。 我的思考过程:如果要求属性实际构造对象,则应该在构造函数中进行。 我有两个好处:

  1. 我知道当一个对象被构造时(没有错误/exception),我的对象是好的。
  2. 它有助于避免忘记设置某个属性。

在forms/用户控制发展方面,这种思维方式已经开始伤害我。 想象一下这个UserControl:

public partial class MyUserControl : UserControl { public MyUserControl(int parm1, string parm2) { // We'll do something with the parms, I promise InitializeComponent(); } } 

在devise时,如果我把这个UserControl放在窗体上,我会得到一个exception:

无法创build组件“MyUserControl”…
System.MissingMethodException – 为此对象定义的无参数构造函数。

对我来说,唯一的办法就是添加默认的构造函数(除非其他人知道方法)。

 public partial class MyUserControl : UserControl { public MyUserControl() { InitializeComponent(); } public MyUserControl(int parm1, string parm2) { // We'll do something with the parms, I promise InitializeComponent(); } } 

不包括无参数构造函数的重点是避免使用它。 而我甚至不能使用DesignMode属性来做类似的事情:

 public partial class MyUserControl : UserControl { public MyUserControl() { if (this.DesignMode) { InitializeComponent(); return; } throw new Exception("Use constructor with parameters"); } } 

这也不起作用:

 if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) 

好吧,沿着…走

我有我的无参数构造函数,我可以把它放在窗体上,窗体的InitializeComponent看起来像这样:

 private void InitializeComponent() { this.myControl1 = new MyControl(); // blah, blah } 

并相信我,因为我做了它(是的,忽略了Visual Studio生成的注释),我试着弄乱了,我传递参数给InitializeComponent,以便我可以将它们传递给MyControl的构造函数。

这导致我:

 public MyForm() { InitializeComponent(); // Constructed once with no parameters // Constructed a second time, what I really want this.myControl1 = new MyControl(anInt, aString); } 

对于我使用带有参数的UserControl来构造函数,我不得不添加第二个构造函数,我不需要? 并实例化控制两次?

我觉得我一定在做错事。 思考? 意见? 保证(希望)?

有关Windows窗体工作方式的devise决定或多或less妨碍了Windows窗体组件的参数化。 你可以使用它们,但是当你这样做的时候,你正在走出通常认可的机制之外。 而是,Windows窗体更喜欢通过属性初始化值。 这是一个有效的devise技术,如果不被广泛使用的话。

这虽然有一些好处。

  1. 易于使用的客户。 客户端代码不需要追踪一堆数据,它可以立即创build一些东西,只要看看它是否合理(如果不感兴趣)的结果。
  2. 易于使用的devise师。 devise器代码通常更清晰,更易于parsing。
  3. 阻止单个组件内的exception数据依赖关系。 (虽然微软也用SplitContainer吹了这个)

这种技术中还有很多与devise师合作的forms上的支持。 诸如DefaultValueAttributeDesignerSerializationVisibilityAttributeBrowsableAttribute让您有机会以最小的努力提供丰富的客户端体验。

(这不是在Windows窗体中为客户体验做的唯一的妥协,抽象的基类组件也会变得多毛。)

我build议坚持一个无参数的构造函数,并在Windows窗体devise原则中工作。 如果有真正的先决条件,您的UserControl必须强制执行,将它们封装到另一个类中,然后通过属性将该类的实例分配给您的控件。 这也会使问题更好地分离。

有两种竞争的devise类的范例:

  1. 之后使用无参数构造函数并设置一堆属性
  2. 使用参数化构造函数在构造函数中设置属性

Visual Studio Windows窗体devise器迫使您提供控件上的无参数构造函数才能正常工作。 实际上,它只需要一个无参数的构造函数来实例化控件,而不是devise它们(devise人员在devise控件时会实际parsingInitializeComponent方法)。 这意味着您可以使用devise器来devise一个没有无参数构造函数的窗体或用户控件,但是您不能devise另一个控件来使用该控件,因为devise器将无法实例化它。

如果你不打算以编程方式实例化你的控件(也就是说“手工构build你的UI”),那么不要担心创build参数化的构造函数,因为它们不会被使用。 即使您要以编程方式实例化您的控件,也可能需要提供无参数的构造函数,以便在需要时仍可以在devise器中使用它们。

无论使用哪种模式,在OnLoad()方法中放入冗长的初始化代码通常也是一个好主意,特别是因为DesignMode属性将在加载时起作用,但在构造函数中不起作用。

我会推荐

 public partial class MyUserControl : UserControl { private int _parm1; private string _parm2; private MyUserControl() { InitializeComponent(); } public MyUserControl(int parm1, string parm2) : this() { _parm1 = parm1; _parm2 = parm2; } } 

通过这种方式,基础构造函数总是首先被调用,并且任何对组件的引用都是有效的。

如果需要的话,你可以重载公共计算机,确保控制总是以正确的值进行实例化。

无论哪种方式,你确保无参数的ctor永远不会被调用。

我没有testing过,所以如果摔倒了,我很抱歉!

不幸的是,这不仅是一个devise问题,而且还会经常发生,而不仅仅是在控制领域。

即使无参数构造函数并不理想,通常情况下你需要一个无参数的构造函数。 例如,许多值types(IMO)在没有无参数的构造函数的情况下会更好,但是不可能创build一个以这种方式工作的types。

在这些情况下,您必须尽可能以最好的方式devise控制/组件。 使用合理的(最好是最常用的)默认参数可以起到很大的帮助,因为您至less可以(希望)初始化具有良好价值的组件。

此外,尝试devise组件的方式可以在生成组件后更改这些属性。 对于Windows Forms组件,这通常很好,因为在安全地加载时间之前,您几乎可以做任何事情。

我再一次同意 – 这并不理想,但这只是我们必须忍受和解决的问题。

总而言之,devise师就是喜欢无参数构造的人。 所以,据我所知,如果你真的想要使用基于参数的构造函数,你可能会坚持以某种方式来解决这个问题。

为devise者提供一个无参数的构造函数,并将其设为私有的 – 如果你真的必须这样做的话:…)

编辑:当然,这不会为UserControls工作。 我显然没有想清楚。 devise者需要在InitializeComponent()中执行代码,如果构造函数是私有的,它就不能工作。 对于那个很抱歉。 它确实适用于表单,但是。

只要这样做:

 public partial class MyUserControl : UserControl { public MyUserControl() : this(-1, string.Empty) { } public MyUserControl(int parm1, string parm2) { // We'll do something with the parms, I promise if (parm1 == -1) { ... } InitializeComponent(); } } 

那么“真正的”构造函数就可以相应地执行。

这个问题被问到很长一段时间了,但也许我的方法对某人有帮助。

我个人也喜欢使用参数化的构造函数来避免忘记设置某个属性。

因此,不是使用实际的构造函数,而是简单地定义一个公共无效的PostConstructor ,通常放置在构造函数中。 所以UserControl的实际构造函数总是只包含InitializeComponent() 。 这样你就不必调整你最喜欢的编程范例,以便VisualStudios正确运行devise器。 为了使这个编程模式起作用,必须从最底层开始。

实际上,这个PostConstructionaliz看起来有些像这样:让我们从UserControl调用层次底部的一个Control开始。

 public partial class ChildControl : UserControl { public ChildControl() { InitializeComponent(); } public void PostConstructor(YourParameters[]) { //setting parameters/fillingdata into form } } 

所以包含ChildControl的UserControl看起来就像这样:

 public partial class FatherControl : UserControl { public FatherControl() { InitializeComponent(); } public void PostConstructor(YourParameters[]) { ChildControl.PostConstructor(YourParameters[]) //setting parameters/fillingdata into form } } 

最后,一个调用用户控件之一的表单只需要在InitializeComponent之后放置PostConstructor。

 public partial class UI : Form { public UI(yourParameters[]) { InitializeComponent(); FatherControl.PostConstructor(yourParameters[]); } } 

我有办法解决它。

  1. 使用无参数构造函数在窗体上创build一个控件A.
  2. 用contstructor的forms创build一个具有参数化构造函数的控件B.
  3. 将位置和大小从A复制到B
  4. 使一个无形的。
  5. 将B添加到A的父项。

希望这会有所帮助。 我刚刚遇到同样的问题,并尝试和testing这种方法。

示范代码:

 public Form1() { InitializeComponent(); var holder = PositionHolderAlgorithmComboBox; holder.Visible = false; fixedKAlgorithmComboBox = new MiCluster.UI.Controls.AlgorithmComboBox(c => c.CanFixK); fixedKAlgorithmComboBox.Name = "fixedKAlgorithmComboBox"; fixedKAlgorithmComboBox.Location = holder.Location; fixedKAlgorithmComboBox.Size = new System.Drawing.Size(holder.Width, holder.Height); holder.Parent.Controls.Add(fixedKAlgorithmComboBox); } 

持有者是控制A,fixedKAlgorithmComboBox是控制B.

一个更好更完整的解决scheme是使用reflection将属性逐一地从A复制到B.目前,我很忙,我没有这样做。 也许在将来我会回来的代码。 但这并不难,我相信你可以自己做。

我有一个类似的问题,试图将主窗体中创build的对象传递给自定义的UserControl窗体。 我的工作是添加一个默认值的属性UserControl.Designer.cs并在主窗体InitializeComponent()调用后更新它。 具有默认值可防止WinFormsdevise器抛出“未设置为对象实例的对象引用”错误。

例:

 // MainForm.cs public partial class MainForm : Form public MainForm() { /* code for parsing configuration parameters which producs in <myObj> myConfig */ InitializeComponent(); myUserControl1.config = myConfig; // set the config property to myConfig object } //myUserControl.Designer.cs partial class myUserControl { /// <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); } // define the public property to hold the config and give it a default value private myObj _config = new myObj(param1, param2, ...); public myObj config { get { return _config ; } set { _config = value; } } #region Component Designer generated code ... } 

希望这可以帮助!