运行时自定义PropertyGrid显示属性项目

运行时自定义PropertyGrid显示属性项目

简述

PropertyGrid所显示的属性内容包括属性分类(Category)及组件属性,

在一般情况下直接使用PropertyGrid来显示一个对象的所有属性是非常方便的,只需一个语句就能完成:

propertyGrid.SelectedObject = component;

但在实际应用中可能会不需要显示所有属性项目,而是通过外部指定(通过XML等进行描述),这些设置一般情况下在创建组件时用代码中的Attribute来进行具体设置,如所属分类,显示标题等,这只能针对于一些自建的组件可以这么做。

问题描述

像上面所说,在创建自建组件时可以用Attribute的方式来设置PropertyGrid的显示样式,但这种方法不能应用于已有的组件,像系统中的TextBox,Button等,除非自己建立一个由这些组件派生的类,当然这样做会加大复杂度。像要实现下面所显示的这种效果在实际操作时会很麻烦。

左图是TextBox原有的所有属性,右图是经过处理后的属性

 

解决方法

.Net中提供了一个自定义类型说明的接口(System.ComponentModel.ICustomTypeDescriptor),PropertyGrid可以直接自动处理用此接口生成的对象,因此在处理这个问题的时候只需要创建一个基于这个接口的处理类就可以达到世期望的目标,在这个接口中提供了GetProperties方法用于返回所选组件的所有属性,因此我们可以通过这个方法可以对我们所需要的属性进行过滤,下面是一段GetPropertys的处理代码:

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
      List<CustomPropertyDescriptor> tmpPDCLst = new List<CustomPropertyDescriptor>();
      PropertyDescriptorCollection tmpPDC = TypeDescriptor.GetProperties(mCurrentSelectObject, attributes);
      IEnumerator tmpIe = tmpPDC.GetEnumerator();
      CustomPropertyDescriptor tmpCPD;
      PropertyDescriptor tmpPD;
      while (tmpIe.MoveNext())
      {
        tmpPD = tmpIe.Current as PropertyDescriptor;
        if (mObjectAttribs.ContainsKey(tmpPD.Name))
        {
          tmpCPD = new CustomPropertyDescriptor(mCurrentSelectObject, tmpPD);
          tmpCPD.SetDisplayName(mObjectAttribs[tmpPD.Name]);
          //此处用于处理属性分类的名称,可以在XML等设置文件中进行设置,在这段代码中只是简单的在分类后加了中文两个字
          tmpCPD.SetCategory(tmpPD.Category + "中文");  
          tmpPDCLst.Add(tmpCPD);
        }
      }
      return new PropertyDescriptorCollection(tmpPDCLst.ToArray());
    }

当然在进行属性过虑之后,
PropertyGrid中所显示的属性名称都还是原有名称,若想同时改变在PropertyGrid中显示出来的名称则需要重写PropertyDescriptor中的部分方法,在上面这段代码中的CustomPropertyDescriptor就是一个基于PropertyDescriptor的类。

CustomPropertyDescriptor类中最主要的是重写DisplayNameCategory这两个属性,但由于在PropertyDescriptor中这两个属性是只读的,因此在这个类中需要加入两个用于设置这两个属性的方法(或直接用Field)在这里我使用了SetDispalyNameSetCategory这两个方法:

     
private string mCategory;
 
      public override string Category 
      { 
        get { return mCategory; } 
      } 
      private string mDisplayName ; 
      public override string DisplayName 
      { 
        get { return mDisplayName; } 
      } 
      public void SetDisplayName(string pDispalyName) 
      { 
        mDisplayName = pDispalyName; 
      } 
      public void SetCategory(string pCategory) 
      { 
        mCategory = pCategory; 
      } 
  就这样的几步,便可以将PropertyGrid中显示的内容完全自定义。

在写ICustomTypeDescriptor接口时,其他的一些方法可以用TypeDescriptor直接返回相关方法调用,并在GetPropertyOwner方法中应返回当前选择对象否则将不会对修改值起任何作用

   public object GetPropertyOwner(PropertyDescriptor pd) 
    {
       return mCurrentSelectObject; 
    }

  在写CustomPropertyDescriptor类时需要一个PropertyDescriptor对象,在实现一些方法时直接返回这个对象的值。

  当然也可以通过这个方法来自定义一些Events的输出,

使用方法 

      //加载组件属性,从XML文件载入,此处为Button
     
