<$BlogRSDURL$>
My Blog Links
Site Links
Currently Reading
Blogroll
Archives
Tools
  • This page is powered by Blogger. Isn't yours?
  • Weblog Commenting and Trackback by HaloScan.com
  • 2RSS.com :: RSS directory
  • Listed on BlogShares
  • Blog Directory, Find A Blog, Submit A Blog, Search For The Best Blogs

Thursday, March 11, 2004

ImageListBox Sample 

Well, I added to my ImageComboBox sample by extracting some shared drawing and ImageItem collection code, so that it could be used for a ListBox item as well. This allowed me to easily create an ImageListBox.

As I was doing this, I wanted to have the Items collections for these controls be editable at design time, so that users could add an item with an image index at the same time. At first, I tried replacing the Items collection with my own ImageItemList:

class ImageItemList : CollectionBase
{
public ImageItemList()
{
}

public new override ImageItem this[int index]
{
get
{
return base[index] as ImageItem;
}
set
{
base[index] = value;
}
}

public override int Add(ImageItem item)
{
return base.Add(item);
}
}

For completeness, the ImageItem class looks like the following. It derives from Control so that it can be used in the collection editor. Otherwise, I would need to support a type converter for my class (I may do that in a later sample).

public class ImageItem : Component
{
private string itemData = "New Item";
private int imageIndex = -1;

public string ItemData
{
get { return itemData; }
set { itemData = value; }
}

public int ImageIndex
{
get { return imageIndex; }
set { imageIndex = value; }
}

public ImageItem(string itemData, int imageIndex)
{
this.itemData = itemData;
this.imageIndex = imageIndex;
}

public ImageItem()
{
}
}

This automatically gave me the collections editor that I wanted during design time with property grid items for ItemData and ImageIndex. However, filling those in didn't actually display the new items. And, worse at run time the control didn't think there were any items in the ListBox. That's because the ListBox.Items property is not virtual and cannot be overridden, so I could only create a new property of the type I wanted. However, the control is still using the underlying Items list and knows nothing about the one I just added.

So, I had to go back to the drawing board and came up with a different collection (called BaseImageItemList). This one actually wrapped the ListBox.Items collection and implemented all of the IList methods. The implementation passes through all of the calls to the innerCollection. Then, I added ImageItemList which derived from BaseImageItemList to provide type specific versions of some methods. This allowed me to create a type-specific Items property and still have it work with the items that the base ListBox control was expecting.

public abstract class BaseImageItemList : IList
{
IList innerCollection = null;

public BaseImageItemList(IList innerCollection)
{
this.innerCollection = innerCollection;
}

#region IList properties
public object this[int index]
{
get
{
return innerCollection[index];
}
set
{
innerCollection[index] = value;
}
}

public int Count
{
get { return innerCollection.Count; }
}

public bool IsSynchronized
{
get { return ((IList)innerCollection).IsSynchronized; }
}

public object SyncRoot
{
get { return ((IList)innerCollection).SyncRoot; }
}

public bool IsFixedSize
{
get { return ((IList)innerCollection).IsFixedSize; }
}

public bool IsReadOnly
{
get { return innerCollection.IsReadOnly; }
}
#endregion

#region IList methods
public virtual int Add(object item)
{
return innerCollection.Add(item);
}

public virtual void Clear()
{
innerCollection.Clear();
}

public virtual bool Contains(object item)
{
return innerCollection.Contains(item);
}

public virtual void CopyTo(Array items, int index)
{
innerCollection.CopyTo((object[])items, index);
}

public virtual IEnumerator GetEnumerator()
{
return innerCollection.GetEnumerator();
}

public virtual int IndexOf(object item)
{
return innerCollection.IndexOf(item);
}

public virtual void Insert(int index, object item)
{
innerCollection.Insert(index, item);
}

public virtual void Remove(object item)
{
innerCollection.Remove(item);
}

public virtual void RemoveAt(int index)
{
innerCollection.RemoveAt(index);
}
#endregion
}

This class is actually pretty generic and can be used to wrap any collection with another typed one around it, if you ever find another place where you might need to use that sort of thing. Boy, I can't wait for Generics support in the .NET Framwork!

After making those changes, everything worked as expected and I could display items with images that were either added at design or run time. You can download the full sample code from GotDotNet or look at it online and report bugs at the workspace.