Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Extended ImageIndexConverter and ImageIndexEditor.

0.00/5 (No votes)
26 May 2003 1  
An Extended ImageIndexConverter and ImageIndexEditor for user control.

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:

  1. The ImageIndexConverter and ImageIndexEditor in the Framework Class Library (FCL) work only when the control has both imageindex and ImageList properties. How can we obtain the same behavior for children without ImageList, like the TabPage class? There is no ImageList in TabPage. The normal ImageIndexConverter and ImageIndexEditor are not applicable for this scenario.
  2. One approach is to add an ImageList property in the child class. Extra coding is required to duplicate the content of the ImageList property from the parent control to the children. Although you may set the BrowsableAttribute of the ImageList property to false to avoid unexpected user interaction through the visual IDE, user still can set the ImageList in the child class by code. Confusion may happen. Definitely, it is not a good solution.
  3. ImageIndexEditor is a private class in the System.Windows.Forms.Design namespace. To use this editor, you pass the string identifying the full reference of ImageIndexEditor in the EditorAttribute for the ImageIndex 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

  1. Since the Controls collection is maintained by the Items collection , the Controls collection in the parent control should not be serialized by the designer. A new Controls collection is defined and set as hidden in DesignerSerializeVisibility.
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new Control.ControlCollection Controls
    {
       get
       {
           return base.Controls;
       }
    }
  2. In order to have the Designer generating code for the Item, the item must be inherited from Component 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.
  3. In ImageListBrowser2, we don't want to have the icon displayed for the ImageItem objects in the component tray of the windows form. The DesignTimeVisibleAttribute 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here