在C#中的参数'UserControl'构造函数
叫我疯了,但我喜欢喜欢带参数的构造函数(如果需要的话)的types,而不是没有参数的构造函数,然后设置属性。 我的思考过程:如果要求属性实际构造对象,则应该在构造函数中进行。 我有两个好处:
- 我知道当一个对象被构造时(没有错误/exception),我的对象是好的。
- 它有助于避免忘记设置某个属性。
在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技术,如果不被广泛使用的话。
这虽然有一些好处。
- 易于使用的客户。 客户端代码不需要追踪一堆数据,它可以立即创build一些东西,只要看看它是否合理(如果不感兴趣)的结果。
- 易于使用的devise师。 devise器代码通常更清晰,更易于parsing。
- 阻止单个组件内的exception数据依赖关系。 (虽然微软也用
SplitContainer
吹了这个)
这种技术中还有很多与devise师合作的forms上的支持。 诸如DefaultValueAttribute
, DesignerSerializationVisibilityAttribute
和BrowsableAttribute
让您有机会以最小的努力提供丰富的客户端体验。
(这不是在Windows窗体中为客户体验做的唯一的妥协,抽象的基类组件也会变得多毛。)
我build议坚持一个无参数的构造函数,并在Windows窗体devise原则中工作。 如果有真正的先决条件,您的UserControl
必须强制执行,将它们封装到另一个类中,然后通过属性将该类的实例分配给您的控件。 这也会使问题更好地分离。
有两种竞争的devise类的范例:
- 之后使用无参数构造函数并设置一堆属性
- 使用参数化构造函数在构造函数中设置属性
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[]); } }
我有办法解决它。
- 使用无参数构造函数在窗体上创build一个控件A.
- 用contstructor的forms创build一个具有参数化构造函数的控件B.
- 将位置和大小从A复制到B
- 使一个无形的。
- 将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 ... }
希望这可以帮助!