JustLooking JustLooking - 22 days ago 8
C# Question

'UserControl' constructor with parameters in C#

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

Answer Source

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.