Creating an Event to run when a change occurs in my array [C#]

Shaitan00

Junior Contributor
Joined
Aug 11, 2003
Messages
358
Location
Hell
Given an array (public int[,] levelMap; // Level layout of cells) that is used to define the cell layout of my screen.
Now, everytime a value changes in levelMap [at a certain location in the array] I need to update an ArrayList (alChangeList) which the coordinates of the changes so that when I redraw I know where.
Currently this means that at every location in my code that I change levelMap I need to also update alChangeList (manually), problem is there are a LOT of places where levelMap can change and I want to centralize the code required to update alChangeList.

So, my idea ... and this is where I need help, a) no clue if this is do-able and b) no clue how to do it...
- Create an EVENT that is triggered when something in the array changes
- In that event's EVENTHANDLER function I could perform the update on the ArrayList alChangeList (hopfully the event would include the location the change was made)
This solves all my problems and makes the code much easier to manage ...

Hopefully something like this can be accomplished, would be much easier then having to account for it all manually, to many places where changes occur... Or if you can think of something else/better?
Any ideas, hints, and help would be greatly appreciated, thanks

One SIDE QUESTION - instead of using an ARRAYLIST (alChangeList) would it be better/more efficient to use a bool array (public bool[,] changesMap) where I would set the location to TRUE if a change was made and have code loop through each location when drawing... OR is it safer to use the ArrayList and just remove each added element (that way I don't need to cycle through the entire array to see if there were any changes, cause there could be none)
Thanks,
 
Here's an approach that I use a lot. How it works is that you fire an event from within the class itself anytime it becomes dirty. It makes a lot more sense to have your "thing" notify others when it is dirty -- instead of having external items constantly check the thing to see if it's dirty. Any place that your object is updated you just fire the "NotifyDirty" method, which will in turn fire any subscribed delegate methods to notify listeners of your object's event.
C#:
class MyThing
{
    public delegate void Dirty(MyThing thing, EventArgs e);

    public event Dirty OnDirty;

    private int _myProperty;

    public MyThing()
    {
    }

    private void NotifyDirty()
    {
        if (OnDirty != null)
        {
            OnDirty(this, new EventArgs());
        }
    }

    public int MyProperty
    {
        get { return _myProperty; }
        set
        {
            if (_myProperty != value)
            {
                _myProperty = value;

                NotifyDirty();
            }
        }
    }
}
External items can register for this event with something like:
C#:
MyThing thing = new MyThing();
thing.OnDirty += new MyThing.Dirty(MyThingIsDirty);
The callback method would look something like:
C#:
private void MyThingIsDirty(MyThing thing, EventArgs e)
{
    // update display, etc
}
You can easily modify the EventArgs to be your own custom class if you like.
 
You could even make it fully generic:
C#:
public class MyList<T>
{
    public enum DirtyAction
    {
        Added,
        Removed
    }

    public class MyListDirtyEventArgs
    {
        private T _item;
        private DirtyAction _action;

        public MyListDirtyEventArgs(DirtyAction action, T item)
        {
            _action = action;
            _item = item;
        }

        public DirtyAction Action
        {
            get { return _action; }
        }

        public T Item
        {
            get { return _item; }
        }
    }

    public delegate void Dirty(MyList<T> list, MyListDirtyEventArgs e);

    public event Dirty OnDirty;

    private List<T> _list = new List<T>();

    public T this[int index]
    {
        get
        {
            return _list[index];
        }
        set
        {
            _list[index] = value;
        }
    }

    public void Add(T item)
    {
        _list.Add(item);

        NotifyDirty(DirtyAction.Added, item);
    }

    public void Remove(T item)
    {
        _list.Remove(item);

        NotifyDirty(DirtyAction.Removed, item);
    }

    public T[] Items
    {
        get 
        { 
            return _list.ToArray(); 
        }
    }

    private void NotifyDirty(DirtyAction action, T item)
    {
        if (OnDirty != null)
        {
            OnDirty(this, new MyListDirtyEventArgs(action, item));
        }
    }
}
Example of using it:
C#:
private void Test()
{
    MyList<Int32> list = new MyList<int>();
    list.OnDirty += new MyList<int>.Dirty(ListDirty);

    list.Add(99);
    list.Add(100);

    list.Remove(9933);
}

private void ListDirty(MyList<Int32> list, MyList<Int32>.MyListDirtyEventArgs e)
{
    if (e.Action == MyList<int>.DirtyAction.Added)
    {
        Console.WriteLine("Somebody added: " + e.Item);
    }
    else if (e.Action == MyList<int>.DirtyAction.Removed)
    {
        Console.WriteLine("Somebody removed: " + e.Item);
    }
}
 
As to whether it is more efficient to maintain a list of tiles to update or an array of bools that flags each tile, that depends on your situation. If you will only be changing a small percantage of tiles each update, it would probably be better to go with the arraylist because using the array of bools would require checking every tile for each update, where as with the ArrayList, you don't need to check tiles because you already have a list. If you will be changing most of the tiles, a bool array would probably be faster and more memory efficient, but unless this application is particularly grand it shouldn't really make much of a difference either way so you should do whatever makes your program easier to write.

As far as tracking changes, this is what I would do: encapsulate the array in a class. Use an indexer or a function pair to access the array elements and have the class track the changes itself.

I hate to throw another screen of code at you, but here is a pretty thorough example:
C#:
// Warning: this class indexes different data than the data it enumerates.
// Some people might frown on this.
public class LevelMap: IEnumerable<Point>
{
	int[,] data; // Map data
	List<Point> dirtyTiles = new List<Point>(); // List of changed tiles

	// Constructor
	public LevelMap(int width, int height) {
		data = new int[width, height];
	}

	// This code allows a user to access the data and automatically
	// updates the dirty tile list
	public int GetTile(int x, int y) {
		return data[x, y];
	}
	public void SetTile(int x, int y, int value) {
		data[x, y] = value;
		dirtyTiles.Add(new Point(x, y));
	}

	// This code also allows a user to access data and maintains the
	// dirty tile list, but this property allows the user to access the
	// class using array-like code.
	public int this[int x, int y] {
		get {
			return data[x, y];
		}
		set {
			data[x, y] = value;
			dirtyTiles.Add(new Point(x, y));
		}
	}

	// This code allows a user to use a foreach loop to get dirty tiles
	public IEnumerator<Point> GetEnumerator() {
		return dirtyTiles.GetEnumerator();
	}
	System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
		return dirtyTiles.GetEnumerator();
	}

	// This code clears the dirty tile list after you have updated the screen
	public void ClearDirtyTiles() {
		dirtyTiles.Clear();
	}
}

// Demonstrates the LevelMap class
class Demo
{
	public Demo(){
		LevelMap myMap = new LevelMap(10, 10);
		
		// Setting data
		myMap.SetTile(5, 5, 10);
		myMap[4, 4] = 20;

		// Getting data
		MessageBox.Show(myMap[5, 5].ToString());
		MessageBox.Show(myMap.GetTile(4, 4).ToString());

		// Enumerate dirty tiles
		foreach(Point dirtyTile in myMap) {
			MessageBox.Show(dirtyTile.ToString());
		}
		
		// Feel free to clear the list and start over.
		myMap.ClearDirtyTiles();
	}
}
 
Back
Top