if i set a default editor for propertygridcontrol, it can't display a readonly property correct. even if i set a Readonly(true) attribute, i can edit it , and then ,it will cause a crash if this property do not have a property setter.
C#using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Diagnostics;
using System.Text.RegularExpressions;
namespace DXApplication25
{
public partial class Form1 : DevExpress.XtraEditors.XtraForm
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var obj = new TestObject();
propertyGridControl1.SelectedObject = obj;
var tp = new DefaultTypeDescriptor(obj);
propertyGridControl2.SelectedObject = tp;
propertyGridControl3.DefaultEditors.Add(typeof(int), new DevExpress.XtraEditors.Repository.RepositoryItemSpinEdit());
propertyGridControl4.DefaultEditors.Add(typeof(int), new DevExpress.XtraEditors.Repository.RepositoryItemSpinEdit());
propertyGridControl3.SelectedObject = obj;
propertyGridControl4.SelectedObject = tp;
}
}
public class TestObject
{
[Browsable(true)]
[ReadOnly(true)]
public int testObj { get; set; }
}
/// <summary>
/// Class PropertyDescriptor.
/// </summary>
/// <seealso cref="System.ComponentModel.PropertyDescriptor" />
/// <seealso cref="BluePrint.Common.Utility.IPropertyDescriptorDirtyStatus" />
public class PropertiesPropertyDescriptor :
PropertyDescriptor
{
/// <summary>
/// The bind object
/// </summary>
public object Object { get; protected set; }
/// <summary>
/// The bind property
/// </summary>
public PropertyInfo Property { get; protected set; }
/// <summary>
/// The node type
/// </summary>
public System.Type ObjectType => Object != null ? Object.GetType() : null;
/// <summary>
/// The friendly name
/// </summary>
public string FriendlyName { get; protected set; }
/// <summary>
/// Initializes a new instance of the <see cref="PropertyDescriptor"/> class.
/// </summary>
/// <param name="targetObject">The in node.</param>
/// <param name="propertyName">Name of the in property.</param>
/// <param name="friendlyName">Name of the in friendly.</param>
/// <param name="attributes">The in attrs.</param>
public PropertiesPropertyDescriptor(
object targetObject,
string propertyName,
string friendlyName,
Attribute[] attributes) :
base(propertyName, attributes)
{
Object = targetObject;
Property = ObjectType.GetProperty(propertyName);
FriendlyName = friendlyName;
}
/// <summary>
/// Initializes a new instance of the <see cref="PropertyDescriptor"/> class.
/// </summary>
/// <param name="targetObject">The in node.</param>
/// <param name="propertyName">Name of the in property.</param>
/// <param name="attributes">The in attrs.</param>
public PropertiesPropertyDescriptor(
object targetObject,
string propertyName,
Attribute[] attributes) :
this(targetObject, propertyName, "", attributes)
{
DisplayNameAttribute Attr = Property.GetAnyCustomAttribute<DisplayNameAttribute>();
if (Attr != null)
{
FriendlyName = Attr.DisplayName;
}
else
{
FriendlyName = propertyName;
}
}
/// <summary>
/// Gets the flag object.
/// </summary>
/// <value>The flag object.</value>
public object FlagObject => Property;
/// <summary>
/// 当在派生类中被重写时,返回重置对象时是否更改其值。
/// </summary>
/// <param name="component">要测试重置功能的组件。</param>
/// <returns>如果重置组件更改其值,则为 true;否则为 false。</returns>
public override bool CanResetValue(object component)
{
return true;
}
/// <summary>
/// 当在派生类中被重写时,获取该属性绑定到的组件的类型。
/// </summary>
/// <value>The type of the component.</value>
public override Type ComponentType
{
get
{
return this.GetType();
}
}
/// <summary>
/// 当在派生类中被重写时,获取组件上的属性的当前值。
/// </summary>
/// <param name="component">具有为其检索值的属性的组件。</param>
/// <returns>给定组件的属性的值。</returns>
public override object GetValue(object component)
{
return Property.GetValue(Object, null);
}
/// <summary>
/// 当在派生类中被重写时,获取指示该属性是否为只读的值。
/// </summary>
/// <value><c>true</c> if this instance is read only; otherwise, <c>false</c>.</value>
public override bool IsReadOnly
{
get
{
ReadOnlyAttribute roAttr = Property.GetAnyCustomAttribute<ReadOnlyAttribute>();
return roAttr != null && roAttr.IsReadOnly;
}
}
/// <summary>
/// 当在派生类中被重写时,获取该属性的类型。
/// </summary>
/// <value>The type of the property.</value>
public override Type PropertyType
{
get
{
return Property.PropertyType;
}
}
/// <summary>
/// 获取可以显示在窗口(如“属性”窗口)中的名称。
/// </summary>
/// <value>The display name.</value>
public override string DisplayName => FriendlyName;
/// <summary>
/// 当在派生类中被重写时,将组件的此属性的值重置为默认值。
/// </summary>
/// <param name="component">具有要重置为默认值的属性值的组件。</param>
public override void ResetValue(object component)
{
if (Property.PropertyType == typeof(string))
{
SetValue(null, "");
}
else
{
try
{
SetValue(null, Activator.CreateInstance(Property.PropertyType));
}
catch
{
}
}
}
/// <summary>
/// 当在派生类中被重写时,将组件的值设置为一个不同的值。
/// </summary>
/// <param name="component">具有要进行设置的属性值的组件。</param>
/// <param name="value">新值。</param>
public override void SetValue(object component, object value)
{
Property.SetValue(Object, value, null);
}
/// <summary>
/// 当在派生类中被重写时,确定一个值,该值指示是否需要永久保存此属性的值。
/// </summary>
/// <param name="component">具有要检查其持久性的属性的组件。</param>
/// <returns>如果属性应该被永久保存,则为 true;否则为 false。</returns>
public override bool ShouldSerializeValue(object component)
{
return false;
}
/// <summary>
/// 获取成员的说明,如 <see cref="T:DescriptionAttribute" /> 中所指定的。
/// </summary>
/// <value>The description.</value>
public override string Description
{
get
{
var Attr = Property.GetAnyCustomAttribute<DescriptionAttribute>();
if (Attr != null)
{
return Attr.Description;
}
return Property.Name;
}
}
/// <summary>
/// 获取该成员所属的类别的名称,如 <see cref="T:CategoryAttribute" /> 中所指定的。
/// </summary>
/// <value>The category.</value>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" />
/// </PermissionSet>
public override string Category
{
get
{
var Attr = Property.GetAnyCustomAttribute<CategoryAttribute>();
if (Attr != null)
{
return Attr.Category;
}
return Property.DeclaringType.Name;
}
}
}
/// <summary>
/// Class TypeExtension.
/// </summary>
public static class TypeExtension
{
/// <summary>
/// Gets any custom attributes.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="memberInfo">The member information.</param>
/// <param name="inherit">if set to <c>true</c> [inherit].</param>
/// <returns>T.</returns>
public static T GetAnyCustomAttribute<T>(this MemberInfo memberInfo, bool inherit = true)
where T : Attribute
{
var Attrs = memberInfo.GetCustomAttributes(typeof(T), inherit);
if (Attrs != null && Attrs.Length > 0)
{
T attr = Attrs[0] as T;
return attr;
}
return null;
}
/// <summary>
/// Gets the custom attributes.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="memberInfo">The member information.</param>
/// <param name="inherit">if set to <c>true</c> [inherit].</param>
/// <returns>T[].</returns>
public static T[] GetCustomAttributes<T>(this MemberInfo memberInfo, bool inherit = true)
where T : Attribute
{
var Attrs = memberInfo.GetCustomAttributes(typeof(T), inherit);
if (Attrs != null && Attrs.Length > 0)
{
return Attrs.Cast<T>().ToArray();
}
return new T[0];
}
/// <summary>
/// Determines whether the specified inherit is defined.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="memberInfo">The member information.</param>
/// <param name="inherit">if set to <c>true</c> [inherit].</param>
/// <returns><c>true</c> if the specified inherit is defined; otherwise, <c>false</c>.</returns>
public static bool IsDefined<T>(this MemberInfo memberInfo, bool inherit = true)
where T : Attribute
{
return memberInfo.IsDefined(typeof(T), inherit);
}
/// <summary>
/// Gets the type of the underlying.
/// </summary>
/// <param name="memberInfo">The member information.</param>
/// <returns>Type.</returns>
/// <exception cref="System.ArgumentException">Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo</exception>
public static Type GetUnderlyingType(this MemberInfo memberInfo)
{
switch (memberInfo.MemberType)
{
case MemberTypes.Event:
return ((EventInfo)memberInfo).EventHandlerType;
case MemberTypes.Field:
return ((FieldInfo)memberInfo).FieldType;
case MemberTypes.Method:
return ((MethodInfo)memberInfo).ReturnType;
case MemberTypes.Property:
return ((PropertyInfo)memberInfo).PropertyType;
default:
throw new ArgumentException
(
"Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"
);
}
}
/// <summary>
/// Determines whether the specified type is implement.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="type">The type.</param>
/// <returns><c>true</c> if the specified type is implement; otherwise, <c>false</c>.</returns>
public static bool IsImplement<T>(this Type type)
where T : class
{
Debug.Assert(typeof(T).IsInterface);
return type.GetInterfaces().Contains(typeof(T));
}
/// <summary>
/// Determines whether the specified interface type is implement.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="interfaceType">Type of the interface.</param>
/// <returns><c>true</c> if the specified interface type is implement; otherwise, <c>false</c>.</returns>
public static bool IsImplement(this Type type, Type interfaceType)
{
Debug.Assert(interfaceType.IsInterface);
return type.GetInterfaces().Contains(interfaceType);
}
/// <summary>
/// Determines whether [is child of] [the specified type].
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="type">The type.</param>
/// <returns><c>true</c> if [is child of] [the specified type]; otherwise, <c>false</c>.</returns>
public static bool IsChildOf<T>(this Type type)
where T : class
{
return typeof(T).IsAssignableFrom(type);
}
/// <summary>
/// Determines whether [is child of] [the specified base type].
/// </summary>
/// <param name="type">The type.</param>
/// <param name="baseType">Type of the base.</param>
/// <returns><c>true</c> if [is child of] [the specified base type]; otherwise, <c>false</c>.</returns>
public static bool IsChildOf(this Type type, Type baseType)
{
return baseType != null && baseType.IsAssignableFrom(type);
}
/// <summary>
/// Gets the type of the converter.
/// </summary>
/// <param name="attribute">The input attribute.</param>
/// <returns>System.Type.</returns>
public static System.Type GetConverterType(this System.ComponentModel.TypeConverterAttribute attribute)
{
Type Result = null;
TryGetTypeByName(attribute.ConverterTypeName, out Result, AppDomain.CurrentDomain.GetAssemblies());
return Result;
}
/// <summary>
/// Gets the type associated with the specified name.
/// </summary>
/// <param name="typeName">Full name of the type.</param>
/// <param name="type">The type.</param>
/// <param name="customAssemblies">Additional loaded assemblies (optional).</param>
/// <returns>Returns <c>true</c> if the type was found; otherwise <c>false</c>.</returns>
public static bool TryGetTypeByName(string typeName, out Type type, params Assembly[] customAssemblies)
{
if (typeName.Contains("Version=")
&& !typeName.Contains("`"))
{
// remove full qualified assembly type name
typeName = typeName.Substring(0, typeName.IndexOf(','));
}
type = Type.GetType(typeName);
if (type == null)
{
type = GetTypeFromAssemblies(typeName, customAssemblies);
}
// try get generic types
if (type == null
&& typeName.Contains("`"))
{
var match = Regex.Match(typeName, "(?<MainType>.+`(?<ParamCount>[0-9]+))\\[(?<Types>.*)\\]");
if (match.Success)
{
int genericParameterCount = int.Parse(match.Groups["ParamCount"].Value);
string genericDef = match.Groups["Types"].Value;
List<string> typeArgs = new List<string>(genericParameterCount);
foreach (Match typeArgMatch in Regex.Matches(genericDef, "\\[(?<Type>.*?)\\],?"))
{
if (typeArgMatch.Success)
{
typeArgs.Add(typeArgMatch.Groups["Type"].Value.Trim());
}
}
Type[] genericArgumentTypes = new Type[typeArgs.Count];
for (int genTypeIndex = 0; genTypeIndex < typeArgs.Count; genTypeIndex++)
{
Type genericType;
if (TryGetTypeByName(typeArgs[genTypeIndex], out genericType, customAssemblies))
{
genericArgumentTypes[genTypeIndex] = genericType;
}
else
{
// cant find generic type
return false;
}
}
string genericTypeString = match.Groups["MainType"].Value;
Type genericMainType;
if (TryGetTypeByName(genericTypeString, out genericMainType))
{
// make generic type
type = genericMainType.MakeGenericType(genericArgumentTypes);
}
}
}
return type != null;
}
/// <summary>
/// Gets the type from assemblies.
/// </summary>
/// <param name="typeName">Name of the type.</param>
/// <param name="customAssemblies">The custom assemblies.</param>
/// <returns>Type.</returns>
private static Type GetTypeFromAssemblies(string typeName, params Assembly[] customAssemblies)
{
Type type = null;
if (customAssemblies != null
&& customAssemblies.Length > 0)
{
foreach (var assembly in customAssemblies)
{
type = assembly.GetType(typeName);
if (type != null)
return type;
}
}
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in loadedAssemblies)
{
type = assembly.GetType(typeName);
if (type != null)
return type;
}
return type;
}
/// <summary>
/// Gets the type.
/// </summary>
/// <param name="typeName">Name of the type.</param>
/// <returns>Type.</returns>
public static Type GetType(string typeName)
{
var type = Type.GetType(typeName);
if (type != null)
{
return type;
}
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
type = a.GetType(typeName);
if (type != null)
{
return type;
}
}
return null;
}
}
/// <summary>
/// Class DefaultTypeDescriptor.
/// </summary>
/// <seealso cref="System.ComponentModel.ICustomTypeDescriptor" />
public class DefaultTypeDescriptor :
ICustomTypeDescriptor
{
/// <summary>
/// The bind object
/// </summary>
protected object BindObject;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultTypeDescriptor"/> class.
/// </summary>
/// <param name="objectTarget">The in object.</param>
public DefaultTypeDescriptor(object objectTarget)
{
Debug.Assert(objectTarget != null);
BindObject = objectTarget;
}
/// <summary>
/// 返回此组件实例的自定义特性的集合。
/// </summary>
/// <returns>包含此对象的特性的 <see cref="T:System.ComponentModel.AttributeCollection" />。</returns>
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
/// <summary>
/// 返回此组件实例的类名。
/// </summary>
/// <returns>该对象的类名;如果此类没有名称,则为 null。</returns>
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
/// <summary>
/// 返回此组件实例的名称。
/// </summary>
/// <returns>该对象的名称;如果该对象没有名称,则为 null。</returns>
public string GetComponentName()
{
return TypeDescriptor.GetClassName(this, true);
}
/// <summary>
/// 返回此组件实例的类型转换器。
/// </summary>
/// <returns>表示该对象的转换器的 <see cref="T:System.ComponentModel.TypeConverter" />;如果此对象没有任何 <see cref="T:System.ComponentModel.TypeConverter" />,则为 null。</returns>
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
/// <summary>
/// 返回此组件实例的默认事件。
/// </summary>
/// <returns>表示该对象的默认事件的 <see cref="T:System.ComponentModel.EventDescriptor" />;如果该对象没有事件,则为 null。</returns>
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
/// <summary>
/// 返回此组件实例的默认属性。
/// </summary>
/// <returns>表示该对象的默认属性的 <see cref="T:System.ComponentModel.PropertyDescriptor" />;如果此对象没有属性,则为 null。</returns>
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
/// <summary>
/// 返回此组件实例的指定类型的编辑器。
/// </summary>
/// <param name="editorBaseType">表示该对象的编辑器的 <see cref="T:System.Type" />。</param>
/// <returns>表示该对象编辑器的指定类型的 <see cref="T:System.Object" />;如果无法找到编辑器,则为 null。</returns>
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
/// <summary>
/// 将指定的特性数组用作筛选器来返回此组件实例的事件。
/// </summary>
/// <param name="attributes">用作筛选器的 <see cref="T:System.Attribute" /> 类型数组。</param>
/// <returns>表示此组件实例的已筛选事件的 <see cref="T:System.ComponentModel.EventDescriptorCollection" />。</returns>
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
/// <summary>
/// 返回此组件实例的事件。
/// </summary>
/// <returns>表示此组件实例的事件的 <see cref="T:System.ComponentModel.EventDescriptorCollection" />。</returns>
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
/// <summary>
/// Adds the attributes.
/// </summary>
/// <param name="attributes">The in attrs.</param>
/// <param name="attribute">The in attribute.</param>
/// <returns>Attribute[].</returns>
protected Attribute[] AddAttributes(Attribute[] attributes, Attribute attribute)
{
return attributes.Concat(new Attribute[] { attribute }).ToArray();
}
/// <summary>
/// 返回将特性数组用作筛选器的此组件实例的属性。
/// </summary>
/// <param name="attributes">用作筛选器的 <see cref="T:System.Attribute" /> 类型数组。</param>
/// <returns>表示此组件实例的已筛选属性的 <see cref="T:System.ComponentModel.PropertyDescriptorCollection" />。</returns>
public virtual PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
List<PropertyDescriptor> DescList = new List<PropertyDescriptor>();
var Type = BindObject.GetType();
foreach (var t in Type.GetProperties())
{
BrowsableAttribute Attr = t.GetAnyCustomAttribute<BrowsableAttribute>();
if (Attr != null && !Attr.Browsable)
{
continue;
}
List<Attribute> Results = new List<Attribute>(attributes);
foreach (Attribute a in t.GetCustomAttributes(true))
{
Results.Add(a);
}
DescList.Add(new PropertiesPropertyDescriptor(BindObject, t.Name, Results.ToArray()));
}
return new PropertyDescriptorCollection(DescList.ToArray());
}
/// <summary>
/// 返回此组件实例的属性。
/// </summary>
/// <returns>表示此组件实例的属性的 <see cref="T:System.ComponentModel.PropertyDescriptorCollection" />。</returns>
public PropertyDescriptorCollection GetProperties()
{
return TypeDescriptor.GetProperties(this, true);
}
/// <summary>
/// 返回包含指定的属性描述符所描述的属性的对象。
/// </summary>
/// <param name="pd">表示要查找其所有者的属性的 <see cref="T:System.ComponentModel.PropertyDescriptor" />。</param>
/// <returns>表示指定属性所有者的 <see cref="T:System.Object" />。</returns>
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
}
}
please check this demo, in the propertgridcontrol1 and propertygridcontrol2, it works well, in propertygridcontrol3 and propertgridcontrol4, it display a editable row.
you can get full code of this demo in attchment, please fix this quickly, thankyou!!
this is correct in the previours versions.
there are so many bugs in version 18.2.3, please release 18.2.4 as quick as you can, i'm waiting for it.
Hi,
Thank you for providing the project. It was very helpful.
I have also noticed the described behavior and passed this ticket to our developers for further investigation. Please bear with us. We will notify you as soon as we make any progress.