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

简述 

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

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

propertyGrid.SelectedObject = component; 

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

问题描述 

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

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

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

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


  

解决方法 

在.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()) 
         { 
           tmpCPD = new CustomPropertyDescriptor(mCurrentSelectObject, tmpPD); 
           tmpCPD.SetDisplayName(mObjectAttribs[]); 
           //此处用于处理属性分类的名称,可以在XML等设置文件中进行设置,在这段代码中只是简单的在分类后加了“中文”两个字 
           tmpCPD.SetCategory(tmpPD.Category + "中文");   
           tmpPDCLst.Add(tmpCPD); 
         } 
       } 
       return new PropertyDescriptorCollection(tmpPDCLst.ToArray()); 
     }

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

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

   

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;