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

对于这几个月的这个项目,一直想做个总结,但是鉴于本人记性之差,总是将这件事想起又忘记,终于在这个月工作的最后几天有了几天的空闲,把这个经验好好的记录下来。

PropertyGrid,.net框架下的一个控件,这是一个软件升级的项目,原来的软件用的是C++,控件用的还是第三方,这次升级到visualstudio .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

{

#regionICustomTypeDescriptor 成员

publicAttributeCollection GetAttributes()

{

return TypeDescriptor.GetAttributes(this, true);

}

publicstring GetClassName()

{

return TypeDescriptor.GetClassName(this, true);

}

publicstring GetComponentName()

{

return TypeDescriptor.GetComponentName(this, true);

}

publicTypeConverter GetConverter()

{

return TypeDescriptor.GetConverter(this, true);

}

publicEventDescriptor GetDefaultEvent()

{

return TypeDescriptor.GetDefaultEvent(this, true);

}

publicPropertyDescriptor GetDefaultProperty()

{

return TypeDescriptor.GetDefaultProperty(this, true);

}

publicobject GetEditor(System.Type editorBaseType)

{

return TypeDescriptor.GetEditor(this, editorBaseType, true);

}

publicEventDescriptorCollection GetEvents(System.Attribute[]attributes)

{

return TypeDescriptor.GetEvents(this, attributes, true);

}

publicEventDescriptorCollection GetEvents()

{

return TypeDescriptor.GetEvents(this, true);

}

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

}

publicPropertyDescriptorCollection GetProperties()

{

return TypeDescriptor.GetProperties(this, true);

}

publicobject GetPropertyOwner(PropertyDescriptor pd)

{

return this;

}

#endregion

publicoverride 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

{

XProptheProp;

publicXPropDescriptor(XProp prop, Attribute[] attrs): base(prop.Name,attrs)

{

theProp = prop;

}

publicoverride bool CanResetValue(object component)

{

return false;

}

publicoverride string Category

{

get { return theProp.Category; }

}

publicoverride string Description

{

get { return theProp.Description; }

}

publicoverride TypeConverter Converter

{

get { return theProp.Converter; }

}

publicoverride System.Type ComponentType

{

get { return this.GetType(); }

}

publicoverride object GetValue(object component)

{

return theProp.Value;

}

publicoverride bool IsReadOnly

{

get { return theProp.ReadOnly; }

}

publicoverride System.Type PropertyType

{

get { return theProp.ProType; }

}

publicoverride void ResetValue(object component)

{

}

publicoverride void SetValue(object component, object value)

{

theProp.Value = value;

}

publicoverride bool ShouldSerializeValue(object component)

{

return false;

}

}

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

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

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

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

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

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

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

//重写下拉菜单中的项,使之与属性页的项关联
public abstract class ComboBoxItemTypeConvert : TypeConverter
{
public Hashtable myhash = null;
public ComboBoxItemTypeConvert()
{
myhash = new Hashtable();
GetConvertHash();
}
public abstract void GetConvertHash();

//是否支持选择列表的编辑
public override boolGetStandardValuesSupported(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, TypedestinationType)
{

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 boolGetStandardValuesExclusive(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();
PropertyGrid控件动态生成属性及下拉菜单 mfc下拉菜单控件
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 = newCustomClass.MyComboItemConvert("中,英,美,法");
xprop.Description = "国籍";
xps.Add(xprop);


PropertyGrideTest.SelectedObject = xps;

来看一下效果:





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

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

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

  

爱华网本文地址 » http://www.aihuau.com/a/25101016/296464.html

更多阅读

ios7动态壁纸下载及设置方法 电脑动态壁纸怎么设置

ios7动态壁纸下载及设置方法——简介iOS7全新功能,全新体验,带给您极致的绝佳体验!iOS7发布一段时间了,拥有上亿用户升级了iOS7系统,体验了全新的iOS。原来的iOS系统只有静态壁纸,iOS7开启了动态壁纸的新时代。ios7动态壁纸怎么下载及设置

Word2007自动生成目录及从任意页开始排页码 word目录页码自动生成

给标题设置大纲等级,是给论文正确添加目录的前提,换句话说,就是,有了大纲等级,才能生成正确的目录。而论文的目录里面,可能不仅仅有带序号的标题,还有摘要、致谢、参考文献等等,这些都必须要进行大纲级别的设置以后才会在目录里面显示。对于

使WPS里面的文章自动生成目录及页面设置 wps如何自动生成目录

假如文章中标题格式为第一节……大标题(一级)1.1……小标题(二级)1.1.1—……小标题下的小标题(三级)……第N节……大标题(一级)n.1……小标题(二级)n.1.1—……小标题下的小标题(三级)自动生成文章目录的操作:一、设置标题格式1.选中文章中的所

声明:《PropertyGrid控件动态生成属性及下拉菜单 mfc下拉菜单控件》为网友泡沫破灭分享!如侵犯到您的合法权益请联系我们删除