当前位置:首页 > C#编程 > C#/.net框架 > 正文内容

PropertyGrid控件动态生成属性及下拉菜单

秋天2年前 (2022-05-09)C#/.net框架1126

 

PropertyGrid,.net框架下的一个控件,这是一个软件升级的项目,原来的软件用的是C++,控件用的还是第三方,这次升级到visual studio .net4.0版本,原以为.net的东西用起来不会费劲的,没想到想要实现项目需要的效果还真没那么简单。

 

由于需要,我这里主要是为了能动态的生成属性页,还要带能动态生成下来菜单,所以今天主要从这方面总结。

 

首先定义一个属性类:

//单条属性类

C#

public class XProp {       private string theId = ""; //属性Id,我的项目中需要,大家可以忽略       private string theCategory = ""; //属性所属类别       private string theName = "";     //属性名称       private bool theReadOnly = false;  //属性的只读性,true为只读       private string theDescription = ""; //属性的描述内容       private object theValue = null;    //值       private System.Type theType = null; //类型       private bool theBrowsable = true;  //显示或隐藏,true为显示       TypeConverter theConverter = null;  //类型转换       public string Id       {           get { return theId; }           set { theId = value; }       }       public string Category       {           get { return theCategory; }           set { theCategory = value; }       }       public bool ReadOnly       {           get { return theReadOnly; }           set { theReadOnly = value; }       }       public string Name       {           get { return this.theName; }           set { this.theName = value; }       }       public object Value       {           get { return this.theValue; }           set { this.theValue = value; }       }       public string Description       {           get { return theDescription; }           set { theDescription = value; }       }       public System.Type ProType       {           get { return theType; }           set { theType = value; }       }       public bool Browsable       {           get { return theBrowsable; }           set { theBrowsable = value; }       }       public virtual TypeConverter Converter       {           get { return theConverter; }           set { theConverter = value; }       } }

我举一个例子:

C#

private string strdemo; [DescriptionAttribute("用于举例说明"),  CategoryAttribute("公有属性"), DefaultValueAttribute(“测试属性”), ReadOnlyAttribute(false), BrowsableAttribute(true),  TypeConverter(typeof(MyComboTypeConvert)) ]  public string strDemo   {           get { return strdemo; }           set { strdemo = value; }  }

这是个写死的属性,那在我的项目中,根据对象的不同,会需要生产不同的属性页,所以需要一个可以动态生成的属性页,将上述这个一般属性定义,利用XProp类,写成:

C#

Private XProp newXpro = new XProp(); newXpro.Category = ”公有属性”; newXpro.Name = ” strDemo”; newXpro.Id = "A"; newXpro.Description = “用于举例说明”; newXpro.ReadOnly =false; newXpro.Value = “测试属性”; newXpro.ProType = typeof(string); newXpro.Browsable = true; newXpro.Converter = null;

这样,一条属性就完成了。当然你也可以根据需要自己重写更多的属性相关定义。这里的Converter属性是在后面的下拉菜单中需要用到的,如果不是基础类型的(string,int,bool,enum等),我们可以赋值为null.

 

当然,这只是一条属性,原本按之前的方法,只要定义一个类,然后这个类里面定义多条属性就可以了。但是现在,由于属性是动态生成的,我们并不能确定需要几个属性,也就不能直接在一个类里面定义完。

 

所以我们需要再定义一个List<XProp>类,作为一一张list可以随意添加多个项,然后将PropertyGrid.SelectedObject设置为这个类就好了:

先来定义以下两个类:

C#

public class XProps : List<XProp>, ICustomTypeDescriptor {     #region ICustomTypeDescriptor 成员       public AttributeCollection GetAttributes()     {         return TypeDescriptor.GetAttributes(this, true);     }       public string GetClassName()     {         return TypeDescriptor.GetClassName(this, true);     }       public string GetComponentName()     {         return TypeDescriptor.GetComponentName(this, true);     }       public TypeConverter GetConverter()     {         return TypeDescriptor.GetConverter(this, true);     }       public EventDescriptor GetDefaultEvent()     {         return TypeDescriptor.GetDefaultEvent(this, true);     }       public PropertyDescriptor GetDefaultProperty()     {         return TypeDescriptor.GetDefaultProperty(this, true);     }       public object GetEditor(System.Type editorBaseType)     {         return TypeDescriptor.GetEditor(this, editorBaseType, true);     }       public EventDescriptorCollection GetEvents(System.Attribute[] attributes)     {         return TypeDescriptor.GetEvents(this, attributes, true);     }       public EventDescriptorCollection GetEvents()     {         return TypeDescriptor.GetEvents(this, true);     }       public PropertyDescriptorCollection GetProperties(System.Attribute[] attributes)     {         ArrayList props = new ArrayList();         for (int i = 0; i < this.Count; i++)         {  //判断属性是否显示             if (this[i].Browsable == true)             {                 XPropDescriptor psd = new XPropDescriptor(this[i], attributes);                 props.Add(psd);             }         }         PropertyDescriptor[] propArray = (PropertyDescriptor[])props.ToArray(typeof(PropertyDescriptor));         return new PropertyDescriptorCollection(propArray);      }       public PropertyDescriptorCollection GetProperties()     {         return TypeDescriptor.GetProperties(this, true);     }       public object GetPropertyOwner(PropertyDescriptor pd)     {         return this;     }       #endregion       public override string ToString()     {         StringBuilder sb = new StringBuilder();         for (int i = 0; i < this.Count; i++)         {         sb.Append("[" + i + "] " + this[i].ToString() + System.Environment.NewLine);         }         return sb.ToString();     } }   private class XPropDescriptor : PropertyDescriptor {     XProp theProp;      public XPropDescriptor(XProp prop, Attribute[] attrs): base(prop.Name, attrs)     {         theProp = prop;     }       public override bool CanResetValue(object component)     {         return false;     }     public override string Category     {         get { return theProp.Category; }     }       public override string Description     {         get { return theProp.Description; }     }       public override TypeConverter Converter     {         get { return theProp.Converter; }     }       public override System.Type ComponentType     {         get { return this.GetType(); }     }       public override object GetValue(object component)     {         return theProp.Value;     }       public override bool IsReadOnly     {         get { return theProp.ReadOnly; }     }       public override System.Type PropertyType     {         get { return theProp.ProType; }     }       public override void ResetValue(object component)     {     }       public override void SetValue(object component, object value)     {         theProp.Value = value;     }       public override bool ShouldSerializeValue(object component)     {         return false;     } }

然后我们新声明一个属性列表:
Private XProps xprops = new XProps();
再将刚刚那个动态生成的属性添加进去,将属性页的selectobject赋值为这个类:
xprops.add(newXpro);
PropertyGridDemo.SelectedObject = xprops;
现在我们来看看效果:

image.png

 

根据需要,你可以添加多条属性。

也许你会想问,这里哪有动态生成啊,那些属性名称,属性类型什么的不还是写死的么。呵呵,我这里只是举个例子所以写死了,在实际应用中,你可以根据需要,在生成属性列表的时候,动态赋值,例如你是从xml文件里读取到的,例如你是从服务器中获取到的。根据你读取的对象,一个一个赋值就可以了。

 

下面来介绍如何在属性页中动态生成下拉菜单框,关于下拉菜单真的是很复杂,在.net的PropertyGrid控件中,想要具有下拉菜单的属性,简单的可以通过枚举类型来实现,还是以刚刚的那个例子来说明:
首先定义个枚举类型:

C#

public enum enumType {        BOOLVAL,        DIGITALVAL,        STRINGVAL,        CHECKVAL,        RATIOVAL,        IPVAL,        COMBOBOX,        RESETBTN }


然后代码中将属性值与属性类型修改为:
newXpro.Value  = CustomClass.enumType.BOOLVAL;  //这里的属性值当然必须为枚举中的某一项
newXpro.ProType = typeof(enumType);
然后我们来看看实现的效果:

image.png

这就是最简单的下拉菜单的实现方式了。
但是这里的枚举类型仍然是需要代码中写死的,而我在网上也所搜了很久,枚举类型似乎没办法动态生成,如果要实现动态生成恐怕要另寻他途。
所幸的是,我曾经见过重写combobox来生成属性页的下来菜单的,我们需要定义:

C#

//重写下拉菜单中的项,使之与属性页的项关联         public abstract class ComboBoxItemTypeConvert : TypeConverter         {             public Hashtable myhash = null;             public ComboBoxItemTypeConvert()             {                 myhash = new Hashtable();                 GetConvertHash();             }             public abstract void GetConvertHash();                         //是否支持选择列表的编辑             public override bool GetStandardValuesSupported(ITypeDescriptorContext context)             {                 return true;             }             //重写combobox的选择列表             public override StandardValuesCollectionGetStandardValues(ITypeDescriptorContext context)             {                 int[] ids = new int[myhash.Values.Count];                 int i = 0;                 foreach (DictionaryEntry myDE in myhash)                 {                     ids[i++] = (int)(myDE.Key);                 }                 return new StandardValuesCollection(ids);             }             //判断转换器是否可以工作             public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)             {                 if (sourceType == typeof(string))                 {                     return true;                 }                 return base.CanConvertFrom(context, sourceType);             }             //重写转换器,将选项列表(即下拉菜单)中的值转换到该类型的值             public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object obj)             {                 if (obj is string)                 {                     foreach (DictionaryEntry myDE in myhash)                     {                         if (myDE.Value.Equals((obj.ToString())))                             return myDE.Key;                     }                 }                 return base.ConvertFrom(context, culture, obj);             }             public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)             {                 if (destinationType == typeof(string))                 {                     return true;                 }                 return base.CanConvertTo(context, destinationType);             }             //重写转换器将该类型的值转换到选择列表中             public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object obj, Type destinationType)             {                 if (destinationType == typeof(string))                 {                     foreach (DictionaryEntry myDE in myhash)                     {                         if (myDE.Key.Equals(obj))                             return myDE.Value.ToString();                     }                     return "";                 }                 return base.ConvertTo(context, culture, obj, destinationType);             }             public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)             {                 return false;             }         } //重写下拉菜单,在这里实现定义下拉菜单内的项 public class MyComboItemConvert : ComboBoxItemTypeConvert         {             private Hashtable hash;             public override void GetConvertHash()             {                 try                 {                     myhash = hash;                 }                 catch                 {                     throw new NotImplementedException();                 }             }             public MyComboItemConvert(string str)             {                 hash = new Hashtable();                 string[] stest = str.Split(',');                 for (int i = 0; i < stest.Length; i++)                 {                     hash.Add(i, stest[i]);                 }                 GetConvertHash();                 value = 0;             }             public int value { get; set; }             public MyComboItemConvert(string str,int s)             {                 hash = new Hashtable();                 string[] stest = str.Split(',');                 for (int i = 0; i < stest.Length; i++)                 {                     hash.Add(i, stest[i]);                 }                 GetConvertHash();                 value = s;             }         }

在这里你可以看到,MyComboItemConvert有两个重载,分别有不同的参数,其中string类型的那个参数就是用于获取下拉菜单的项的。当然你也可以根据需要定义为其他类型的,例如List或是HashTable。只要在函数中将下拉菜单的每一项放入哈希表中 就可以了。
而在生成的代码中,我们就需要用到刚刚没有使用的Converter属性了:
举个例子:

C#

XProps xps = new XProps(); XProp xprop = new XProp(); xprop.Name = "姓名"; xprop.Value = "某人; xprop.Category = "人类"; xprop.Description = "姓甚名谁"; xprop.ProType = typeof(String); xprop.ReadOnly = true; xps.Add(xprop); xprop = new XProp(); xprop.Category = "人类"; xprop.Name = "年龄"; xprop.ProType = typeof(int); xprop.Value = "2"; xprop.Description = "多大年纪"; xprop.ReadOnly = false; xps.Add(xprop); xprop = new XProp(); xprop.Category = "人类"; xprop.Name = "性别"; xprop.Value = 1; xprop.ReadOnly = false; xprop.ProType = typeof(CustomClass.MyComboItemConvert); xprop.Converter = new CustomClass.MyComboItemConvert("M,F"); xprop.Description = "性别是男是女"; xps.Add(xprop); xprop = new XProp(); xprop.Category = "人类"; xprop.ReadOnly = false; xprop.Name = "国籍"; xprop.Value = 1; xprop.ProType = typeof(CustomClass.MyComboItemConvert); xprop.Converter = new CustomClass.MyComboItemConvert("中,英,美,法"); xprop.Description = "国籍"; xps.Add(xprop); PropertyGrideTest.SelectedObject = xps;

来看一下效果:

image.png

image.png

image.png

 

到这里,我需要的功能都能实现啦。

在这个过程中,我也在网上搜寻了很久,要感谢以下几篇博文,及其博主:

http://blog.csdn.net/luyifeiniu/article/details/5426960#创建 PropertyGrid 控件

http://blog.csdn.net/akron/article/details/2750566 

http://www.cnblogs.com/greatverve/archive/2012/02/09/propertygird-bind.html



#转载请注明出处 www.skcircle.com 《少有人走的路》勇哥的工业自动化技术网站。

扫描二维码推送至手机访问。

版权声明:本文由7点博客发布,如需转载请注明出处。

本文链接:http://6dot.cn/?id=132

标签: .NET.NET框架
分享给朋友:

“PropertyGrid控件动态生成属性及下拉菜单” 的相关文章

C#的变迁史 - C# 4.0 之线程安全集合篇

C#的变迁史 - C# 4.0 之线程安全集合篇

作为多线程和并行计算不得不考虑的问题就是临界资源的访问问题,解决临界资源的访问通常是加锁或者是使用信号量,这个大家应该很熟悉了。  而集合作为一种重要的临界资源,通用性更广,为了让大家更安全的使用它们,微软为我们带来了强大的并行集合:System.Collections.Concurrent里面的各...

C# tableLayoutPanel动态加载控件闪烁的解决方案

C# tableLayoutPanel动态加载控件闪烁的解决方案

WinForm加载多个自定义控件时,会出现很严重的闪烁问题,很卡,一块一块的加载(像打开网页时,网络很卡的那种感觉)简直没法忍受。在网上搜索了好久,网上大部分的方法是一下4种,但是都不能有效的解决问题。  1.将DoubleBuffered 设置 true,用双缓存处理Form界面内容加载,可以提高...

计算代码执行时间,可以精确到十亿分之一秒

计算代码执行时间,可以精确到十亿分之一秒

注:.Net的Stopwatch类可以精确到1/10000毫秒, 有没有更精确的计时吗?见下面的代码。暂时没试过效果,大家可以试下。计算某个代码片段的执行时间,精确到CPU执行一条指令所用的时间(十亿分之一秒),可用于精确计算某个算法的执行时间。 代码:C#using Syste...

C# 查询运算符测试

C# 查询运算符测试

测试一下全部的查询运算符。每天测试一部分,代码会持续更新中……C#using System;using System.Collections;using System.Collections.Generic;using System.Linq;using&nb...

C# 异步和等待,async/await

C# 异步和等待,async/await

 首先,重点是:异步将从根本上改变大多数代码的编写方式。是的,我相信 async/await 会比 LINQ 产生更大的影响。理解异步将在短短几年内成为基本必需品。关键字介绍让我们直接开始吧。我将使用一些稍后将阐述的概念——请继续阅读第一部分。异步方法看起来像这样:public ...

细说进程、应用程序域与上下文之间的关系(四)——进程应用程序域与线程的关系

细说进程、应用程序域与上下文之间的关系(四)——进程应用程序域与线程的关系

目录一、进程的概念与作用二、应用程序域三、深入了解.NET上下文四、进程应用程序域与线程的关系 四、进程、应用程序域、线程的相互关系4.1 跨AppDomain运行代码在应用程序域之间的数据是相对独立的,当需要在其他AppDomain当中执行当前 AppDomain中的程序集代码时,可以使...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。