programming4us
programming4us
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  
 
Video
PS4 game trailer XBox One game trailer
WiiU game trailer 3ds game trailer
Top 10 Video Game
-   Rise of Incarnates [PC] Zeus Trailer
-   Heroes Reborn | The Extraordinary Among Us (Preview)
-   Battleborn | E3 2015 Gameplay Demo
-   Fortnite [PC] Mac Showcase Trailer
-   Overwatch [PC] Zarya Gameplay Trailer
-   Tony Hawk's Pro Skater 5 [PS3/PS4/X360/XOne] THPS Is Back Trailer
-   Bombing Busters Trailer
-   Blade & Soul 'What is Blade & Soul?' Trailer
-   Cast of the Seven Godsends 'Plague Armour' Trailer
-   Guncraft X360 Trailer
-   Disgaea 5: Alliance of Vengeance | Official Trailer
-   XCOM 2 [PC] E3 2015 Gameplay Trailer
-   RONIN | Turn-Based Action Platformer
-   Balance Benny | Trailer
-   We Happy Few | An Uncle Jack Episode - Nighty Night, The Pied Piper of Hamlyn, Part1
Game of War | Kate Upton Commercial
programming4us
 
 
programming4us