Extended ImageIndexConverter and ImageIndexEditor.






4.20/5 (7 votes)
May 18, 2003
4 min read

56020

382
An Extended ImageIndexConverter and ImageIndexEditor for user control.
- A user control with the ExtImageIndexConverter and ExtImageIndexEditor - 19 Kb
- Download demo project - 14 Kb
Introduction
ImageIndex
and ImageList
in a component work as a pair. Having these two properties in the same control, you can have a list of images displayed as a dropdown combo by using the ImageIndexConverter
and also the private editor ImageIndexEditor
in the System.Windows.Forms.Design
namespace.
[Category("Appearance"),
Description("..."), DefaultValue(-1)]
[TypeConverter(typeof(ImageIndexConverter)),
Editor("System.Windows.Forms.Design.ImageIndexEditor", typeof(UITypeEditor))]
public int ImageIndex
{
get
{
return _ImageIndex;
}
set
{
if (_ImageIndex = value)
{
_ImageIndex=value;
}
}
However, there are number of limitations in using these two classes:
- The
ImageIndexConverter
andImageIndexEditor
in the Framework Class Library (FCL) work only when the control has bothimageindex
andImageList
properties. How can we obtain the same behavior for children withoutImageList
, like theTabPage
class? There is noImageList
inTabPage
. The normalImageIndexConverter
andImageIndexEditor
are not applicable for this scenario. - One approach is to add an
ImageList
property in the child class. Extra coding is required to duplicate the content of theImageList
property from the parent control to the children. Although you may set theBrowsableAttribute
of theImageList
property to false to avoid unexpected user interaction through the visual IDE, user still can set theImageList
in the child class by code. Confusion may happen. Definitely, it is not a good solution. ImageIndexEditor
is a private class in theSystem.Windows.Forms.Design
namespace. To use this editor, you pass the string identifying the full reference ofImageIndexEditor
in theEditorAttribute
for theImageIndex
property. The VisualStudio designer object (i.e. the Site property of the object in design time) will resolve the editor for the property. The problem of using this editor is compatibility with future .NET versions. Microsoft has no guarantee to maintain this editor or the features of this editor since it is private.
General Implementation
ExtImageIndexConverter
and ExtImageIndexEditor
work for two different scenarios. The typical case is the class having its own ImageList
and ImageIndex
. The TypeDescriptor
class is used to determine whether the ImageList
is in the property list of the context instance.
public override StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
...
PropertyDescriptorCollection PropertyCollection
= TypeDescriptor.GetProperties(context.Instance);
PropertyDescriptor Property;
if ((Property = PropertyCollection.Find("ImageList", false)) != null)
ImageList = (ImageList) Property.GetValue(context.Instance);
...
If the class has its own ImageList
, the image list will be built from that ImageList
.
The other scenario is the ImageList
exists in the Parent
object, just likes TabPages
and TabControl
classes. The Parent
property of children is used to locate the ImageList
. Only object inherited from the Control
class has the Parent
property. The value of the Parent
property is only assigned to when the object is added into a container Controls
collection. Otherwise, the value of Parent
is null.
PropertyDescriptorCollection PropertyCollection
= TypeDescriptor.GetProperties(context.Instance);
PropertyDescriptor Property;
if ((Property = PropertyCollection.Find("ImageList", false)) != null)
ImageList = (ImageList) Property.GetValue(context.Instance);
else if ((Property = PropertyCollection.Find("Parent", false)) != null)
{
object Parent = Property.GetValue(context.Instance);
PropertyDescriptorCollection ParentPropertyCollection
= TypeDescriptor.GetProperties(Parent);
if (ParentPropertyCollection != null)
{
PropertyDescriptor ParentProperty
= ParentPropertyCollection.Find("ImageList", false);
if (ParentProperty != null)
ImageList = (ImageList) ParentProperty.GetValue(Parent);
...
When you add a TabPage
into the TabPages
collection of a TabControl
object, the collection event handler adds that TabPage
object into the Control's
collection of the TabControl
object. That is also what we did in our ImageListBrowser.
public class ImageItemCollection : CollectionBase
{
private Control _Parent;
public ImageItemCollection(Control Parent)
{
_Parent = Parent;
}
}
protected override void OnRemoveComplete(int index,object value)
{
if (_Parent.Controls.Contains((Control) value))
{
_Parent.Controls.Remove((Control) value);
OnCollectionChanged();
}
}
protected override void OnInsertComplete(int index,object value)
{
if (!_Parent.Controls.Contains((Control) value))
{
_Parent.Controls.Add((Control) value);
OnCollectionChanged();
}
}
protected override void OnClearComplete()
{
_Parent.Controls.Clear();
OnCollectionChanged();
}
protected virtual void OnCollectionChanged()
{
if (CollectionChanged != null)
CollectionChanged(this, EventArgs.Empty);
}
We also want to refresh the parent control for whatever changes that we made in Items
collection. The OnCollectionChanged
event is for this purpose.
Proprietary Implementation
We may not always work on container model. In some case, we may only have a collection of Items
inherited from Component
class, and there is an ImageIndex
property in it. The items are not maintained in Controls
collection of the parent. For this case, what we need is a reference from child to the parent just like the Parent
property of Control
class. We can do this in the Collection
class. The ImageBrowser2
shows how to have the same result for non-container type parent. The ExtImageIndexConverter
and ExtImageIndexEditor
in this case only work on the ImageItem
class.
public override StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
...
ImageList ImageList = null;
if (context.Instance != null && context.Instance is ImageItem)
{
// Step 1 - Determine who has the imagelist.
if (((ImageItem)context.Instance).ReferenceParent != null)
ImageList = ((ImageItem)context.Instance).ReferenceParent.ImageList;
// Step 2 - Construct list of index for the images in the ImageList if any.
if (ImageList != null)
...
Other Concerns
- Since the
Controls
collection is maintained by theItems
collection , theControls
collection in the parent control should not be serialized by the designer. A newControls
collection is defined and set as hidden inDesignerSerializeVisibility
.[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new Control.ControlCollection Controls { get { return base.Controls; } }
- In order to have the Designer generating code for the
Item
, the item must be inherited fromComponent
or one of its derived classes. Otherwise, you need to develop your own serializer to take care the code generation. To have a light weighted class, you should do that. - In
ImageListBrowser2
, we don't want to have the icon displayed for theImageItem
objects in the component tray of the windows form. TheDesignTimeVisibleAttribute
is used to make it invisible.[DesignTimeVisible(false)] public class ImageItem : System.ComponentModel.Component
Conclusion
The ExtImageIndexConverter
and ExtImageIndexEditor
in the ImageListBrowser
work on most scenarios related to ImageList
and ImageIndex
. In fact, I used these two classes in my projects.
You may also review the ExtImageIndexConverter
and ExtImageIndexEditor
in ImageListBrowser2
for the proprietary implementation.