Click here to Skip to main content
Click here to Skip to main content

Customizing a Component: Inheriting vs. IExtenderProvider

, 2 Jan 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
Inheriting vs. IExtenderProvider when customizing a component.

Sample Image - Inherit_vs_Provider.jpg

Introduction

I had to add a 'maximum number of rows' (called MaxRows) property to a DataGridView control recently, and came across two ways of handling this problem. Either of which is overly complicated, but none-the-less I thought I'll share my findings.

In the example code, I provided two grids on two different forms (the reason will be explained later), both with the same customized property (MaxRows) that does exactly the same thing, but done in different ways.

Approaches

I will elaborate on the two approaches first, before going into detail (well, sort of) with the code. I feel this is more of a software design issue rather than a coding issue.

IExtenderProvider

My first approach was using the IExtenderProvider interface (which lives under the System.ComponentModel namespace). IExtenderProvider is a component which attaches itself to other components, like a parasite. The ErrorProvider control is an example of IExtenderProvider, where you add it to your design form and new properties will appear in the property panel for the specified controls.

The good thing about using IExtenderProvider is that it can attach properties to more than one control at a time (e.g., can attach itself to buttons, panels, grids...).

By dropping the ExtenderProvider component on to your form, it will attach itself to all the specified controls. Hence we have two different forms for this example; otherwise, IExtenderProvider will attach to my Inherit Control example as well.

One thing I noticed with IExtenderProvider: it seems to be a lot of tedious code just to add one property to a control.

Inherit Control

My second approach is inheriting from a DataGridView control and creating my own custom DataGridView control. This I found to be a much easier and cleaner way for adding new properties to a control. There wasn't much code either. Actually, it is extremely easy.

But if you are already quite far into a project, and/or doing maintenance programming, this might not be the best approach, as you'll have to change all the controls on all the forms or base forms manually. Hence, designing your software project from the start with inherited components will help overcome these issues down the track.

Verdict

Adding custom properties to a control? Inheriting is the best approach in my opinion. The IExtenderProvider interface seems more like parasite code that attaches itself to controllers than well structured code. But by saying that, if you want to create a property that affects many controls in a container, this would be an okay approach.

I still think creating your own base controls to start with will ease headaches in the long term. Especially on GUI demanding projects.

To add new properties to your controls will be a breeze afterwards as well, as all your controls will update as soon as your base gets a new property.

Remember to make a default value (like -1) that does nothing.

Plus visually, inheriting looks better in the VS Property Panel as seen in the figure above.

The Code

I found creating an inherited control a much cleaner and less painful experience than creating a property via the IExtenderProvider interface.

Let's look at the code for both techniques. Straight away, one can see the difference in the amount of code between the two techniques. Remember, they do exactly the same thing.

Inheriting

Inherit from the DataGridView control by adding System.Windows.Forms.DataGridView after your class name.

