带有C#参数的'UserControl'构造函数

时间:2009-11-23 16:32:54

标签: c# winforms parameters constructor user-controls

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

  1. 我知道在构造一个对象时(没有错误/异常),我的对象很好。
  2. 有助于避免忘记设置某个属性。
  3. 这种心态开始在形式/用户控制开发方面受到伤害。想象一下这个UserControl

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

    在设计时,如果我将UserControl放在表单上,​​我会得到一个Exception

      

    无法创建组件'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,以便我可以将它们传递给{{1}的构造函数}。

    这让我想到了这个:

    MyControl

    对于我使用带有参数的public MyForm() { InitializeComponent(); // Constructed once with no parameters // Constructed a second time, what I really want this.myControl1 = new MyControl(anInt, aString); } 构造函数,我必须添加一个我不需要的第二个构造函数?并实例化控件两次?

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

10 个答案:

答案 0 :(得分:61)

关于Windows窗体的工作方式的设计决策或多或少地排除了Windows窗体组件的参数化.ctors。你可以使用它们,但是当你这样做时,你会走出普遍认可的机制。相反,Windows窗体更喜欢通过属性初始化值。如果没有广泛使用,这是一种有效的设计技术。

但这有一些好处。

  1. 易于使用的客户。客户端代码不需要追踪一堆数据,它可以立即创建一些东西,只是看到它的结果是合理的(如果不感兴趣)。
  2. 设计师易于使用。设计器代码更清晰,更容易解析。
  3. 阻止单个组件中的异常数据依赖性。 (尽管微软甚至用SplitContainer
  4. 吹了这个

    在这种技术中,与设计师合作的形式也有很多支持。 DefaultValueAttributeDesignerSerializationVisibilityAttributeBrowsableAttribute等内容为您提供了以最少的工作量提供丰富客户体验的机会。

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

    我建议坚持使用无参数构造函数并在windows窗体设计原则中工作。如果有UserControl必须强制实际的先决条件,请将它们封装在另一个类中,然后通过属性将该类的实例分配给您的控件。这样可以更好地分离关注点。

答案 1 :(得分:36)

设计类有两种竞争范式:

  1. 使用无参数构造函数并在之后设置一组属性
  2. 使用参数化构造函数在构造函数中设置属性
  3. Visual Studio Windows窗体设计器强制您在控件上提供无参数构造器以便正常工作。实际上,它只需要一个无参数构造函数来实例化控件,而不是设计它们(设计器实际上会在设计控件时解析InitializeComponent方法)。这意味着您可以使用设计器来设计没有无参数构造函数的表单或用户控件,但是您无法设计另一个控件来使用该控件,因为设计器将无法实例化它。

    如果您不打算以编程方式实例化控件(即“手动”构建UI),则不必担心创建参数化构造函数,因为它们不会被使用。即使您要以编程方式实例化控件,也可能需要提供无参数构造函数,以便在需要时仍可在设计器中使用它们。

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

答案 2 :(得分:9)

我会推荐

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,确保控件始终使用正确的值进行实例化。

无论哪种方式,都要确保永远不会调用无参数ctor。

我没有对此进行测试,如果它失败了我道歉!

答案 3 :(得分:4)

为设计师提供无参数构造函数并将其设为私有 - 如果你真的必须这样做......: - )

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

答案 4 :(得分:4)

遗憾的是,这是一个经常发生的设计问题,而不仅仅是在控制空间中。

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

在这些情况下,您必须以最佳方式设计控件/组件。使用合理的(最好是最常见的)默认参数可以大大提高,因为至少(希望)可以用一个好的值初始化组件。

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

同样,我同意 - 这不是理想的,但这只是我们必须忍受和解决的问题。

答案 5 :(得分:4)

嗯,简而言之,设计师就是那种喜欢无参数构造函数的人。所以,据我所知,如果你真的想使用基于参数的构造函数,你可能会陷入以某种方式解决它。

答案 6 :(得分:3)

这样做:

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();
    }
}

然后'真正的'构造函数可以相应地行动。

答案 7 :(得分:1)

问题问题已经有一段时间了,但也许我的方法对某人有帮助。

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

因此,我不是使用实际的构造函数,而是简单地定义一个 public void PostConstructor ,其中所有内容都放在构造函数中。因此UserControl的Actual Constructor始终只包含 InitializeComponent()。 这样您就不必调整自己喜欢的编程范例,而VisualStudios需要正确运行Designer。要使这个编程模式起作用,必须从最底层开始。

在实践中,这个 PostConstructionalizm 看起来有点像这样: 让我们从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
  }
}

最后,一个调用用户控件之一的Form只是将PostConstructor放在 InitializeComponent 之后。

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

答案 8 :(得分:0)

我有办法解决它。

  1. 使用无参数构造函数在表单上创建控件A.
  2. 使用contstructor形式的参数化构造函数创建一个控件B.
  3. 将位置和大小从A复制到B.
  4. 让A看不见。
  5. 将B添加到A的父级。
  6. 希望这会有所帮助。我刚刚遇到了同样的问题并尝试并测试了这种方法。

    演示代码:

    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);
    }
    

    holder是Control A,fixedKAlgorithmComboBox是Control B。

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

答案 9 :(得分:0)

我在尝试将主Windows窗体中创建的对象传递给自定义UserControl表单时遇到了类似的问题。对我有用的是向 UserControl.Designer.cs 添加一个默认值的属性,并在主窗体中的InitializeComponent()调用之后更新它。具有默认值可防止WinForms设计器抛出未设置为对象实例的" Object引用。错误。

示例:

// 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
    ...
}

希望这有帮助!