Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Adding Functionality To .NET Controls

0.00/5 (No votes)
30 Jul 2014 1  
Two ways to add functionality to .NET controls

Introduction

At times, I find myself building extra functionality around standard .NET controls, but adding that functionality litters my application code with what are essentially bookkeeping functions with which I shouldn't have to concern myself. Additionally, I find myself repeatedly adding the same functionality into different applications. So I eventually decided to explore ways of adding functionality to the controls themselves, and sharing them between applications.

Background

I found two basic ways to accomplish what I want to do. The first is by extending the .NET Label control, and the second is to build my own control that inherits from the .NET Label control. Each method has its strengths, so let's look at them one at a time.

I wrote my code in C# .NET, using Visual Studio 2010.

Using the Code

For this example, I will be adding some basic functionality to a simple control, but the principles extend much farther than this example. In this case, I'll be modifying the .NET Label class to add the ability to fade the text out after a specified number of seconds.

Let me begin by making clear what I do NOT want to do. I do not want to create a custom or user control, nor do I want to create a brand new control from scratch, because those would not be of type Label. Why recreate the wheel and go to the trouble of creating a new Label control, especially when I can retain the existing Label control and let it do all of the heavy lifting? All I want to do is layer my new functionality on top of the existing control.

The first method of accomplishing this is by adding an extension method to the Label class. Basically, the new method is added directly onto the control's functionality whenever the appropriate using statement is placed in the application code.

We start by setting up the namespace, then declaring the static class. Note that the extension class must be static, as must all the functions within it. The code below extends the .NET Label class to include the ShowFadeText() method.

namespace ControlExtensions
{
    public static class LabelExtension
    {
        // Extension method for the .NET Label class. The first argument shows which class is
        // being extended, the second contains the label text, and the third indicates how long
        // the text should display before disappearing. The method sets the text and then creates
        // and starts a timer for the Fadeout functionality.
        public static void ShowFadeText(this Label lbl, String text, int seconds)
        {
            lbl.Text = text;
            lbl.Visible = true;

            Timer tmr = new Timer();
            tmr.Interval = seconds * 1000;
            tmr.Tick += new EventHandler(tmr_Tick);
            tmr.Tag = lbl; // Stores the lbl as an Object in the Timer
            tmr.Start();
        }

        // Tick event for the timer declared in the ShowFadeText() method... the specific
        // Label control being referenced is stored in the tmr.Tag property as an Object.
        static void tmr_Tick(object sender, EventArgs e)
        {
            Timer tmr = ((Timer)sender);
            Label lbl = ((Label)tmr.Tag); // Retrieves the lbl control from the Timer
            lbl.Text = "";
            tmr.Stop();
        }
    }
}

Notice the first parameter of the static ShowFadeText() method... it indicates what class the extension method applies to. It also allows the control to be addressed within the extension method. So we first set the text and make sure the control is visible, then we configure and kick off a timer for the number of seconds specified in the final parameter.

The timer method was a bit tricky, because it has to reference the label control... the specific label that invoked the extension method. We can't simply save the Label to a private class variable, because this is a static class, and this wouldn't support multiple fading Labels at the same time. To do this, we have to pass the Label into the tmr_Tick handler somehow, and to do that I give you the Tag property. So, inside the ShowFadeText() method, before we kick off the timer, we set the Tag to the label control.

Inside the tmr_Tick event handler, we extract the timer control from the sender parameter, and then we extract the label control from the tmr.Tag. Finally, we set the Label to an empty string and stop the timer.

When it comes to using this extension method, the first step is to make sure the application code is referencing the namespace that contains the extension method.

using ControlExtensions;

Without that line, the extension method is not implemented and is not available. But with that line, all you need to do is drop a label onto the form and then call the extension method as follows:

label1.ShowFadeText("This text should fade out in 3 seconds...", 3);

This method accomplishes the goal for adding this simple functionality, but it does have some limitations. For example, though we can create extension methods, we can't create extension properties or events. We also can't override existing methods, so it's not the most flexible solution... we can't CHANGE the behavior of the Label control this way, we can only add to it.

That's why I had to create a new method to set the fade text, instead of just using the Label.Text property... using an extension method, I can't change the Text property, I can only add new methods.

A more robust solution is to create a new control that inherits from the Label control, and place the new functionality there. The code below is the entire code needed to implement the custom Label control.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MyLib
{
    namespace Controls
    {
        public class MyLabel : Label
        {
            // Internal variables
            private int? timeLimit;  // timeLimit is accessed by property TimeLimit
            private Timer tmr;

            // Public property to store the display time prior to fade. If the TimeLimit is
            // not null and is > 0, the Interval value for the timer is set.
            public int? TimeLimit
            {
                get { return timeLimit; }
                set
                { 
                    timeLimit = value;
                    if (timeLimit != null)
                        tmr.Interval = (int)TimeLimit * 1000;
                }
            }

            // Constructor for the new class... it initialized the fadeout Timer
            public MyLabel()
            {
                TimeLimit = null;

                tmr = new Timer();
                tmr.Tick += new EventHandler(tmr_Tick);
                tmr.Tag = this;
            }

            // Overriding the base Text property in the .NET Label control. Notice it saves to
            // and gets from the base.Text property. It also adds code to fire the timer during
            // a Text set.
            public override String Text
            {
                get { return base.Text; }
                set
                {
                    base.Text = value; 
                    if (TimeLimit != null && TimeLimit > 0) 
                        tmr.Start();
                }
            }

            // Timer tick event to fade out text when fired
            void tmr_Tick(object sender, EventArgs e)
            {
                Timer tmr = ((Timer)sender);
                this.Text = ""; 
                tmr.Stop();
            }
        }
    }
}

Unlike with the first example, in this case I didn't create a custom method to display the text and fade it out. In this case, the code will fire the timer IF the TimeLimit property has a value greater than 0 when the MyLabel.Text property is set.

The code adds three public members:

  1. A property named TimeLimit that stores a nullable integer representing the number of seconds for the label to display before it disappears.
  2. A public constructor for MyLabel control.
  3. A public property named Text which overrides the base Label Text property.

When the text is set in the application code, the override property fires the timer, which is set for the number of seconds provided in the TimeLimit property. If the TimeLimit property is null or 0, then it doesn't fade. Using the new functionality looks like this:

myLabel1.TimeLimit = 5;

myLabel1.Text = "This label will fade in 5 seconds.";

This is a better solution all the way around. The MyLabel control can be placed into the ToolBox and dropped onto a form, or it can be invoked programmatically just like a Label. And as you saw with the Text property, we can override existing methods and properties, as well as add methods, properties, and events. The new properties can be referenced via the properties window in the Form Designer, as can the events.

One possible enhancement to this class, for example, would be to add a new event to it... name it the FadeoutComplete event, which fires when the text disappears... the tmr_Tick event after the text is cleared. In this way, the user is notified when the text fade is finished.

Points of Interest

I set out looking for one way to accomplish my goal, but ended up with two... life is funny like that. The extension method solution works well for a small change to the control, one that can be implemented by adding methods. Creating a new Label type that inherits from the .NET Label control, however, allows for much more versatility in creating greatly enhanced controls.

History

  • 07-28-2014 - Article originally posted
  • 07-28-2014 - Added source code and demo program for download

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here