All right, here is a separate answer to a follow-up question by Sports Kuo: how to customize the editing of the fields of the target structure used to be edited by
System.Windows.Forms.PropertyGrid
using its property
SelectedObject
.
As I say (please see my comments to my other answer), this component is not designed to intercept events and replace in-place editor with a custom editor.
The technique is so complex that it would take a whole big article to explain the techniques. As I'm not writing such article, I can only depict the steps schematically; and the detail should be taken from regular Microsoft help.
So, the design
System.Windows.Forms.PropertyGrid
suggests to modify in-place editor by sub-classing. More than that, to invoke such editor, the property
SelectedObject
should not be used with the target object. Instead, that object should be dynamically wrapped in some class implemented the interface
System.ComponentModel.ICustomTypeDescriptor
. Let's name this class "
PropertySurrogate
". Let's also assume the surrogate (wrapper) takes an actual object as a constructor parameter:
PropertySurrogate.PropertySurrogate(object target)
. When this is done, the surrogate should be used instead of actual target object:
System.Windows.Forms.PropertyGrid myPropertyGrid;
object selectedObject =
myPropertyGrid.SelectedObject = selectedObject;
myPropertyGrid.SelectedObject = new PropertySurrogate(selectedObject);
Note:
PropertyGrid
itself is not modified. It is the
PropertySurrogate
defines all the detail of the presentation of
selectedObject
: which members to show, which members are allowed to be editable and the editors. Also, nested structures could be shown in a special way, using miniature tree views (see for example, how the properties of the type
System.Graphics.Point
are presented; same effect can be achieved for any other structure).
Now, as
System.ComponentModel.ICustomTypeDescriptor
should be implemented. All 12 of the interface methods are important, one of the interface methods is used to define the editor:
System.ComponentModel.ICustomTypeDescriptor.GetEditor
. The interface can be implemented using the "helper" class
System.ComponentModel.TypeDescriptor
, but the particular implementation which need to select some specialized kind of editor should provide special editor instead the default.
Here is my example. I had to implement text-property editor in addition to the default string editor. The default editor behaves like single-line edit control, but I needed a multi-line text editor. First of all, I need to specify which properties "deserve" multi-line editing. For this purpose, I created a special attribute:
[System.AttributeUsage(
System.AttributeTargets.Property,
AllowMultiple=false, Inherited=false)]
public class UiMultilineTextAttribute : System.Attribute { }
Using Reflection, we need to look at every property of the target object type, find out what editor should be used (if any) and pass this information to implementation of
System.ComponentModel.ICustomTypeDescriptor.GetEditor
. This implementation should also use the editor type, so it ultimately brings us to the editor itself.
To implement the editor good for the use by
PropertyGrid
via the class implementing
System.ComponentModel.ICustomTypeDescriptor
, one should create a class using the type
System.Drawing.Design.UITypeEditor
as a base class:
internal class MultilineStringEditor : UITypeEditor {
public override UITypeEditorEditStyle GetEditStyle(
ITypeDescriptorContext context) { }
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value) { }
TextBox TextBox = new TextBox();
}
Here is a way to insert some control in the editor:
IWindowsFormsEditorService edSvc =
(IWindowsFormsEditorService)provider.GetService(
typeof(IWindowsFormsEditorService));
edSvc.DropDownControl(TextBox);
In the implementation of
MultilineStringEditor
the instance of the
TextBox
should be re-used using lazy evaluation technique: one needs an instance of the text box and an activation flag as the slot of the class
MultilineStringEditor
. All the activation should be done only once per the instance of the class.
In this way, I implemented special editors with few field in one editor to edit
Multiplicity
, another special string editor to edit file names (showing ellipsis button calling a regular File Fialog and more. The special chapter is editing enumeration-type members. This is a whole big topic.
For a complete course on my enumeration techniques please see my series of three CodeProject articles on Enumeration topics, please find them in my CodeProject profile.
How's that?
Thank you.
—SA