【200分】如何让PropertyGrid显示控件的Name属性

CsToD 2012-02-03 03:18:52
加精
由于Control.Name属性被标记为[Browsable(false)],导致Name属性无法显示,请问怎么解决呢?我要让所有的控件(比如TextBox,Button,ComboBox)都能显示Name出来,所以不能用派生子控件的方式去做
...全文
1372 67 打赏 收藏 转发到动态 举报
写回复
用AI写文章
67 条回复
切换为时间正序
请发表友善的回复…
发表回复
冰心的小屋 2012-02-28
  • 打赏
  • 举报
回复
200分给我吧,以上都没有好答案!

PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(TextBox));
AttributeCollection attrs= props["Name"].Attributes;

BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic |BindingFlags.CreateInstance;

FieldInfo type = typeof(BrowsableAttribute).GetField("browsable", flags);
filedInfo.SetValue(attrs[typeof(ReadOnlyAttribute)], false/true);

以上方法适应于所有的字段属性标签
代码打字员 2012-02-26
  • 打赏
  • 举报
回复
[Quote=引用 63 楼 chongsha 的回复:]

直接继承
new Name
{
get{retrun base.Name}
set{base.Name=value;}
}

上面的搞了那么多复杂的方法干啥
[/Quote]

这个兄弟说的很对。
[Browsable(True)]
public new Name
{
get{retrun base.Name}
set{base.Name=value;}
}

冲杀 2012-02-24
  • 打赏
  • 举报
回复
直接继承
new Name
{
get{retrun base.Name}
set{base.Name=value;}
}

上面的搞了那么多复杂的方法干啥
rzxxtest 2012-02-23
  • 打赏
  • 举报
回复
来看看
hao603324 2012-02-22
  • 打赏
  • 举报
回复
WPF正在学习。坐等高手
jonglin 2012-02-22
  • 打赏
  • 举报
回复
不是很懂
过来等答案的
bobi456 2012-02-22
  • 打赏
  • 举报
回复
也不会,纯粹是来凑数的。
足球中国 2012-02-21
  • 打赏
  • 举报
回复
楼主可以搜一下。tooltip这个就有动态添加属性的。
足球中国 2012-02-21
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 qldsrx 的回复:]

引用 18 楼 cstod 的回复:
办法应该有,“动态添加属性”,不过我没搜到有用的信息

你这是凭空想想,根本没这样的技术,就算有这样的说法,也只是自定义个支持添加属性的类,且添加了不可反射的属性,这里必须是可反射才行。

你要求是系统自带的类进行动态添加,且不说动态添加,静态添加都不可能,所以劝你趁早放弃。

退一步有一种技术,虽然可以实现,但是十分复杂,不推荐。就是动态创建……
[/Quote]
动态属性有这个啊。怎么是凭空想的呢。
我前段时间搞这个东西。下载了动态属性的列子。列子好象msdn上也有。
CsToD 2012-02-08
  • 打赏
  • 举报
回复
你的答案已经让我非常满意了,后面仅做研究而已,结贴之
CsToD 2012-02-05
  • 打赏
  • 举报
回复
又研究了一下,去掉了从PropertyDescriptor派生子类的部分,并且可以直接显示Name,而不是(Name),更加完善了

    class ObjectDescriptionProvider : TypeDescriptionProvider
{
public ObjectDescriptionProvider() : base(TypeDescriptor.GetProvider(typeof(object))) { }

public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
ICustomTypeDescriptor defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
PropertyInfo pi = objectType.GetProperty("Name");
if (pi != null&&pi.PropertyType==typeof(string) )
{
BrowsableAttribute[] bas = (BrowsableAttribute[])pi.GetCustomAttributes(typeof(BrowsableAttribute), false);
if (bas.Length > 0 && !bas[0].Browsable)
return new NameCustomTypeDescriptor(defaultDescriptor);
}
return defaultDescriptor;
}
}

