110

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)?

Neuron
  • 5,141
  • 5
  • 38
  • 59
JustLooking
  • 2,405
  • 4
  • 28
  • 38

11 Answers11

71

Design decisions made regarding the way Windows Forms works more or less preclude parameterized .ctors for windows forms components. You can use them, but when you do you're stepping outside the generally approved mechanisms. Rather, Windows Forms prefers initialization of values via properties. This is a valid design technique, if not widely used.

This has some benefits, though.

  1. Ease of use for clients. Client code doesn't need to track down a bunch of data, it can immediately create something and just see it with sensible (if uninteresting) results.
  2. Ease of use for the designer. Designer code is clearer and easier to parse in general.
  3. Discourages unusual data dependencies within a single component. (Though even microsoft blew this one with the SplitContainer)

There's a lot of support in forms for working properly with the designer in this technique also. Things like DefaultValueAttribute, DesignerSerializationVisibilityAttribute, and BrowsableAttribute give you the opportunity to provide a rich client experience with minimal effort.

(This isn't the only compromise that was made for client experience in windows forms. Abstract base class components can get hairy too.)

I'd suggest sticking with a parameterless constructor and working within the windows forms design principles. If there are real preconditions that your UserControl must enforce, encapsulate them in another class and then assign an instance of that class to your control via a property. This will give a bit better separation of concern as well.

Greg D
  • 43,259
  • 14
  • 84
  • 117
36

There are two competing paradigms for designing classes:

  1. Use parameterless constructors and set a bunch of properties afterwards
  2. Use parameterized constructors to set properties in the constructor

The Visual Studio Windows Forms Designer forces you to provide a parameterless constuctor on controls in order to work properly. Actually, it only requires a parameterless constructor in order to instantiate controls, but not to design them (the designer will actually parse the InitializeComponent method while designing a control). This means that you can use the designer to design a form or user control without a parameterless constructor, but you cannot design another control to use that control because the designer will fail to instantiate it.

If you don't intend to programmatically instantiate your controls (i.e. build your UI "by hand"), then don't worry about creating parameterized constructors, since they won't be used. Even if you are going to programmatically instantiate your controls, you may want to provide a parameterless constructor so they can still be used in the designer if need be.

Regardless of which paradigm you use, it is also generally a good idea to put lengthy initialization code in the OnLoad() method, especially since the DesignMode property will work at load time, but not work in the constructor.

Kevin Kibler
  • 13,357
  • 8
  • 38
  • 61
10

I would recommend

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

As this way the base constructor is always called first and any references to components are valid.

You could then overload the public ctor if need be, ensuring the control is always instantiated with the correct values.

Either way, you ensure that the parameterless ctor is never called.

I haven't tested this so if it falls over I apologise!

Antony Koch
  • 2,043
  • 1
  • 16
  • 23
  • If my memory serves me, because of the private "parameterless" constructor, I still couldn't use this in design mode. But, +1 anyway. – JustLooking Dec 02 '09 at 15:14
5

This is unfortunately a design issue that will occur frequently, not just in the control space.

There are often situations where you need to have a parameterless constructor, even though a parameterless constructor is not ideal. For example, many value types, IMO, would be better off without parameterless constructors, but it's impossible to create one that works that way.

In these situations, you have to just design the control/component in the best manner possible. Using reasonable (and preferably the most common) default parameters can help dramatically, since you can at least (hopefully) initialize the component with a good value.

Also, try to design the component in a way that you can change these properties after the component is generated. With Windows Forms components, this is typically fine, since you can pretty much do anything until load time safely.

Again, I agree - this isn't ideal, but it's just something we have to live with and work around.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
4

Provide a parameterless constructor for the designer and make it private - if you really must do it this way... :-)

EDIT: Well of course this won't work for UserControls. I obviously wasn't thinking clearly. The designer need to execute the code in InitializeComponent() and it's can't work if the constructor is private. Sorry about that. It does work for forms, however.

Dan Byström
  • 9,067
  • 5
  • 38
  • 68
  • That was actually the first thing I did, but I'm pretty sure that doesn't work. You'll get the same 'parameterless constructor' error when dropping it on the form. – JustLooking Nov 23 '09 at 16:39
  • 1
    If it is private, I doubt that the designer can use it. – Fredrik Mörk Nov 23 '09 at 16:41
  • Really? I always make the constructor of my *dialogs* private... and that works, at least. – Dan Byström Nov 23 '09 at 16:41
  • 1
    UserControl's don't work - this also has significant issues if you're using multiple assemblies. – Reed Copsey Nov 23 '09 at 16:43
  • 1
    Are you sure they are `private`? If you make a constructor private, only the code within the class can use it. If you make them `internal` all code in the same assembly can use them, but no code outside the assembly (unless you use the `InternalsVisibleTo` attribute). For the VS designers to be able to use them, they need to be `public`. – Fredrik Mörk Nov 23 '09 at 16:44
  • I'm pretty sure! :-) That's a design idiom I've been using for several years now. The designer seems to be using reflection. – Dan Byström Nov 23 '09 at 16:48
  • 1
    Yes, that might be. Reflection does not let itself be stopped by such details as accessibility of members... – Fredrik Mörk Nov 23 '09 at 16:52
  • 1
    Private constructors for modal dialog boxes (forms) is a great way to make sure that a consumer of a dialog doesn't forget to dispose it (or even have to create it for that matter). Use a public static method on the form and be happy! – Dan Byström Nov 23 '09 at 17:01
