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

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

 

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

 

首先定义一个属性类:

//单条属性类

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

我举一个例子:

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

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

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设置为这个类就好了:

先来定义以下两个类:

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控件中,想要具有下拉菜单的属性,简单的可以通过枚举类型来实现,还是以刚刚的那个例子来说明:
首先定义个枚举类型:

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


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

image.png

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

//重写下拉菜单中的项,使之与属性页的项关联
        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属性了:
举个例子:

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



本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
  • 评论列表:
  •  访客
     发布于 2019-03-06 18:21:07  回复该评论
  • 非常感谢分享,解决了我一个很头疼的问题。
  •  勇哥,很想停止
     发布于 2019-03-07 09:04:30  回复该评论
  • 动态构建属性可以参见下面的封装类介绍,使用起来更加简单。
    [netMarketing类库] propertyGridHelper 类:动态构建属性,用于配合PropertyGrid控件
    http://www.skcircle.com/?id=148

发表评论:

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

会员中心
搜索
«    2024年3月    »
123
45678910
11121314151617
18192021222324
25262728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864