<$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

Friday, March 26, 2004

ArrayList with Events (Part 1) 

I recently was involved in a thread on GotDotNet about responding to ArrayList changes in the UI. DalanGalma wanted to populate a ListView with data from an ArrayList and keep it in synch with the associated data objects. Having an ArrayList that fired events (this could also be done for other collections as well) when things changed could be generally useful, not just for keeping the ListView up to date. So, I decided to create one (after a few searches for something like that on the web).

So, first thing to do was figure out what events to send. I decided on having several:

  • ArrayItemsAdded - called when new items are added to the list either by Add or Insert methods.
  • ArrayItemsRemoved - called when items are removed from the list through Remove methods or Clear.
  • ArrayItemsChanged - called when the actual data for an item has changed (depends on object implementor to fire event appropriately).
  • ArrayItemsSorted - called whenever the Arraylist is sorted.

The interesting thing about ArrayItemsChanged is that data objects need to fire it, and it doesn't get fired when you set an item into an array position.
          myArray[index] = newItem;
Instead, I decided to fire an ArrayItemsRemoved followed by ArrayItemsAdded (rather than ArrayItemsChanged) because the item itself isn't actually changing, but the item that was at that position in the ArrayList is being replaced by the new item. It's debatable as to which is better, but if you feel that ArrayItemsChanged should be fired instead, feel free to update the code in the sample to your requirements.

Here's some of the code for the EventedArrayList class:

///
/// EventedArrayList overrides the default ArrayList class to fire events when
/// interesting things happen to the array, like when items are added, removed,
/// and changed.
///

public class EventedArrayList : ArrayList
{
#region Properties
public override object this[int index]
{
get
{
return base[index];
}
set
{
object oldValue = base[index];
if (oldValue != value)
{
base[index] = value;

// when the indexer is set, then we're really getting rid of the old
// item and setting the new one.
if (oldValue != null)
{
this.OnArrayItemsRemoved(new ArrayListEventArgs(oldValue));
}

if (value != null)
{
this.OnArrayItemsAdded(new ArrayListEventArgs(value));
}
}
}
}
#endregion

#region ArrayList override methods
public override int Add(object value)
{
int index = base.Add(value);
if (index >= 0)
{
this.OnArrayItemsAdded(new ArrayListEventArgs(value));
}
return index;
}

public override void Insert(int index, object value)
{
base.Insert(index, value);
this.OnArrayItemsAdded(new ArrayListEventArgs(value));
}

public override void InsertRange(int index, ICollection c)
{
base.InsertRange(index, c);
if (c.Count > 0)
{
this.OnArrayItemsAdded(new ArrayListEventArgs(c));
}
}

public override void RemoveAt(int index)
{
object obj = this[index];
base.RemoveAt(index);

if (obj != null)
{
this.OnArrayItemsRemoved(new ArrayListEventArgs(obj));
}
}

public override void RemoveRange(int index, int count)
{
object[] obj = new object[count];
this.CopyTo(index, obj, 0, count);

base.RemoveRange(index, count);

if (obj.Length > 0)
{
this.OnArrayItemsRemoved(new ArrayListEventArgs(obj));
}
}

public override void Clear()
{
object[] obj = new object[this.Count];
this.CopyTo(obj, 0);

base.Clear();

if (obj.Length > 0)
{
this.OnArrayItemsRemoved(new ArrayListEventArgs(obj));
}
}

public override void Sort()
{
base.Sort();
this.OnArrayItemsSorted(new EventArgs());
}

public override void Sort(int index, int count, IComparer comparer)
{
base.Sort(index, count, comparer);
this.OnArrayItemsSorted(new EventArgs());
}

public override void Sort(IComparer comparer)
{
base.Sort(comparer);
this.OnArrayItemsSorted(new EventArgs());
}
#endregion
}

 


You'll notice that I didn't override AddRange or Remove because they call through to other methods (InsertRange and RemoveAt respectively) that already fire events, so you would get multiple events fired in those cases. Counter-intuitively, you do need fire an event for Clear because it doesn't call into RemoveRange as I thought it might... gotta love consistency. :)

Note: This class does not contain an overloaded constructor that takes an ICollection similar to the one ArrayList has. No events are fired during the class's creation because no one could be listening for them yet anyway, so you would not get notification that these items were added in the constructor. Therefore, I decided to leave that method out.


Finally, the ArrayListEventArgs class is used by all of the events. This class simply keeps a list of items that have been manipulated by an EventedArrayList operation. This is passed into the event handlers, so that they can use that information to respond accordingly.

In my next blog entry, I'll discuss the data object that fires change events...

You can download the full sample code from GotDotNet or look at it online and report bugs at the workspace.

Comments: Post a Comment