4

Just do this:

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

Then the 'real' constructor can act accordingly.

Bob Nadler
  • 2,755
  • 24
  • 20
  • 1
    I admit it, I'm not sure what this buys me (or how it answers my question). – JustLooking Nov 23 '09 at 16:56
  • 1
    It was the duplicate InitializeComponent() calls that caught my eye. Also, a private parameterless constructor with a UserControl works with the VS designer for me. – Bob Nadler Nov 23 '09 at 17:09
  • 1
    Well what you can do with this is show in the designer a message like "parameter foo needs setting for runtime". To indicate the need to setup the parameters you would normally pass in using a constructor. The setting of the parameter may need to handle some of the controls setup which would usually be done in the constructor. – PeteT Feb 25 '10 at 10:57
4

Well, in short, the designer is the kind of guy that likes parameter-less constructors. So, to the best of my knowledge, if you really want to use parameter based constructors you are probably stuck with working around it one way or the other.

Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • 1
    "The designer is the kind of guy" ... good stuff (funny)! Yeah, it looks like I'll have to utilize work-arounds. I just wanted to make sure that there wasn't some slick way of doing things, or there's some "known rule" such as: "You never do what I was doing with my UserControl" – JustLooking Nov 23 '09 at 16:55
2

It's quite a while since the question was asked, but maybe my approach is helpful to somebody.

I personally also prefer to use parameterized Constructors to avoid forgetting to set a certain property.

So instead of using the actual Constructor I simply define a public void PostConstructor where all things are put you would normally put in the Constructor. So the Actual Constructor of the UserControl always contains only InitializeComponent(). This way you don't have to adjust your favourite programming paradigm to VisualStudios needs to run the Designer properly. For this programming schema to work it has to be followed from the very bottom.

In practice this PostConstructionalizm would look somewhat like this: Let's start with a Control at the bottom of your UserControl call hierarchy.

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

  public void PostConstructor(YourParameters[])
  {
      //setting parameters/fillingdata into form
  }
}

So a UserControl containing the ChildControl would look something like that:

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

  public void PostConstructor(YourParameters[])
  {
      ChildControl.PostConstructor(YourParameters[])
      //setting parameters/fillingdata into form
  }
}

And finally a Form calling one of the User Control simply puts the PostConstructor after InitializeComponent.

public partial class UI : Form
{
  public UI(yourParameters[])
  {
    InitializeComponent();
    FatherControl.PostConstructor(yourParameters[]);
  }
}
Hermilton
  • 1,179
  • 1
  • 7
  • 5
1

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.

Eric Chow
  • 411
  • 4
  • 7
0

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!

0

Downcast ParentForm

My workaround is to define an interface and downcast ParentForm to it. This way you can keep properties private and (likely) initialized early.

ParentForm property becomes available after the user control is added to Controls list of the form, so the downcast cannot be done in the user control's constructor. I often use ISupportInitialize.EndInit, but using a Load event handler also works fine (i.e. designer will fail if the interface is not implemented).

/// <summary>Service required for MyUserControl. Please implement this in your parent form.</summary>
interface IMyUserControlParam // or MyUserControl.IParam if you prefer it nested
{
    int Parm1 { get; }
    string Parm2 { get; }
}

public partial class MyUserControl : UserControl, ISupportInitialize
{
    // ... constructor remains parameterless ...

    public void EndInit() // or MyUserControl_Load event if you already have one
    {
        // Retrieves parameters from the parent form.
        // Raises if the parent form author forgot to implement the interface.
        // Also you may want to check like: if (ParentForm is not IMyUserControlParam) { ... handle the error gracefully ... }

        IMyUserControlParam param = (IMyUserControlParam)ParentForm;
        m_parm1 = param.Parm1;
        m_parm2 = param.Parm2;
    }
}

public partial class MyForm : Form, IMyUserControlParam
{
    // Define parameters for MyUserControl here
    public int Parm1 => 42;
    public string Parm2 => "foo";
}

It will still not be checked by the compiler as constructors with parameters are, but at least it will throw as soon as the form is loaded, so you'll have a better chance than relying solely on consumers properly calling myUserControl.Init(1, "foo"); or
registering the event handler like myUserControl.MyInit += myUserControl_MyInit;.

Variants

As a short and lazy variant, you can do with getters:

public partial class MyUserControl : UserControl
{
    private IMyUserControlParam ParentParam => (IMyUserControlParam)ParentForm;
    private int Parm1 => ParentParam.Parm1;
    private string Parm1 => ParentParam.Parm2;
}

Also, you could skip the interface and refer directly to the parent form class like param = (MyForm)ParentForm. However, this undermines reusability, which defeats the purpose of creating a user control.

snipsnipsnip
  • 2,268
  • 2
  • 33
  • 34