Files
HawkeyeVision/framework/Inspectron.Settings/BoundPropertyDescriptor.cs
2025-07-14 12:03:59 +02:00

494 lines
19 KiB
C#

using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
namespace Inspectron.Settings
{
/// <summary>
/// A specialization of System.ComponentModel.PropertyDescriptor that is bound
/// to a specific property of an object or type. If the property's setter is private,
/// this BoundPropertyDescriptor's IsReadOnly property is true.</summary>
/// <remarks>Use this class to expose an object or type's property for property editing.</remarks>
public class BoundPropertyDescriptor : PropertyDescriptor
{
public delegate object OnClickDelegate(object sender, EventArgs e);
public OnClickDelegate OnClick { get; set; }
/// <summary>
/// Constructor for instance properties</summary>
/// <param name="owner">Property owner</param>
/// <param name="expression">Lambda expression that accesses the property;
/// e.g., () => myObject.MyProperty</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
public BoundPropertyDescriptor(
object owner,
Expression<Func<object>> expression,
string displayName,
string category,
string description)
: this(displayName, category, description)
{
PropertyInfo propertyInfo = GetPropertyInfo(expression);
Init(owner, null, null, propertyInfo, null, null);
}
/// <summary>
/// Constructor for static properties</summary>
/// <param name="ownerType">Type holding static property</param>
/// <param name="expression">Lambda expression that accesses the property;
/// e.g., () => MyClass.MyProperty</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
public BoundPropertyDescriptor(
Type ownerType,
Expression<Func<object>> expression,
string displayName,
string category,
string description)
: this(displayName, category, description)
{
PropertyInfo propertyInfo = GetPropertyInfo(expression);
Init(null, ownerType, null, propertyInfo, null, null);
}
/// <summary>
/// Constructor for instance properties</summary>
/// <param name="owner">Property owner</param>
/// <param name="expression">Lambda expression that accesses the property;
/// e.g., () => myObject.MyProperty</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
/// <param name="editor">Editor for property</param>
/// <param name="converter">TypeConverter for property</param>
public BoundPropertyDescriptor(
object owner,
Expression<Func<object>> expression,
string displayName,
string category,
string description,
object editor,
TypeConverter converter)
: this(displayName, category, description)
{
PropertyInfo propertyInfo = GetPropertyInfo(expression);
Init(owner, null, null, propertyInfo, editor, converter);
}
/// <summary>
/// Constructor for static properties</summary>
/// <param name="ownerType">Type holding static property</param>
/// <param name="expression">Lambda expression that accesses the property;
/// e.g., () => MyClass.MyProperty</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
/// <param name="editor">Editor for property</param>
/// <param name="converter">TypeConverter for property</param>
public BoundPropertyDescriptor(
Type ownerType,
Expression<Func<object>> expression,
string displayName,
string category,
string description,
object editor,
TypeConverter converter)
: this(displayName, category, description)
{
PropertyInfo propertyInfo = GetPropertyInfo(expression);
Init(null, ownerType, null, propertyInfo, editor, converter);
}
/// <summary>
/// Constructor for instance properties</summary>
/// <param name="owner">Property owner</param>
/// <param name="name">Property name</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
public BoundPropertyDescriptor(
object owner,
string name,
string displayName,
string category,
string description)
: this(displayName, category, description)
{
Init(owner, null, name, null, null, null);
}
/// <summary>
/// Constructor for instance properties</summary>
/// <param name="owner">Property owner</param>
/// <param name="name">Property name</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
/// <param name="editor">Editor for property</param>
public BoundPropertyDescriptor(
object owner,
string name,
string displayName,
string category,
string description,
object editor)
: this(displayName, category, description)
{
Init(owner, null, name, null, editor, null);
}
/// <summary>
/// Constructor for instance properties</summary>
/// <param name="owner">Property owner</param>
/// <param name="name">Property name</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
/// <param name="editor">Editor for property</param>
/// <param name="converter">TypeConverter for property</param>
public BoundPropertyDescriptor(
object owner,
string name,
string displayName,
string category,
string description,
object editor,
TypeConverter converter)
: this(displayName, category, description)
{
Init(owner, null, name, null, editor, converter);
}
/// <summary>
/// Constructor for static properties</summary>
/// <param name="ownerType">Type holding static property</param>
/// <param name="name">Property name</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
public BoundPropertyDescriptor(
Type ownerType,
string name,
string displayName,
string category,
string description)
: this(displayName, category, description)
{
Init(null, ownerType, name, null, null, null);
}
/// <summary>
/// Constructor for static properties</summary>
/// <param name="ownerType">Type holding static property</param>
/// <param name="name">Property name</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
/// <param name="editor">Editor for property</param>
public BoundPropertyDescriptor(
Type ownerType,
string name,
string displayName,
string category,
string description,
object editor)
: this(displayName, category, description)
{
Init(null, ownerType, name, null, editor, null);
}
/// <summary>
/// Constructor for static properties</summary>
/// <param name="ownerType">Type holding static property</param>
/// <param name="name">Property name</param>
/// <param name="displayName">Property display name</param>
/// <param name="category">Property category</param>
/// <param name="description">Property description</param>
/// <param name="editor">Editor for property</param>
/// <param name="converter">TypeConverter for property</param>
public BoundPropertyDescriptor(
Type ownerType,
string name,
string displayName,
string category,
string description,
object editor,
TypeConverter converter)
: this(displayName, category, description)
{
Init(null, ownerType, name, null, editor, converter);
}
private void Init(
object owner,
Type ownerType,
string name,
PropertyInfo propertyInfo,
object editor,
TypeConverter converter)
{
m_owner = owner;
// if given the property owner, ignore the ownerType parameter
if (owner != null)
ownerType = owner.GetType();
m_ownerType = ownerType;
if (string.IsNullOrEmpty(name))
{
if (propertyInfo == null)
throw new ArgumentException("either 'name' or 'propertyInfo' must be non-null");
name = propertyInfo.Name;
}
// if given the property info, don't use reflection to find it
_propertyInfo = propertyInfo;
if (_propertyInfo == null)
{
_propertyInfo = m_ownerType.GetProperty(
name,
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.Static);
if (_propertyInfo == null)
throw new ArgumentException(name + ": Property doesn't exist");
}
// look at "set_" method to determine if property is read-only.
// (PropertyInfo.CanWrite will return true if there is a set
// accessor, even if that accessor is made inaccessible using
// asymmetric accessor accessibility.)
MethodInfo setInfo = m_ownerType.GetMethod("set_" + name,
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.Static);
m_readOnly = (setInfo == null);
m_editor = editor;
m_typeConverter = converter;
}
private BoundPropertyDescriptor(
string displayName,
string category,
string description)
: base(displayName,
new Attribute[] { new CategoryAttribute(category),
new DescriptionAttribute(description), })
{
}
/// <summary>
/// When overridden in a derived class, returns whether resetting an object changes its value</summary>
/// <param name="component">The component to test for reset capability</param>
/// <returns>True iff resetting the component changes its value</returns>
public override bool CanResetValue(object component)
{
object defaultValue;
return GetDefaultValue(out defaultValue) && !Object.Equals(GetValue(null), defaultValue);
}
/// <summary>
/// Gets the component this property is bound to. Is null, if bound to a static class's property.</summary>
public object Owner
{
get { return m_owner; }
}
/// <summary>
/// Gets the type of the component this property is bound to</summary>
public override Type ComponentType
{
get { return m_ownerType; }
}
/// <summary>
/// Gets the type of the property</summary>
public override Type PropertyType
{
get { return _propertyInfo.PropertyType; }
}
/// <summary>
/// Gets whether this property is read-only</summary>
public override bool IsReadOnly
{
get { return m_readOnly; }
}
/// <summary>
/// Resets the value for this property of the component to the default value</summary>
/// <param name="component">The component with the property value that is to be reset to the default value</param>
public override void ResetValue(object component)
{
object defaultValue;
GetDefaultValue(out defaultValue);
SetValue(component, defaultValue);
}
/// <summary>
/// Determines whether the value of this property needs to be persisted</summary>
/// <param name="component">The component with the property to be examined for persistence</param>
/// <returns>True iff the property should be persisted</returns>
public override bool ShouldSerializeValue(object component)
{
object val = GetValue(component);
object defaultValue;
if (!GetDefaultValue(out defaultValue) && val == null)
return false;
else
return !val.Equals(defaultValue);
}
/// <summary>
/// Returns the Owner's value (if the Owner is not null) or the component's value of the property</summary>
/// <param name="component">Component to examine</param>
/// <returns>The value of a property</returns>
public override object GetValue(object component)
{
if (m_owner != null)
component = m_owner;
return _propertyInfo.GetValue(component, null);
}
/// <summary>
/// Sets the value of the Owner (if not null) or component</summary>
/// <param name="component">Component</param>
/// <param name="value">The new value</param>
public override void SetValue(object component, object value)
{
if (m_owner != null)
component = m_owner;
_propertyInfo.SetValue(component, value, null);
}
/// <summary>
/// Gets the default value</summary>
/// <param name="result">Is set to the default value or null, if it couldn't be determined</param>
/// <returns>Whether or not the default value was determined</returns>
/// <remarks>Uses reflection to look for a DefaultValueAttribute</remarks>
public virtual bool GetDefaultValue(out object result)
{
bool foundDefault = false;
result = null;
object[] attributes = _propertyInfo.GetCustomAttributes(typeof(DefaultValueAttribute), false);
if (attributes.Length > 0)
{
foundDefault = true;
result = (attributes[0] as DefaultValueAttribute).Value;
if (result != null && result.GetType() != _propertyInfo.PropertyType)
{
// Default value type is not the same as the property type; convert it.
// This can happen if the property's type is not CLS-compliant (e.g. UInt32).
TypeConverter converter = TypeDescriptor.GetConverter(result);
if (converter.CanConvertTo(_propertyInfo.PropertyType))
{
result = converter.ConvertTo(result, _propertyInfo.PropertyType);
}
else
{
// Try using the converter associated with the source instead of the target
// (Not sure if this is useful for not, but can it hurt?)
converter = TypeDescriptor.GetConverter(_propertyInfo.PropertyType);
if (converter.CanConvertFrom(result.GetType()))
{
result = converter.ConvertFrom(result);
}
}
}
}
return foundDefault;
}
/// <summary>
/// Returns an editor of the specified type</summary>
/// <param name="editorBaseType">Base type of editor, which is used to differentiate between multiple
/// editors that a property supports</param>
/// <returns>An instance of the requested editor type, or null if an editor cannot be found</returns>
public override object GetEditor(Type editorBaseType)
{
if (m_editor != null &&
editorBaseType.IsInstanceOfType(m_editor))
{
return m_editor;
}
if (editorBaseType.IsEnum)
{
}
return base.GetEditor(editorBaseType);
}
/// <summary>
/// Gets the type converter for this property</summary>
public override TypeConverter Converter
{
get
{
if (m_typeConverter != null)
return m_typeConverter;
return base.Converter;
}
}
public PropertyInfo PropertyInfo => _propertyInfo;
// Does not return null. Will throw an exception if 'expression' is poorly formed.
private static PropertyInfo GetPropertyInfo(Expression<Func<object>> expression)
{
PropertyInfo propertyInfo = null;
MemberExpression memberExpression = expression.Body as MemberExpression;
if (memberExpression != null)
{
// this is the usual case for when a property has a public getter and setter
propertyInfo = memberExpression.Member as PropertyInfo;
}
else
{
// if the setter is private, the expression is a UnaryExpression type for some reason.
UnaryExpression unaryExpression = expression.Body as UnaryExpression;
if (unaryExpression != null)
{
memberExpression = unaryExpression.Operand as MemberExpression;
if (memberExpression != null)
propertyInfo = memberExpression.Member as PropertyInfo;
}
}
if (propertyInfo == null)
throw new ArgumentException(
"lambda expression was not properly formed." +
" Should be \" => myObject.MyProperty\" or" +
" \" => MyClass.MyProperty\"");
return propertyInfo;
}
object m_owner;
Type m_ownerType;
PropertyInfo _propertyInfo;
bool m_readOnly;
private object m_editor;
private TypeConverter m_typeConverter;
}
}