'UserControl' constructor with parameters in C#

Viewed 56369

Call me crazy, but I'm the type of guy that likes constructors with parameters (if needed), as opposed to a constructor with no parameters followed by setting properties. My thought process: if the properties are required to actually construct the object, they should go in the constructor. I get two advantages:

  1. I know that when an object is constructed (without error/exception), my object is good.
  2. It helps avoid forgetting to set a certain property.

This mindset is starting to hurt me in regards to form/usercontrol development. Imagine this UserControl:

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

At designtime, if I drop this UserControl on a form, I get an Exception:

Failed to create component 'MyUserControl' ...
System.MissingMethodException - No parameterless constructor defined for this object.

It seems like, to me, the only way around that was to add the default constructor (unless someone else knows a way).

public partial class MyUserControl : UserControl
{
  public MyUserControl()
  {
    InitializeComponent();
  }

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

The whole point of not including the parameterless constructor was to avoid using it. And I can't even use the DesignMode property to do something like:

public partial class MyUserControl : UserControl
{
  public MyUserControl()
  {
    if (this.DesignMode)
    {
      InitializeComponent();
      return;
    }

    throw new Exception("Use constructor with parameters");
  }
}

This doesn't work either:

if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)

Fine, moving along ...

I have my parameterless constructor, I can drop it on the form, and the form's InitializeComponent will look like this:

private void InitializeComponent()
{
  this.myControl1 = new MyControl();

  // blah, blah
}

And trust me, because I did it (yes, ignoring the comments Visual Studio generated), I tried messing around and I passed parameters to InitializeComponent so that I could pass them to the constructor of MyControl.

Which leads me to this:

public MyForm()
{
  InitializeComponent(); // Constructed once with no parameters

  // Constructed a second time, what I really want
  this.myControl1 = new MyControl(anInt, aString);  
}

For me to use a UserControl with parameters to the constructor, I have to add a second constructor that I don't need? And instantiate the control twice?

I feel like I must be doing something wrong. Thoughts? Opinions? Assurance (hopefully)?

10 Answers

I have a way to work around it.

  1. Create a control A on the form with the parameterless constructor.
  2. Create a control B with parameterized constructor in the form contstructor.
  3. Copy position and size from A to B.
  4. Make A invisible.
  5. Add B to A's parent.

Hope this will help. I just encountered the same question and tried and tested this method.

Code for demonstrate:

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 is Control A, fixedKAlgorithmComboBox is Control B.

An even better and complete solution would be to use reflect to copy the properties one by one from A to B. For the time being, I am busy and I am not doing this. Maybe in the future I will come back with the code. But it is not that hard and I believe you can do it yourself.

I had a similar problem trying to pass an object created in the main Windows Form to a custom UserControl form. What worked for me was adding a property with a default value to the UserControl.Designer.cs and updating it after the InitializeComponent() call in the main form. Having a default value prevents WinForms designer from throwing an "Object reference not set to an instance of an object" error.

Example:

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

Hope this helps!

Related