Hello,
I ran into a snag recently that I realized was probably not a "common" request or feature set (due to most documentation/code examples).
The EditorAttribute attribute class does its job for the compile-time defining of custom controls for properties. This part works well, however I was wondering if there is any other way to define custom controls of specific types for the RadPropertyGrid class at runtime?
Where was my "snag"?
Since The "target property" is passed to the constructor of the EditorAttribute when it is declared as an attribute above any given class's property/variable, any "target property" defined becomes a "static declaration" due to it being bound to a class's property's CustomProperty's constructor argument list within the assembly which ends up being a read-only list at run-time.
What scenario would require "run-time" defined EditorAttributes?
Since our scenario is a bit complex (run-time construction of classes), the easiest way to describe the issue would be that a "data container class" could have the same EditorAttribute defined for a generic property (as an example an Object class) of which could require the defined User Control Editor class to utilize different TargetProperty depending upon the type of the given example "Object class".
My temporary Fix (or potentially permanent with some instantiation reduction optimizations):
====================================================================
public class CustomEditorAttribute : EditorAttribute
{
public CustomEditorAttribute(Type editorType)
: base(editorType, GetTargetProperty(editorType), GetEditorStyle(editorType))
{
}
public CustomEditorAttribute(Type editorType, EditorStyle editorStyle)
: base(editorType, GetTargetProperty(editorType), GetEditorStyle(editorType))
{
}
public CustomEditorAttribute(Type editorType, string targetProperty)
: base(editorType, GetTargetProperty(editorType), GetEditorStyle(editorType))
{
}
public CustomEditorAttribute(Type editorType, string targetProperty, EditorStyle editorStyle)
: base(editorType, GetTargetProperty(editorType), GetEditorStyle(editorType))
{
}
static CustomPropertyControl GetPropertyControl(Type type)
{
if (type.IsSubclassOf(typeof(CustomPropertyControl )))
{
return (CustomPropertyControl )InternalLib.AssemblyClassHelper.GetObjectInstance(type);
}
else
{
throw new Exception("The type (" + type.Name + ") is not a valid PropertyControl type!");
}
}
public static String GetTargetProperty(Type type)
{
return GetPropertyControl(type).GetPropertyTargetName();
}
public static EditorStyle GetEditorStyle(Type type)
{
return GetPropertyControl(type).GetEditorStyle();
}
}
====================================================================
So, my current run-time assignment of EditorAttribute arguments "fix" was to derive from the EditorAttribute class and pass the results of statically defined methods within said class that invoke an instance of the Custom Editor Control class type defined within the EditorAttribute declaration in question. The above code could also be modified further by passing a specific class type that is not the editor class in question, but a "type" container class which then could be modified at run-time based on different states of the application at the time of invocation.
Usage of the EditorAttribute Example:
====================================================================
...
[InternalLib.PropertyControls.FieldProperties.CustomEditorAttribute(typeof(InternalLib.PropertyControls.ImagePropertyControl))]
public Image ImageProperty
{
get
{
return imageProperty;
}
set
{
if (value != null && this.imageProperty != value)
{
this.imageProperty = value;
this.OnPropertyChanged("ControlImage");
if (OnImageSourceChanged != null)
{
OnImageSourceChanged.Invoke(this.imageProperty.Source);
}
}
}
}
...
Where:
public partial class ImagePropertyControl:CustomPropertyControl
{
...
public override String GetPropertyTargetName()
{
return "SelectedItem"; //The control houses a RadListBox and as such the "SelectedItem" property of the listbox is the target property
}
public override EditorStyle GetEditorStyle()
{
return EditorStyle.Modal;
}
...
}
And where:
public class BaseCustomPropertyControl:UserControl
{
public virtual String GetPropertyTargetName()
{
return String.Empty;
}
public virtual EditorStyle GetEditorStyle()
{
return EditorStyle.None;
}
}
====================================================================
However, it seems like a lot of work to simply be able to define the TargetPropery and the EditorStyle at run-time for a specific type.
Is there some way to (more easily) add EditorAttribute's programmatically to a RadPropertyGrid?
If not, then I figured I would post my current solution here in the event anyone else was trying to define class property EditorAttributes at run-time and needed one possible way to do it.
Cheers,
-Noel
p.s.
~Side-Note on the included Code~
The code snippet included has an major optimization/instantiation issue. One optimization one could do would be to create a
static dictionary in the BaseCustomPropertyControl class that is populated via a method always called in the BaseCustomPropertyControl
constructor that then in turn invokes the GetPropertyTargetName and GetEditorStyle methods (or is virtual and the child class is responsible
for returning proper information), stores both values into another container class/structure, and then adds the Key, as the Type, and
the Value, as the hypothetical container class, to the static dictionary which could then be checked via a static method defined within the
BaseCustomPropertyControl prior to constructing an entire new class instance each time. This would reduce the number of
"bogus/ghost" instances of the child class derived from the BaseCustomPropertyControl (in this case it is the
ImagePropertyControl). That would then reduce the number of times it would have to instantiate custom property controls (editors) when getting the TargetProperty and EditorStyle.
So, the example included in this post is strictly just a quick and dirty version.
I ran into a snag recently that I realized was probably not a "common" request or feature set (due to most documentation/code examples).
The EditorAttribute attribute class does its job for the compile-time defining of custom controls for properties. This part works well, however I was wondering if there is any other way to define custom controls of specific types for the RadPropertyGrid class at runtime?
Where was my "snag"?
Since The "target property" is passed to the constructor of the EditorAttribute when it is declared as an attribute above any given class's property/variable, any "target property" defined becomes a "static declaration" due to it being bound to a class's property's CustomProperty's constructor argument list within the assembly which ends up being a read-only list at run-time.
What scenario would require "run-time" defined EditorAttributes?
Since our scenario is a bit complex (run-time construction of classes), the easiest way to describe the issue would be that a "data container class" could have the same EditorAttribute defined for a generic property (as an example an Object class) of which could require the defined User Control Editor class to utilize different TargetProperty depending upon the type of the given example "Object class".
My temporary Fix (or potentially permanent with some instantiation reduction optimizations):
====================================================================
public class CustomEditorAttribute : EditorAttribute
{
public CustomEditorAttribute(Type editorType)
: base(editorType, GetTargetProperty(editorType), GetEditorStyle(editorType))
{
}
public CustomEditorAttribute(Type editorType, EditorStyle editorStyle)
: base(editorType, GetTargetProperty(editorType), GetEditorStyle(editorType))
{
}
public CustomEditorAttribute(Type editorType, string targetProperty)
: base(editorType, GetTargetProperty(editorType), GetEditorStyle(editorType))
{
}
public CustomEditorAttribute(Type editorType, string targetProperty, EditorStyle editorStyle)
: base(editorType, GetTargetProperty(editorType), GetEditorStyle(editorType))
{
}
static CustomPropertyControl GetPropertyControl(Type type)
{
if (type.IsSubclassOf(typeof(CustomPropertyControl )))
{
return (CustomPropertyControl )InternalLib.AssemblyClassHelper.GetObjectInstance(type);
}
else
{
throw new Exception("The type (" + type.Name + ") is not a valid PropertyControl type!");
}
}
public static String GetTargetProperty(Type type)
{
return GetPropertyControl(type).GetPropertyTargetName();
}
public static EditorStyle GetEditorStyle(Type type)
{
return GetPropertyControl(type).GetEditorStyle();
}
}
====================================================================
So, my current run-time assignment of EditorAttribute arguments "fix" was to derive from the EditorAttribute class and pass the results of statically defined methods within said class that invoke an instance of the Custom Editor Control class type defined within the EditorAttribute declaration in question. The above code could also be modified further by passing a specific class type that is not the editor class in question, but a "type" container class which then could be modified at run-time based on different states of the application at the time of invocation.
Usage of the EditorAttribute Example:
====================================================================
...
[InternalLib.PropertyControls.FieldProperties.CustomEditorAttribute(typeof(InternalLib.PropertyControls.ImagePropertyControl))]
public Image ImageProperty
{
get
{
return imageProperty;
}
set
{
if (value != null && this.imageProperty != value)
{
this.imageProperty = value;
this.OnPropertyChanged("ControlImage");
if (OnImageSourceChanged != null)
{
OnImageSourceChanged.Invoke(this.imageProperty.Source);
}
}
}
}
...
Where:
public partial class ImagePropertyControl:CustomPropertyControl
{
...
public override String GetPropertyTargetName()
{
return "SelectedItem"; //The control houses a RadListBox and as such the "SelectedItem" property of the listbox is the target property
}
public override EditorStyle GetEditorStyle()
{
return EditorStyle.Modal;
}
...
}
And where:
public class BaseCustomPropertyControl:UserControl
{
public virtual String GetPropertyTargetName()
{
return String.Empty;
}
public virtual EditorStyle GetEditorStyle()
{
return EditorStyle.None;
}
}
====================================================================
However, it seems like a lot of work to simply be able to define the TargetPropery and the EditorStyle at run-time for a specific type.
Is there some way to (more easily) add EditorAttribute's programmatically to a RadPropertyGrid?
If not, then I figured I would post my current solution here in the event anyone else was trying to define class property EditorAttributes at run-time and needed one possible way to do it.
Cheers,
-Noel
p.s.
~Side-Note on the included Code~
The code snippet included has an major optimization/instantiation issue. One optimization one could do would be to create a
static dictionary in the BaseCustomPropertyControl class that is populated via a method always called in the BaseCustomPropertyControl
constructor that then in turn invokes the GetPropertyTargetName and GetEditorStyle methods (or is virtual and the child class is responsible
for returning proper information), stores both values into another container class/structure, and then adds the Key, as the Type, and
the Value, as the hypothetical container class, to the static dictionary which could then be checked via a static method defined within the
BaseCustomPropertyControl prior to constructing an entire new class instance each time. This would reduce the number of
"bogus/ghost" instances of the child class derived from the BaseCustomPropertyControl (in this case it is the
ImagePropertyControl). That would then reduce the number of times it would have to instantiate custom property controls (editors) when getting the TargetProperty and EditorStyle.
So, the example included in this post is strictly just a quick and dirty version.