namespace CodeProject_Inherit_Vs_IExtProv
{
    public partial class InheritDataGrid : System.Windows.Forms.DataGridView

Now we've inherited from a DataGridView called InheritDataGrid. It's now time to add our MaxRow property.

Set the [DefaultValue], [Category], and [Description] attributes. These are the display values that appear on the property panel in VS. The attributes are quite self-explanatory.

Add a public property called MaxRows. This will get, set the maximum rows for our InheritDataGrid control by connecting to an event that will trigger the CheckMaxRows method.

The CheckMaxRows method will check how many rows are currently shown, and will disable adding rows when the set value is reached. The default property '-1' will give you unlimited rows to add.

private int maxRows = -1;

[DefaultValue(-1)]
[Category("Data")]
[Description("Set the maximum number of rows for DataGrid")]
public int MaxRows
{
    get { return maxRows; }
    set 
    {
        base.RowValidated += 
          new DataGridViewCellEventHandler(CheckMaxRows);
        this.maxRows = value; 
    }
}

//Grid enter handler
void CheckMaxRows(object sender, EventArgs e)
{
    DataGridView grid = (DataGridView)sender;
            
    if ((grid.RowCount > MaxRows) && (MaxRows != -1))
        grid.AllowUserToAddRows = false;
    else
        grid.AllowUserToAddRows = true;
}

That's it for inheriting...

IExtenderProvider

The foundation of InheritDataGrid. One can pretty much use this as a template. Inherit from System.ComponentModel.Component and add the IExtenderProvider interface to the public class reference. The ProvideProperty attribute specifies the name of the property and the type of the component it should latch onto. Use Hashtable to track instances of controls.

The IExtenderProvider.CanExtend method has to be set for IExtenderProvider to know which controls to infect.

Then create the properties and ensure the property by calling EnsurePropertiesExists.

namespace CodeProject_Inherit_Vs_IExtProv
{
    [ProvideProperty("MaxRows", typeof(DataGridView))]
    public class ExtenderProviderDataGrid : 
           System.ComponentModel.Component, IExtenderProvider
    {
        private Hashtable properties;
        private System.ComponentModel.Container components = null;

        public ExtenderProviderDataGrid()
        {
            properties = new Hashtable();
        }

        public ExtenderProviderDataGrid(
               System.ComponentModel.IContainer container): this()
        {
            container.Add(this);
        }

        bool IExtenderProvider.CanExtend(object ctrl)
        {
            return (ctrl is DataGridView);
        }

        private class Properties
        {
            public int MaxRows;

            public Properties()
            {
                MaxRows = -1;
            }
        }

        private Properties EnsurePropertiesExists(object key)
        {
            Properties p = (Properties)properties[key];

            if (p == null)
            {
                p = new Properties();

                properties[key] = p;
            }            
            return p;
        }

Now I add my logic to the code. Create the Get-Set methods. 'Get' + add the ProvideProperty name = GetMaxRows method. Same for 'Set', which equals SetMaxRows. Add a default value to SetMaxRows that does nothing and add your new functionality to an event.

Set the [DefaultValue], [Category], and [Description] attributes. These are the display values that appear on the property panel in VS. The attributes are quite self-explanatory.

[DefaultValue(-1)]
[Category("Data")]
[Description("Set the maximum number of rows for DataGrid")]
public int GetMaxRows(DataGridView g)
{
    return EnsurePropertiesExists(g).MaxRows;
}

public void SetMaxRows(DataGridView g, int value)
{
    if ((value == -1) || (value > 0))
        EnsurePropertiesExists(g).MaxRows = value;

    g.RowValidated += new DataGridViewCellEventHandler(CheckMaxRows);

    g.Invalidate();
}

//Grid enter handler
void CheckMaxRows(object sender, EventArgs e)
{
    DataGridView grid = (DataGridView)sender;

    Properties ctrlProperties;
    ctrlProperties = (Properties)properties[(grid as Control)];

    if ((grid.RowCount > ctrlProperties.MaxRows) && 
                   (ctrlProperties.MaxRows != -1))
        grid.AllowUserToAddRows = false;
    else
        grid.AllowUserToAddRows = true;
}

Please note that I did use IExtenderProvider in a limited capacity for this example. For a more in-depth and well-written article, please view Getting to know IExtenderProvider by James T. Johnson.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Johan Vorster
Software Developer
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
JokeThanx ^^ PinmemberDong-Ho KIM3-Jan-08 20:43 
GeneralRe: Thanx ^^ PinmemberBJ Vorster3-Jan-08 21:06 
GeneralA note/question about your code [modified] PinmemberIlíon3-Jan-07 3:55 
GeneralRe: A note/question about your code PinmemberJohan Vorster3-Jan-07 4:04 
GeneralRe: A note/question about your code PinmemberIlíon3-Jan-07 4:09 
GeneralIExtenderProvider is a component which attaches it self to other components, like a parasite PinmemberIlíon3-Jan-07 3:37 
AnswerRe: IExtenderProvider is a component which attaches it self to other components, like a parasite PinmemberJohan Vorster3-Jan-07 3:55 
GeneralRe: IExtenderProvider is a component which attaches it self to other components, like a parasite PinmemberIlíon3-Jan-07 4:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.150327.1 | Last Updated 3 Jan 2007
Article Copyright 2007 by Johan Vorster
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid