Click here to Skip to main content
15,891,253 members
Articles / Programming Languages / C#
Tip/Trick

Extension Methods for Multiple Control Tags

Rate me:
Please Sign up or sign in to vote.
4.85/5 (7 votes)
28 Oct 2018CPOL2 min read 4.8K   5   2
The Control.Tag property is meant to contain data closely related to the control. Let's extend it.

The objective of the extension methods provided in this tip is to facilitate the storage, manipulation, and retrieval of multiple values in the Tag property of a Control object.

The Code

The extension methods InitTag, GetTag, and SetTag are implemented as shown below:

C#
internal static void InitTag( this Control control, params object[] objects )
{
    control.Tag = objects;
}

internal static object GetTag( this Control control, int index )
{
    if( control.Tag.GetType() != typeof( object[] ) )
    {
        return null;
    }

    var objects = control.Tag as object[];

    if( index < 0
        || index >= objects.Length )
    {
        return null;
    }

    return objects[ index ];
}

internal static void SetTag( this Control control, int index, object value )
{
    if( control.Tag.GetType() != typeof( object[] ) )
    {
        return;
    }

    var objects = control.Tag as object[];

    if( index < 0
        || index >= objects.Length )
    {
        return;
    }

    objects[ index ] = value;
}

Their use is as follows:

C#
listview1.InitTag( SortOrder.Ascending, @"c:\Program Files (x86)\" );

var sortOrder = listview1.GetTag( 0 ) as SortOrder;
var path      = listview1.GetTag( 1 ) as string;

listview1.SetTag( 1, @"c:\Program Files\" );

Godspeed.

The Rant

The Tag property of the Control class is often used to store individual values.

C#
listview1.Tag = @"c:\Program Files (x86)\";
listview2.Tag = SortOrder.Ascending;

It is also used to store (references to) objects.

C#
class Chair
{
    public int  legs;
    public bool backrest;
}

var chair = new Chair();

chair.legs     = 3;
chair.backrest = false;

textbox1.Tag   = chair;

Retrieving the value from the Tag property nearly always involves a cast since the Tag property is of type Object.

C#
var path      = listview1.Tag as string;
var sortOrder = listview2.Tag as SortOrder;
var chair     = textbox1.Tag as Chair;

In order to store multiple values or objects in a single Tag, a collection is required.

C#
var integers = new List<int>(){ 0, 1, 2, 3, 4 };

listbox.Tag  = integers;

var numbers  = listbox.Tag as List<int>;

Often times, the collection itself contains values and objects of various types. This, in turn, requires additional casts.

C#
listview1.Tag = new object[]{ 0, "tin", false };

var bucket    = listview1.Tag as object[];

var holes     = bucket[ 0 ] as int;
var material  = bucket[ 1 ] as string;
var leaks     = bucket[ 2 ] as bool;

bucket[ 2 ]   = true;

Using the extension methods provided at the top of the page, the code above becomes the code below:

C#
listview1.InitTag( 0, "tin", false );

var holes    = listview1.GetTag( 0 ) as int;
var material = listview1.GetTag( 1 ) as string;
var leaks    = listview1.GetTag( 2 ) as bool;

listview1.SetTag( 2, true );

A bit neater.

Caveats

Potential misuse is all too easy. One wrong cast can quickly upset the state of an otherwise pretty program. This is a necessary "fault" of the Tag property itself, one that naturally increases in complexity when storing multiple values.

Considerations and Alternative Implementations

I prefer to use an object[] and thus state in the InitTag method the amount and the type of values the Tag is to contain. This is the simplest approach, in my view, as it makes remembering the index (and type) of each element easier, thus minimizing somewhat unexpected misuse.

However, interesting alternatives exist.

Rewriting these extension methods to use a List<object> instead of an object[] adds flexibility, albeit at the cost of reliability. Remembering the index (and type) of each possibly changing element can become cumbersome.

A Dictionary can also be used, with Keys perhaps being strings or enums. This approach circumvents the indexing problem and allows for very large collections of items. The problem now becomes one of remembering the Keys. Although enums go a long way to safeguard against a weak memory, the casting issue remains.

The casting issue can be more or less addressed via a parallel dictionary (containing the type of each element) or multiple extensions methods (requesting each type explicitly with a prayer). However, I shun these approaches because the amount of additional code required makes the use of the Tag property overly cumbersome.

Kudos

I was motivated to share my code after reading this tip by . Check it out.

License

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


Written By
Japan Japan
Louis Armstrong, Count Basie, Chick Corea, Miles Davis, Benny Goodman, Spyro Gyra, Dizzy Gillespie, Keith Jarrett, Leroy Jenkins, Yusef Lateef, Al Di Meola, Glenn Miller, McCoy Tyner, Cecil Taylor, John Coltrane, Duke Ellington, Bill Evans, Ella Fitzgerald, Jean-Luc Ponty, John McLaughlin, Fats Navarro, Tito Puente, Paul Whiteman, Sun Ra, Caravan, Joe Farrell, Paco de Lucia, Weather Report, Charles Mingus, Pat Metheny, Charlie Parker, Charlie Byrd, Mahavishnu Orchestra, Wynton Marsalis, Return to Forever, Julien Loureau, Thelonious Monk, Max Roach , Pharaoh Sanders, Albert Ayler, Ornette Coleman, Sidney Bechet,...

Comments and Discussions

 
GeneralMy vote of 5 Pin
FernandoUY28-Oct-18 12:52
professionalFernandoUY28-Oct-18 12:52 
GeneralRe: My vote of 5 Pin
Franc Morales28-Oct-18 13:13
Franc Morales28-Oct-18 13:13 

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

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