class NameCustomTypeDescriptor : CustomTypeDescriptor
{
public NameCustomTypeDescriptor(ICustomTypeDescriptor parent)
: base(parent)
{
pd = TypeDescriptor.CreateProperty(parent.GetType().GetField("_objectType", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(parent) as Type, "Name", typeof(string), BrowsableAttribute.Yes);
}
private PropertyDescriptor pd;
public override PropertyDescriptorCollection GetProperties()
{
PropertyDescriptorCollection pdc = base.GetProperties();
PropertyDescriptor[] pds = new PropertyDescriptor[pdc.Count + 1];
pds[0] = pd;
pdc.CopyTo(pds, 1);
return new PropertyDescriptorCollection(pds);
}

public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
PropertyDescriptorCollection pdc = base.GetProperties(attributes);
PropertyDescriptor[] pds = new PropertyDescriptor[pdc.Count + 1];
pds[0] = pd;
pdc.CopyTo(pds, 1);
return new PropertyDescriptorCollection(pds);
}
}
CsToD 2012-02-05
  • 打赏
  • 举报
回复
我之前还有更疯狂的想法:监视Application.OpenForm,从其中去找到子项的ProperyGrid控件的instance,从而通过其SelectedObject来获取需要的Type。。。
qldsrx 2012-02-05
  • 打赏
  • 举报
回复
反射用多了降低程序的执行效率,因此能直接访问的不要用反射,这不是炫耀,底层不是给人看的,别人只能看到执行效率。
qldsrx 2012-02-05
  • 打赏
  • 举报
回复
本来我打算睁只眼闭只眼的,但是你还在错误的方向越陷越深,实在看不下去了。

最佳方法是我前面给的那个自定义类的方法,而且可扩展性高。你的方法看似代码量少,但是执行效率却降低了。而且最可怕的是,你反射了微软保护的字段,所有受保护的字段不保证在.NET的所有版本中都适用,这不是WINDOWS API,私有成员切记直接反射,否则程序兼容性大大下降。
CsToD 2012-02-05
  • 打赏
  • 举报
回复
在没有想出更好的办法之前,看来只有使用绝招了(^_^)


static void Main()
{
foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
AddNameProperty(ass);
AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);

//code

static void AddNameProperty(Assembly ass)
{
foreach (Type t in ass.GetTypes())
{
try
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(t);
PropertyDescriptor pd = pdc.Find("Name", false);
if (pd != null && !pd.IsBrowsable)
{
pdc.GetType().GetField("readOnly", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(pdc, false);
pdc.Add(TypeDescriptor.CreateProperty(t, "Name", typeof(string), BrowsableAttribute.Yes));
}
}
catch
{
}
}
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
AddNameProperty(args.LoadedAssembly);
}


把所有满足条件的都添加上,这样不需要处理SelectedObjectsChanged事件了。。。
CsToD 2012-02-05
  • 打赏
  • 举报
回复
1.我最早就是你这么写的,后来分析.Net的源码,发现CustomTypeDescriptor中本来就已经保存了这个Type,所以我才只传了一个参数,我反而觉得我现在的写法更好一点
2.显示的时候没有,我觉得就没什么问题了
3.我又发现了一种新方法,可以不自定义类型(ObjectDescriptionProvider,NameCustomTypeDescriptor 都可以不要)


        private void propertyGrid1_SelectedObjectsChanged(object sender, EventArgs e)
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(propertyGrid1.SelectedObject.GetType());
PropertyDescriptor pd = pdc.Find("Name", false);
if (pd != null && !pd.IsBrowsable)
{
pdc.GetType().GetField("readOnly", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(pdc, false);
pdc.Add(TypeDescriptor.CreateProperty(propertyGrid1.SelectedObject.GetType(), "Name", typeof(string), BrowsableAttribute.Yes));
}
}


