DESKTOP

Combine Multiple Events into a Single Event

10/13/2010 5:14:24 PM
Rather than notifying subscribers of each update, group them into a meta-event. For a starting example, assume you have a collection that notifies listeners when it has been updated:
class ItemAddedEventArgs<T> : EventArgs
{
private T _item;
public T Item {get;}

public ItemAddedEventArgs(T item)
{
_items = item;
}
}

class MyCollection<T>
{
List<T> _data = new List<T>();

public event EventHandler<ItemAddedEventArgs<T>> ItemsAdded;

protected void OnItemsAdded(T item)
{
if (ItemsAdded != null)
{
ItemsAdded(this, new ItemAddedEventArgs<T>(item));
}
}
public void Add(T item)
{
_data.Add(item);
OnItemsAdded(item);
}
}


The client of this collection happens to be a Windows Form. Here is part of its source code:

public partial class Form1 : Form
{
MyCollection<int> _items = new MyCollection<int>();

public Form1()
{
InitializeComponent();
}

void _items_ItemsAdded(object sender, ItemAddedEventArgs<int> e)
{
listViewOutput.Items.Add(e.Item.ToString());
}

private void buttonOneAtATime_Click(object sender, EventArgs e)
{
_items = new MyCollection<int>();
_items.ItemsAdded += new EventHandler<ItemAddedEventArgs<int>>(_items_ItemsAdded);

GenerateItems();
}

private void GenerateItems()
{
listViewOutput.Items.Clear();

for (int i = 0; i < 20000; i++)
{
_items.Add(i);
}
}
}


When the button is clicked, 20,000 items are added to the collection, which generates 20,000 event notifications, and 20,000 inserts and updates into the ListView.

The ListView has the ability to prevent UI updating during large inserts with the BeginUpdate and EndUpdate methods. This idea could also be used in your custom collection to batch updates.

The ItemAddedEventArgs<T> class must be updated to contain more than one item:

class ItemAddedEventArgs<T> : EventArgs
{
private IList<T> _items = new List<T>();
public IList<T> Items { get { return _items; } }

public ItemAddedEventArgs()
{
}

public ItemAddedEventArgs(T item)
{
_items.Add(item);
}

public void Add(T item)
{
_items.Add(item);
}
}

Here’s the updated MyCollection<T>:

class MyCollection<T>
{
List<T> _data = new List<T>();
int _updateCount = 0;

public event EventHandler<ItemAddedEventArgs<T>> ItemsAdded;
List<T> _updatedItems = new List<T>();

protected void OnItemsAdded(T item)
{
if (!IsUpdating)
{
if (ItemsAdded != null)
{
ItemsAdded(this, new ItemAddedEventArgs<T>(item));
}
}
else
{
_updatedItems.Add(item);
}
}

protected void FireQueuedEvents()
{
if (!IsUpdating && _updatedItems.Count > 0)
{
//the event args have the ability to contain multiple items
ItemAddedEventArgs<T> args = new ItemAddedEventArgs<T>();
foreach (T item in _updatedItems)
{
args.Add(item);
}
_updatedItems.Clear();
if (ItemsAdded != null)
{
ItemsAdded(this, args);
}
}
}

public bool IsUpdating
{
get
{
return _updateCount > 0;
}
}

public void BeginUpdate()
{
//keep a count in case multiple clients call BeginUpdate,
//or it's called recursively, though note that this
//class is NOT thread safe.
++_updateCount;
}

public void EndUpdate()
{
--_updateCount;
if (_updateCount == 0)
{
//only fire when we're done with all updates
FireQueuedEvents();
}
}

public void Add(T item)
{
_data.Add(item);
OnItemsAdded(item);
}
}


Now the client must call BeginUpdate before adding items to take advantage of this:

//in Form class

private void buttonUpdateBatch_Click(object sender, EventArgs e)
{
_items = new MyCollection<int>();
_items.ItemsAdded +=
new EventHandler<ItemAddedEventArgs<int>>(_items_ItemsAdded);

_items.BeginUpdate();
GenerateItems();
_items.EndUpdate();
}
void _items_ItemsAdded(object sender, ItemAddedEventArgs<int> e)
{
listViewOutput.BeginUpdate();
foreach (var i in e.Items)
{
listViewOutput.Items.Add(i.ToString());
}
listViewOutput.EndUpdate();
}

The time savings can be immense. Refer to the BatchEvents sample program in the included source code to see the difference. On my machine, the non-batched version took nearly 6 seconds, whereas the batched version took less than a second.

Other  
  •  Internet Security and Acceleration Server 2004 : Additional Configuration Tasks
  •  Windows Server AppFabric
  •  Cloud Application Architectures : Privacy Design
  •  Cloud Application Architectures : Machine Image Design
  •  Windows Azure : Using the Storage Client Library
  •  Windows Azure : Using the Blob Storage API
  •  Windows Azure : Blobs - Usage Considerations
  •  Windows Azure : Understanding the Blob Service
  •  Design and Deploy High Availability for Exchange 2007 : Design Edge Transport and Unified Messaging High Availability
  •  Design and Deploy High Availability for Exchange 2007 : Design Hub Transport High Availability
  •  Design and Deploy High Availability for Exchange 2007 : Design CAS High Availability
  •  Design and Deploy High Availability for Exchange 2007 : Create Bookmark Create Note or Tag Implement Standby Continuous Replication (SCR)
  •  Windows Server 2008 : Utilize System Center VMM
  •  Windows Server 2008 : Create Virtual Hard Drives and Machines
  •  Windows Server 2008 : Manage Hyper-V Remotely
  •  Windows Server 2008 : Install the Hyper-V Role
  •  Windows 7 : Rolling Back to a Stable State with System Restore
  •  Windows 7 : Configuring System Protection Options
  •  Windows 7 : Using the Windows Backup Program
  •  Active Directory Federation Services (ADFS)
  •  
    Most View
    Customizing the Browser User Interface
    OpenGL on OS X : Full-Screen Rendering
    Windows Phone 7 Development : Debugging Application Exceptions (part 1) - Debugging Page Load Exceptions
    Windows Phone 7 Development : Revising WeatherRx to Manage Slow Data Connections
    SQL Azure : Building Two OData Consumer Applications (part 2) - Windows Mobile 7 Application
    Programming .NET Security : Programming Cryptographic Keys (part 1) - Creating Keys
    Personalize Your iPhone Case
    Design and Deploy High Availability for Exchange 2007 : Design Edge Transport and Unified Messaging High Availability
    ASRock Z77E-ITX - Great-Looking And Very Overclockable
    Canon PIXMA MX895
    Top 10
    Buying Guide: All-In-One Multifunction Printers – May 2013 (Part 3)
    Buying Guide: All-In-One Multifunction Printers – May 2013 (Part 2)
    Buying Guide: All-In-One Multifunction Printers – May 2013 (Part 1)
    Arduino Due - A Microcontroller Board
    Compact Liquid Cooling Systems Roundup – Front Runners (Part 4)
    Compact Liquid Cooling Systems Roundup – Front Runners (Part 3)
    Compact Liquid Cooling System Roundup – Front Runners (Part 2)
    Compact Liquid Cooling System Roundup – Front Runners (Part 1)
    The Best PC Deals Around – May 2013 (Part 2)
    The Best PC Deals Around – May 2013 (Part 1)