XmlNode tmpXNode = mXDoc.SelectSingleNode("Components/Component[@Name=\"Button\"]");
      //选择属性设置
      XmlNodeList tmpXPropLst = tmpXNode.SelectNodes("Propertys/Property");
      //创建CustomProperty对象
      CustomProperty cp = new CustomProperty(sender, tmpXPropLst);
      //设置PropertyGrid选择对象
      propertyGrid1.SelectedObject = cp;

文件下载
 

源码
演示

posted @ 2006-02-09 13:17 Pvistely 阅读(9607) 评论(23) 编辑 收藏

 回复 引用 查看   
#1楼2006-02-09 15:54 | 冬冬      
这种方法不错,我一般的做法是写一个xxxPerporty类,付给PropertyGrid.SelectObject属性。xxxPerporty类似一个VO,有空我把我的作法写出来,一起讨论一下,呵呵。

PS:搂主把代码整理一下吧……不敢恭维呀,呵呵

 回复 引用   
#2楼2006-02-09 18:19 | jzhye[未注册用户]
多谢,很实用,正为这范愁呢!
 回复 引用 查看   
#3楼[楼主]2006-02-09 19:24 | Pvistely      
@冬冬
哎,为了实现目的我不择手段呀,呵呵,

 回复 引用   
#4楼2006-03-31 10:30 | Phimath[未注册用户]
谢谢,解决了显示中文的问题!呵呵。
 回复 引用   
#5楼2006-05-14 17:13 | 曲滨[未注册用户]
2003 也可以用的吧!
 回复 引用   
#6楼2006-07-10 14:08 | flipcode[未注册用户]
最近正在用c#,需要自定义PropertyGrid这个东东,谢谢!
 回复 引用   
#7楼2006-09-01 16:45 | became[未注册用户]
正是我急需的内容
感谢了!!

 回复 引用   
#8楼2006-09-18 11:07 | Holala[未注册用户]
@冬冬


把你的运行时自定义PropertyGrid显示属性项目实现方法贴出来看看嘛

 回复 引用 查看   
#9楼[楼主]2006-09-19 10:14 | Pvistely      
@Holala
呵呵,他是为每个特定的OBJECT写一个xxxPerporty类

 回复 引用   
#10楼2007-07-31 10:42 | marking[未注册用户]
原来属性项目中的某一项是可以更改的,那如何设该置属性项目为只读?
 回复 引用 查看   
#11楼[楼主]2007-08-02 10:05 | Pvistely      
@marking
PropertyDescriptor.IsReadOnly

可以重写PropertyDescriptor类,并处理IsReadOnly属性

 回复 引用   
#12楼2007-12-25 09:40 | 九品仙[未注册用户]
终于解决了.感谢博主..
Wonderful !
I have been seeking this kind of paper, till I see this one !
Thanks so much :)
Happy spring festival !

 回复 引用   
#14楼2008-08-16 10:00 | Walnut[未注册用户]
太感谢了
 回复 引用   
#15楼2009-01-13 11:04 | entire[未注册用户]
试了一下demo,看起来应该是我要的东西
感谢博主!!!!!

 回复 引用   
#16楼2009-01-16 23:56 | entire[未注册用户]
已将类改为2003 VB版本,有需要的朋友请来信跟我索取
w_entire$163.com
再一次感谢博主的帮助!!!!

 回复 引用 查看   
#17楼2009-03-06 15:51 | wanghualiang      
非常感谢,很实用的内容!
 回复 引用 查看   
#18楼2009-04-09 21:05 | 寒蝉      
非常感谢,用上了。被这个东西折磨了一天了,再次感谢!
 回复 引用   
#19楼2009-06-17 10:12 | gattaca[未注册用户]
請問如何將屬性值存回XML?
 回复 引用   
#20楼2009-08-21 15:46 | qqqqq[未注册用户]
属性编辑后如何保存的呢?关闭窗口后,再次打开怎样显示编辑后的值呢》???
 回复 引用 查看   
#21楼2010-07-13 13:12 | Revive and Strive      
mCurrentSelectObject 上下文信息是什么?
 回复 引用 查看   
#22楼[楼主]2010-08-16 03:16 | Pvistely      
@Revive and Strive
当前选择对象,也就是当前设置propertygrid的选择对象

 回复 引用 查看   
#23楼2011-04-14 18:43 | 会长      
[DisplayName("1.区域名称")],[Browsable(false)]这两个特性也可以起作用。:-D