但是必须为每一种类型执行一次操作,直接在object或Control类上操作不行,所以我把代码放在SelectedObjectsChanged事件中,每次使用propertyGrid1.SelectedObject.GetType(),但问题是,如果有子项就不好弄了,比如MenuStrip.Items,修改它的子项并不会触发propertyGrid1的SelectedObjectsChanged事件,我分析后发现子项其实是一个VsPropertyGrid类型,但是没法取得Instance,所以没法注册其SelectedObjectsChanged事件
qldsrx 2012-02-05
  • 打赏
  • 举报
回复
想法不错,不过代码总是有点冗余,比如这里完全可以优化成这样:
    class NameCustomTypeDescriptor : CustomTypeDescriptor
{
public NameCustomTypeDescriptor(ICustomTypeDescriptor parent,Type objectType)
: base(parent)
{
pd = TypeDescriptor.CreateProperty(objectType, "Name", typeof(string), BrowsableAttribute.Yes);
}
//.................

你再看下调用方,有个objectType参数可以直接传递给它的。

另外这种方法虽然可以,但是你没有去掉原来的那个Name,实际连隐藏属性一起显示的话,就会看到两个Name(完全同名),太别扭了。
yan_c_g 2012-02-05
  • 打赏
  • 举报
回复
学了四年不知道自己会啥
CsToD 2012-02-04
  • 打赏
  • 举报
回复
恩,我修改了两处,一处是判断是否存在[Browsable(false)],另一处是判断对象的Name属性是否为string类型(否则进行属性修改时可能会报错),现在应该比较完善了,非常感谢!


class ObjectDescriptionProvider : TypeDescriptionProvider
{
public ObjectDescriptionProvider() : base(TypeDescriptor.GetProvider(typeof(Object))) { }

public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
ICustomTypeDescriptor defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
PropertyInfo pi = objectType.GetProperty("Name");
if (pi != null && pi.PropertyType==typeof(string))
{
BrowsableAttribute[] bas = (BrowsableAttribute[])pi.GetCustomAttributes(typeof(BrowsableAttribute), false);
if (bas.Length > 0 && !bas[0].Browsable)
return new NameCustomTypeDescriptor(defaultDescriptor);
}
return defaultDescriptor;
}
}

class NameCustomTypeDescriptor : CustomTypeDescriptor
{
public NameCustomTypeDescriptor(ICustomTypeDescriptor parent)
: base(parent)
{
}

public override PropertyDescriptorCollection GetProperties()
{
PropertyDescriptorCollection pdc = base.GetProperties();
PropertyDescriptor[] pds = new PropertyDescriptor[pdc.Count + 1];
pds[0] = new NamePropertyDescriptor();
pdc.CopyTo(pds, 1);
return new PropertyDescriptorCollection(pds);
}

public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
PropertyDescriptorCollection pdc = base.GetProperties(attributes);
PropertyDescriptor[] pds = new PropertyDescriptor[pdc.Count + 1];
pds[0] = new NamePropertyDescriptor();
pdc.CopyTo(pds, 1);
return new PropertyDescriptorCollection(pds);
}
}

class NamePropertyDescriptor : PropertyDescriptor
{
public NamePropertyDescriptor() : base("(Name)", null) { }

public override bool CanResetValue(object component)
{
return false;
}

public override Type ComponentType
{
get
{
return typeof(object);
}
}

public override object GetValue(object component)
{
return component.GetType().GetProperty("Name").GetValue(component, null);
}

public override bool IsReadOnly
{
get
{
return false;
}
}

public override Type PropertyType
{
get
{
return typeof(string);
}
}

public override void ResetValue(object component)
{
throw new NotImplementedException();
}

public override void SetValue(object component, object value)
{
component.GetType().GetProperty("Name").SetValue(component, value, null);
}

public override bool ShouldSerializeValue(object component)
{
return false;
}
}
CsToD 2012-02-04
  • 打赏
  • 举报
回复
大概思路我了解,需要创建一个属性:
PropertyDescriptor pd= TypeDescriptor.CreateProperty(typeof(Control),"Name",typeof(string));
然后把这个属性描述添加进去,但是我不知道添加到哪里
加载更多回复(26)

111,125

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