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

Custom Data Binding Through Reflection

, 12 Apr 2005
Rate this:
Please Sign up or sign in to vote.
Using reflection and a custom attribute to bind data to UI elements.

Sample Image - data_binding_reflection.png

Introduction

Do you ever find yourself writing duplicate code mapping data objects to UI elements? There are several articles available on MSDN that cover different approaches to data binding, this article covers yet another possible solution to the problem. I am going to attack this problem through the use of reflection and a custom attribute.

Approach

The approach that I am making requires the usage of a custom attribute. This is simply a class that derives from Attribute and contains our specific usage characteristics. Specifically we will be applying this attribute to properties within data object classes alone. We actually apply an attribute to our derived attribute identifying that our custom attribute will only target properties, this is done through the AttributeUsage attribute. I have defined this custom attribute to be called UIMapAttribute. UIMapAttribute consists of two properties, Index which is an int and UIMap which is a bool. You can use the Boolean property to signify whether or not the specific property should be rendered within the UI element, and the Index property allows you to specify the order in which the elements should be rendered.

Implementation

In order to making sorting of the PropertyInfo array quick I wrote a class that implements IComparer so I can evaluate the Index property defined in each UIMapAttribute applied to our properties. In this example I am using a simple ArrayList to hold a collection of data objects. The Translate method does all the hard work. It begins by iterating over each object within the collection. Once I have an individual object, I ask for its properties, which return an array of PropertyInfo objects. I now need to order this array of PropertyInfo objects according to what was specified within its attribute, a quick call to Array.Sort passing my collection of properties from the specific object along with an instance of a compare class that implements IComparer performs the requested operation. Now I have an array of PropertyInfo objects that are sorted based on the criteria specified in the UIMapAttribute, I simply need to iterate over each PropertyInfo object, confirm that it’s contents need to be rendered to the UI element and continue to the next object. This specific example deals with the ListView control, however it wouldn’t take much to implement this on another type of UI control.  The following is what defines the UIMapAttribute class we apply to our properties.

[AttributeUsage(AttributeTargets.Property)]
public class UIMapAttribute : Attribute
{
    public UIMapAttribute(bool bMap)
    {    
        UIMap = bMap;
    }

    public UIMapAttribute(bool bMap, int index)
    {
        UIMap = bMap;
        Index = index;
    }

    private bool maps;
    private int index;
    public bool UIMap
    {
        get{return maps;}
        set{maps = value;}
    }

    public int Index
    {
        get{return index;}
        set{index = value;}
    }
}

Next we have our class that implements IComparer, this is what we will us to compare the Index property defined with our UIMapAttribute.

class UIMapComparer : IComparer
{
    public int Compare(object x, object y)
    {
        PropertyInfo pix = x as PropertyInfo;
        PropertyInfo piy = y as PropertyInfo;

        if(pix == null)
            return -1;

        if(piy == null)
            return 1;
        
        object[] ciaa1 = pix.GetCustomAttributes(typeof(UIMapAttribute), true);
        object[] ciaa2 = piy.GetCustomAttributes(typeof(UIMapAttribute), true);

        if(ciaa1 == null || ciaa1.Length == 0)
            return -1;
        
        if(ciaa2 == null || ciaa2.Length == 0)
            return 1;

        UIMapAttribute uim1 = ciaa1[0] as UIMapAttribute;
        if(uim1 == null)
            return -1;

        UIMapAttribute uim2 = ciaa2[0] as UIMapAttribute;
        if(uim2 == null)
            return 1;

        return uim1.Index.CompareTo(uim2.Index);
    }
}

And last is the Translate method in which we iterate over the objects and generate the ListViewItem array.

public static ListViewItem[] Translate(ArrayList collection)
{
	UIMapComparer comparer = new UIMapComparer();
	int count = collection.Count;
	ListViewItem[] lvia = new ListViewItem[count];
	for(int i = 0; i < count; i++)
	{
		object item = collection[i];
		if(item != null)
		{
			lvia[i] = new ListViewItem();
			PropertyInfo[] pia = item.GetType().GetProperties();
			
			// Sort properties
			Array.Sort(pia, comparer);
		
			if(pia != null)
			{
				object[] caa = pia[0].GetCustomAttributes(typeof(UIMapAttribute), true);
				if(caa != null && caa.Length > 0)
				{		
					UIMapAttribute uim = caa[0] as UIMapAttribute;
					if(uim != null  && uim.UIMap == true)
					{	
						// Set the Text Property of the ListViewItem
						object objText = pia[0].GetValue(item, null);
						lvia[i].Text = objText.ToString();
					}
				}

				// iterate over the remaining properties and set the SubItem
				if(pia.Length > 1)
				{
					for(int j = 1; j < pia.Length; j++)
					{
						object[] sicaa = pia[j].GetCustomAttributes(
										typeof(UIMapAttribute), true);
						if(sicaa != null && sicaa.Length > 0)
						{
							UIMapAttribute siUim = sicaa[0] as UIMapAttribute;
							if(siUim != null && siUim.UIMap == true)
							{
								object siText = pia[j].GetValue(item, null);
								if(siText != null)
								      lvia[i].SubItems.Add(siText.ToString());
							}
						}
					}
				}
				
			}
		}
	}
	return lvia;
}

If I change the class that I used in the above screenshot so the UIMapAttribute of the Price property is false, you will see that we no longer map the price into the ListViewItem's.

class food
{
	private string item;
	private string category;
	private double price;

	[UIMap(true, 1)]
	public string Item
	{
		get{return item;}
		set{item = value;}
	}

	[UIMap(true, 2)]
	public string Category
	{
		get{return category;}
		set{category = value;}
	}

	[UIMap(false, 3)]
	public double Price
	{
		get{return price;}
		set{price = value;}
	}
}

Please feel free to leave comments below.

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

Share

About the Author

Nick Parker
Software Developer (Senior)
United States United States
Nick graduated from Iowa State University with a B.S. in Management Information System and a minor in Computer Science. Nick works for Zetetic.
 
Nick has also been involved with the Iowa .NET User Group since it's inception, in particular giving presentations over various .NET topics. Nick was awarded the Visual C# MVP award from Microsoft for four years in a row.
 
In his mystical spare time he is working on a development project called "DeveloperNotes" which integrates into Visual Studio .NET allowing developers easy access to common code pieces. He is also a fan of using dynamically typed languages to perform unit testing, not to mention how he loves to talk about himself in the third person.

Comments and Discussions

 
GeneralRe: Makes it easy, yes, BUT! PinmemberMaksimP12-May-05 4:44 
GeneralRe: Makes it easy, yes, BUT! Pinmemberrune@vrk.dk15-Jul-05 3:48 
GeneralRe: Makes it easy, yes, BUT! PinmemberJohn Cardinal17-May-06 10:54 
GeneralNice Article PinmemberJavier Lozano11-Apr-05 17:25 
GeneralRe: Nice Article PinprotectorNick Parker11-Apr-05 19:02 
GeneralRe: Nice Article PinmemberBrickChen19-Apr-05 21:43 

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
Web04 | 2.8.1411023.1 | Last Updated 12 Apr 2005
Article Copyright 2005 by Nick Parker
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid