65.9K
CodeProject 正在变化。 阅读更多。
Home

通过反射进行自定义数据绑定

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.45/5 (12投票s)

2005年4月11日

2分钟阅读

viewsIcon

110298

downloadIcon

918

使用反射和自定义特性将数据绑定到 UI 元素。

Sample Image - data_binding_reflection.png

引言

您是否发现自己经常编写重复代码来将数据对象映射到 UI 元素? MSDN 上有很多文章介绍了不同的数据绑定方法,本文介绍了解决该问题的另一种可能的解决方案。我将通过使用反射和自定义特性来解决这个问题。

方法

我使用的方法需要使用自定义特性。这只是一个派生自 Attribute 的类,并且包含我们特定的使用特征。具体来说,我们将仅将此特性应用于数据对象类中的属性。 实际上,我们将一个特性应用于我们的派生特性,标识我们的自定义特性将仅以属性为目标,这是通过 AttributeUsage 特性完成的。 我已将此自定义特性定义为 UIMapAttributeUIMapAttribute 由两个属性组成,Index 是一个 int,UIMap 是一个 bool。 您可以使用 Boolean 属性来表示是否应在 UI 元素中呈现特定属性,并且 Index 属性允许您指定元素的呈现顺序。

实现

为了快速对 PropertyInfo 数组进行排序,我编写了一个实现 IComparer 的类,以便我可以评估每个应用于我们属性的 UIMapAttribute 中定义的 Index 属性。 在此示例中,我使用一个简单的 ArrayList 来保存数据对象集合。 Translate 方法完成了所有繁重的工作。 它首先遍历集合中的每个对象。 获得单个对象后,我请求其属性,这将返回一个 PropertyInfo 对象数组。 现在,我需要根据其特性中指定的内容来对这个 PropertyInfo 对象数组进行排序,快速调用 Array.Sort,传递来自特定对象的属性集合以及实现 IComparer 的比较类实例,即可执行请求的操作。 现在我有一个 PropertyInfo 对象数组,该数组基于 UIMapAttribute 中指定的标准进行排序,我只需要遍历每个 PropertyInfo 对象,确认其内容是否需要呈现到 UI 元素,然后继续到下一个对象。 这个特定的示例处理 ListView 控件,但是,在另一种类型的 UI 控件上实现它并不需要太多工作。 以下定义了我们应用于属性的 UIMapAttribute 类。

[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;}
    }
}

接下来是我们实现 IComparer 的类,我们将使用它来比较使用 UIMapAttribute 定义的 Index 属性。

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);
    }
}

最后是 Translate 方法,我们遍历对象并生成 ListViewItem 数组。

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;
}

如果我更改上面屏幕截图中使用的类,以便 Price 属性的 UIMapAttribute 为 false,您将看到我们不再将价格映射到 ListViewItem 中。

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;}
	}
}

请随意在下面发表评论。

© . All rights reserved.