Introduction

The idea of drawing duplicate-controls on a form came to me after I did a few commercial projects. I found that making several forms with 30+ controls can be a pretty tiresome task. That is especially true when you are making a form with lots of text fields. You have to make several TextBoxes, and each of them must be accompanied by a Label. More than that, every Label must have the same distance to the TextBox so your forms would look neat. In the worst case scenario, you have to assign each label with some nice name, because there can always be a situation where some label changes its text when you type some information on the text box and you don't want to end up with names like 'labelXXX'.

In VC#.NET, we have a mechanism to build multi-control components, and it's called the UserControl class. This is basically a container where you can put other controls from the toolbox. However, this mechanism has disadvantages. If you make labels and textboxes and you want to align all the textboxes, you will have big problems, you cannot use leads anymore:

When using my technique, I can still use leads, despite the fact that I have a multi-control component:

Of course, there is a catch, and that is layouts... but since I'm not a big fan of layouts, I can live with that.

Background

If you ever wrote something in Delphi, you must have noticed that the VCL (Visual Component Library) has a control which is called LabeledEdit. In this article, we will try to make the same thing in C# .NET. However, instead of using the designer and inheriting from UserControl, we will use only code and inherit directly from TextBox.

The Solution

When you want to draw another control in your component, there are three methods you have to override and customize:

void OnParentChanged(EventArgs e)

void OnLocationChanged(EventArgs e)

void Dispose(bool disposing)

You can read in MSDN or other documentation sites about these, but what you need to know is that on OnParentChanged(EventArgs e), you want to place the creation and drawing code of your control. For example:

The third one is not really mandatory because your component will work without this, but as soon as you delete it from the designer, the other components will not be deleted. The problem is, you cannot select them because from the designer point of view, they do not exist :-). The Dispose code is in the *.designer.cs file, but I always like to move it to my main *.cs file.

I think the most important thing to remember is when you do the set section, "reload" the component's position, so every time you change the property, the label will re-position to fit nicely. Of course, you have to override some events like Enable, Visible to make it look more deluxe, but this is pretty much basic to get you started. Now, we are going to move to the next class, which is a LabeledText with a button.

LabeledTextBox with Button: Inherited from the Component We Just Made

This is a little more complicated because we have to make an event that will respond to the button click. We cannot do it directly because the designer only sees our TextBox, for it, the Label and Button do not exist. So, we have to do a little trick...

One of the nice things in C# is the fact that we can add events to a component, simply by using the operator +=. Of course, we have to make some method which will do work that must be done.

// *STEP 1* create this method
protectedvirtualvoid OnButtonClick(object sender, EventArgs e)
{
// just to test the event
MessageBox.Show("You have just clicked you button");
}
// and in overrided "position" function
...
// *STEP 2* link method and event
// 27.X.2008 put this below adding component to Parent (OnParentChanged)
// so it will execute only once
_button.Click += new EventHandler(OnButtonClick);

So now, we have a method that will fire when a button is clicked, but since we want to transfer this event to our component, we have to make a delegate and an event.

// arguments that will be used for our event *STEP 3*
publicdelegatevoid ButtonClickDelegate(object sender, EventArgs e);
// name of our event, look for it in events section of properties *STEP 3*
publicevent ButtonClickDelegate LabeledButtonClick;

And, our modified OnButtonClick:

protectedvirtualvoid OnButtonClick(object sender, EventArgs e)
{
// just to test the event
MessageBox.Show("You have just clicked you button");
//event with delegate arguments, this method will be
//implemented when you double click on event
LabeledButtonClick(sender, e); // LabeledButtonClick *STEP 4*
}

So remember, if you try to transfer events from one control to another:

Make a method with the same parameters as the event.

Add an event handler with this method to the control you are transferring from.

Make a delegate and an event for the component you are transferring to.

Fire an event in the method created in step 1.

If we build this now, we will see this in the event section:

And, whatever you implement in that function, it will be fired as you press the button. Here is the complete class:

How To Use It

Unpack the zip to some directory.

Make a new Windows Forms project and save it.

Follow the steps in these pictures...

Enjoy!

Points of Interest

I am using these kinds of components in my every day work. Labeled ComboBoxes, DateTimePickers, all kinds of stuff. As for me... it really saves a lot of time. I'd love to hear what you guys (or girls) think about this. Feedback is always appreciated.

Thanks

Greetz goes to Ralf Jansen - for providing the right event for adding the control.

History

24.X.2008 - v1.0

27.X.2008 - v1.1 - Fixed bug where event is added every time position method is fired, now event is added after creation of button

License

This article, along with any associated source code and files, is licensed under The MIT License

as it was written, this technique is for someone who doesnt use layouts. If you do, you have wrap label and your control with panel or groupbox etc. You can inherit from panel instead of TextBox, and place label inside panel when control is beign placed on it....

Much easier is to use IExtender provider than UserControl. Ready-to-use LabelProvider you can find here: http://blogs.vbcity.com/hotdog/archive/2006/06/01/6030.aspxMaybe you are be able to extend this control to draw frame around the caption and control?

Of course. You have to make even more work: Enabled, Visible etc. Purpose of this article is to get you started in this technique of writing visual components. I didnt wanted to make big article that covers all features needed to make enterprise-ready component, because it would be rather boring, and people who read this learns much more when they have to make something themselves.

In your [LabButtonEdit] class, in the [setControlsPosition()] method, you have this code:

_button.Click += new EventHandler(OnButtonClick);

What happens if the method is executed multiple times? Will you not have multiple event handler delegates attached to the button? And will not these multiple instances all fire/execute when the button is clicked?

I never really thought of making a control and a label in the same control. It probably is much easier to do the formatting.

But I thought that it could be nice to do a "table" container control that would format the label on row 1 and the control on row 2. That way, if a label becomes larger, the interface can resize pretty easily.

Just a quick formatting comment. The article is wider than a screen and requires horizontal scrolling. This is because several long lines in your code blocks. Add some carriage returns to improve this.