Directory.Delete() Test: program.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace DirectoryTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Creating test directory");
                CreateTestDirectory();
            }
            catch (Exception ex)
            {
                DisplayError(ex);
                WaitForKey();
                return;
            }

            Console.WriteLine("Directory created");
            WaitForKey();

            DeleteTestDirectory();

            //SafeDeleteTestDirectory();
            
            Console.WriteLine("Directory deleted");
            WaitForKey();
        }

        static void DeleteTestDirectory()
        {
            try
            {
                Console.WriteLine("Deleting test directory");
                Directory.Delete("c:\\Temp\\Test1", true);
            }
            catch (Exception ex)
            {
                DisplayError(ex);
                WaitForKey();
                return;
            }
        }

        static void SafeDeleteTestDirectory()
        {
            try
            {
                Console.WriteLine("Deleting test directory with retry");
                Directory.Delete("c:\\Temp\\Test1", true);
            }
            catch (Exception ex)
            {
                DisplayError(ex);
                Thread.Sleep(1000);
                Console.WriteLine("Failed! Trying again...");
                try
                {
                    Directory.Delete("c:\\Temp\\Test1", true);
                }
                catch (Exception ex2)
                {
                    DisplayError(ex2);
                    WaitForKey();
                    return;
                }
            }
        }
        
        static char WaitForKey()
        {
            Console.WriteLine("Press any key...");
            ConsoleKeyInfo cki = Console.ReadKey();
            return cki.KeyChar;
        }

        
        static void CreateTestDirectory()
        {
            Directory.CreateDirectory("c:\\Temp\\Test1");
            Directory.CreateDirectory("c:\\Temp\\Test1\\Test2");

            using (StreamWriter sw = new StreamWriter("c:\\Temp\\Test1\\TestFile1.txt", false))
            {
                sw.WriteLine("This is a line of text");
                sw.Close();
            }

            using (StreamWriter sw = new StreamWriter("c:\\Temp\\Test1\\Test2\\TestFile2.txt", false))
            {
                sw.WriteLine("This is a line of text");
                sw.Close();
            }
        }

        static void DisplayError(Exception ex)
        {
            Exception current = ex;
            while (null != current)
            {
                Console.WriteLine("Error: " + ex.Message);
                Console.WriteLine("Stack: " + ex.StackTrace);
                Console.WriteLine(Environment.NewLine);
                current = current.InnerException;       
            }
        }
    }
}

Playing With Blocks Listing 3 – DragHandleConnector.cs

/******************************************************************************
 * DragHandleConnector.cs
 * 
 * This module implements the code behind for the DragHandleConnector class.
 *  
 * Date:   2/2009
 * 
 * Copyright (c) 2009, Mark Betz 
 * 
 * All rights reserved. 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 * 
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer. 
 *   * Redistributions in binary form must reproduce the above copyright 
 *     notice, this list of conditions and the following disclaimer in the 
 *     documentation and/or other materials provided with the distribution. 
 *   * Neither the name of the Author nor the names of contributors may be 
 *     used to endorse or promote products derived from this software
 *     without specific prior written permission. 
 *     
  * THIS SOFTWARE IS PROVIDED BY MARK BETZ ''AS IS'' AND ANY  EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PART-
 * ICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL MARK BETZ BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE. 
 */
using System;
using System.Collections.Generic;
using System.Windows;

namespace DrawControls
{
	/// <summary>
	/// Delegate type for position coercion callbacks used to
	/// adjust DragHandle position properties for consumption
	/// by clients
	/// </summary>
	/// <param name="p">A <see cref="T:System.Windows.Point">Point</see></param>
	/// <returns></returns>
	public delegate Point CoercePointCallback(Point p);

	/// <summary>
	/// Enumerates the position axes that should serve as update data
	/// for a given client
	/// </summary>
	public enum SourceAxes
	{
		/// <summary>
		/// Specifies that the X axis will be reported. The
		/// implied update type is double.
		/// </summary>
		X,

		/// <summary>
		/// Specifies that the Y axis will be reported. The
		/// implied update type is double.
		/// </summary>
		Y,

		/// <summary>
		/// Specifies that both axes will be reported. The
		/// implied update type is Point.
		/// </summary>
		Both
	};

	/// <summary>
	/// DragHandleTarget encapsulates a single update point from a DragHandle
	/// to a client object property. Multiple targets can be defined to send
	/// values to multiple properties. This is primarily used to send Point
	/// data to a double X/Y pair, as in the case of the Line shape.
	/// </summary>
	public class DragHandleTarget
	{
		/// <summary>
		/// Constructs a new DragHandle target for the specified property,
		/// owner object, and source
		/// </summary>
		/// <param name="p">A DependencyProperty to be updated when the
		/// DragHandle position changes</param>
		/// <param name="o">The DependencyObject that owns the property to be updated</param>
		/// <param name="s">A value from the <see cref="DrawControls.SourceAxes">SourceAxes</see>
		/// enum specifying which axis should be used to update the property.</param>
		public DragHandleTarget( DependencyProperty p, DependencyObject o, 
			SourceAxes s )
		{
			Property = p;
			Owner = o;
			Axes = s;
		}

		/// <summary>
		/// The property which this target should update when the DragHandle
		/// position changes. It's type must be compatible with the type
		/// implied by the Axes property.
		/// </summary>
		public DependencyProperty Property { get; set; }

		/// <summary>
		/// The DependencyObject which owns the property to be updated
		/// </summary>
		public DependencyObject Owner { get; set; }

		/// <summary>
		/// A value from the <see cref="DrawControls.SourceAxes">SourceAxes</see>
		/// enum specifying which axis of handle movement should be used to
		/// update the target property.
		/// </summary>
		public SourceAxes Axes { get; set; }
	}

	/// <summary>
	/// A DragHandleConnection is a list of DragHandleTargets and a callback
	/// used to examine and coerce position values before they are reported
	/// to a client.
	/// </summary>
	public class DragHandleConnection
	{
		public void AddTarget(DependencyProperty p, DependencyObject o,
			SourceAxes s)
		{
			_targets.Add( new DragHandleTarget(p, o, s) );
		}

		/// <summary>
		/// The CoercePointCallback will be invoked when the DragHandle
		/// position changes, before the position is sent to clients.
		/// </summary>
		public CoercePointCallback CoercePoint;

		/// <summary>
		/// Gets the list of DragHandleTargets associated with this connection
		/// </summary>
		public List<DragHandleTarget> Targets 
		{ 
			get { return _targets; } 
		}

		/// <summary>
		/// Implements the Targets property
		/// </summary>
		List<DragHandleTarget> _targets = new List<DragHandleTarget>();
	}

	/// <summary>
	/// DragHandleConnector is the overall container for a set of 
	/// connections attached to a specific DragHandler
	/// </summary>
	public class DragHandleConnector
	{
		/// <summary>
		/// Create a DragHandleConnector attached to the specified
		/// DragHandle
		/// </summary>
		/// <param name="dh">An instance of DragHandle</param>
		public DragHandleConnector( DragHandle dh )
		{
			SetHandle( dh );
		}

		/// <summary>
		/// Disconnects the connection from a DragHandle by unwiring its
		/// event handler. This should when the connection is no
		/// longer needed.
		/// </summary>
		public void Disconnect()
		{
			if (null != _dh)
				_dh.Drag -= new EventHandler<DragHandleEventArgs>(DragHandle_Drag);
		}

		/// <summary>
		/// Gets and sets the DragHandle associated with this connector
		/// </summary>
		public DragHandle Handle
		{
			get { return _dh; }
			set { SetHandle(value); }
		}

		/// <summary>
		/// Gets the list of DragHandleConnections for this connector
		/// </summary>
		public List<DragHandleConnection> Connections
		{
			get { return _connections; }
		}

		/// <summary>
		/// Used internally to connect a drag handle, while making sure that
		/// the previous handle, if any, is disconnected.
		/// </summary>
		/// <param name="dh"></param>
		private void SetHandle( DragHandle dh )
		{
			Disconnect();                
			_dh = dh;
			_dh.Drag += new EventHandler<DragHandleEventArgs>(DragHandle_Drag);
		}

		/// <summary>
		/// Handles the Drag event for connected DragHandles
		/// </summary>
		/// <param name="sender">a DragHandle</param>
		/// <param name="e">event arguments</param>
		private void DragHandle_Drag( object sender, DragHandleEventArgs e )
		{
			for( int i = 0; i < _connections.Count; i++ )
			{
				Point p;
				DragHandleConnection d = _connections[i];

				if (null != d.CoercePoint)
					p = d.CoercePoint(e.Position);
				else
					p = e.Position;  

				for ( int j = 0; j < d.Targets.Count; j++ )
				{
					DragHandleTarget t = d.Targets[j];
					switch ( t.Axes )
					{
						case SourceAxes.Both:
							t.Owner.SetValue(t.Property, p);
							break;

						case SourceAxes.X:
							t.Owner.SetValue(t.Property, p.X);
							break;

						case SourceAxes.Y:
							t.Owner.SetValue(t.Property, p.Y);
							break;
					}
				}
			}
		}

		private DragHandle _dh;
		private List<DragHandleConnection> _connections = 
		new List<DragHandleConnection>();
	}
}

Playing With Blocks Listing 2 – DragHandle.xaml.cs

/******************************************************************************
 * DragHandle.xaml.cs
 * 
 * This module implements the code behind for the DragHandle class.
 *  
 * Date:   2/2009
 * 
 * Copyright (c) 2009, Mark Betz 
 * 
 * All rights reserved. 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 * 
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer. 
 *   * Redistributions in binary form must reproduce the above copyright 
 *     notice, this list of conditions and the following disclaimer in the 
 *     documentation and/or other materials provided with the distribution. 
 *   * Neither the name of the Author nor the names of contributors may be 
 *     used to endorse or promote products derived from this software
 *     without specific prior written permission. 
 *     
 * THIS SOFTWARE IS PROVIDED BY MARK BETZ ''AS IS'' AND ANY  EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PART-
 * ICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL MARK BETZ BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE. 
 */
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace DrawControls
{
	/// <summary>
	/// DragConstraint enumerates possible constraints on the motion of a
	/// DragHandle. Motion constraints are relative to the HandleOrigin
	/// of the handle. For more information 
	/// see the <see cref="P:DrawControls.DragHandle.HandleOrigin">
	/// HandleOrigin property documentation.</see>.
	/// </summary>
	public enum DragConstraint
	{
		/// <summary>
		/// Specifies that no movement constraint will be placed on the Drag
		/// Handle. This is the default.
		/// </summary>
		None,

		/// <summary>
		/// Specifies that the DragHandle will be able to move on the east-
		/// west horizontal axis to both sides of its 
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin;</see>
		/// (+/-x, y).
		/// </summary>
		EW,

		/// <summary>
		/// Specifies that the DragHandle will be able to move on the north-
		/// south vertical axis above and below its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin;</see>
		/// (x, +/-y).
		/// </summary>
		NS,

		/// <summary>
		/// Specifies that the DragHandle will be able to move on the north-
		/// east-southwest diagonal axis, to both sides of its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin;</see>
		/// (-x, +y) or (+x, -y).
		/// </summary>
		NESW,

		/// <summary>
		/// Specifies that the DragHandle will be able to move on the south-
		/// east-northwest diagonal axis, to both sides of its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin;</see>
		/// (-x, -y) or (+x, +y).
		/// </summary>
		SENW,

		/// <summary>
		/// Specifies that the DragHandle will be able to move from its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// to the northeast; (+x, -y).
		/// </summary>
		NE,

		/// <summary>
		/// Specifies that the DragHandle will be able to move from its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// to the northwest; (-x, -y).
		/// </summary>
		NW,

		/// <summary>
		/// Specifies that the DragHandle will be able to move from its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// to the southeast; (+x, +y).
		/// </summary>
		SE,

		/// <summary>
		/// Specifies that the DragHandle will be able to move from its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// to the southwest; (-x, +y).
		/// </summary>
		SW,

		/// <summary>
		/// Specifies that the DragHandle will be able to move from its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// to the east (+x, y).
		/// </summary>
		E,

		/// <summary>
		/// Specifies that the DragHandle will be able to move from its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// to the north (x, -y).
		/// </summary>
		N,

		/// <summary>
		/// Specifies that the DragHandle will be able to move from its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// to the south (x, +y).
		/// </summary>
		S,

		/// <summary>
		/// Specifies that the DragHandle will be able to move from its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// to the west (-x, y).
		/// </summary>
		W
	};

	/// <summary>
	/// Event arguments for the DragHandle class events
	/// </summary>
	public class DragHandleEventArgs : EventArgs
	{
		/// <summary>
		/// The position of the handle when the event was raised. The
		/// passed coordinates are relative to the 
		/// <see cref="P:DrawControls.DragHandle.ReferenceElement"> coordinate
		/// space, if one was set. Otherwise they are relative to the parent
		/// UIElement of the handle.</see>
		/// </summary>
		public Point Position { get; set; }

		/// <summary>
		/// The position of the handle's 
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">HandleOrigin</see>
		/// at the time the event was raised.
		/// </summary>
		public Point Origin { get; set; }
	}

	/// <summary>
	/// DragHandle represents a control that can contain a shape, and that
	/// can be moved around the screen with a mouse. Its movement can be
	/// constrained in various directions, and it supports several events
	/// to provide clients with notifications when it is moved or clicked.
	/// </summary>
	public partial class DragHandle : UserControl
	{
		/// <summary>
		/// Creates a DragHandle initially positioned at 0,0 with its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// at 0,0.
		/// </summary>
		public DragHandle()
		{
			InitializeComponent();
			InitHandle();
		}

		/// <summary>
		/// Creates a DragHandle initially positioned at <c>position</c> with its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>
		/// at <c>position</c>.
		/// </summary>
		public DragHandle(Point position)
		: base()
		{
			InitializeComponent();
			HandlePosition = position;
			InitHandle();
		}

		/// <summary>
		/// Causes the DragHandle to relocate to its
		/// <see cref="P:DrawControls.DragHandle.HandleOrigin">origin</see>.
		/// </summary>
		public void SnapToOrigin()
		{
			HandlePosition = HandleOrigin;
		}

		/// <summary>
		/// The Click event is raised when the MouseLeftButtonDown event occurs
		/// on the DragHandle's shape. The handle responds by capturing the
		/// mouse and raising this event.
		/// </summary>
		public event EventHandler<DragHandleEventArgs> Click;

		/// <summary>
		/// The BeginDrag event is raised when the mouse is moved with the left
		/// button down, within the confines of the handle's shape, and the
		/// handle is not already dragging.
		/// </summary>
		public event EventHandler<DragHandleEventArgs> BeginDrag;

		/// <summary>
		/// The Drag event is raised when the mouse is moved with the left
		/// button down, and the handle is in the dragging state (BeginDrag
		/// has been raised).
		/// </summary>
		public event EventHandler<DragHandleEventArgs> Drag;

		/// <summary>
		/// The EndDrag event is raised when the MouseLeftButtonUp event 
		/// occurs with the handle in the dragging state. The handle releases
		/// mouse capture and raises this event.
		/// </summary>
		public event EventHandler<DragHandleEventArgs> EndDrag;

		/// <summary>
		/// Backing DP for the HandleCursor property.
		/// </summary>
		public static readonly DependencyProperty HandleCursorProperty =
			DependencyProperty.Register("HandleCursor", typeof(Cursor),
			typeof(DragHandle), new PropertyMetadata(Cursors.Arrow,
			new PropertyChangedCallback(OnHandleCursorChange)));

		/// <summary>
		/// Gets and sets the <see cref="T:System.Windows.Input.Cursor">
		/// mosue cursor that will be displayed when the mouse is hovered
		/// over the handle. Defaults to Cursors.Arrow.</see>
		/// </summary>
		public Cursor HandleCursor
		{
			get { return (Cursor)GetValue(HandleCursorProperty); }
			set { SetValue(HandleCursorProperty, value); }
		}

		/// <summary>
		/// Handles the property changed event for the HandleCursor DP.
		/// </summary>
		private static void OnHandleCursorChange(DependencyObject source,
		DependencyPropertyChangedEventArgs e)
		{
			((DragHandle)source).UpdateHandleCursor((Cursor)e.NewValue);
		}

		/// <summary>
		/// Called from the property changed event handler for the HandleCursor
		/// DP. If the handle has a shape it sets the cursor.
		/// </summary>
		/// <param name="newCursor">A Cursor</param>
		private void UpdateHandleCursor(Cursor newCursor)
		{
			if (null != _shape)
				_shape.Cursor = newCursor;
		}

		/// <summary>
		/// Backing DP for the HandleDragConstraint property
		/// </summary>
		public static readonly DependencyProperty HandleDragConstraintProperty =
			DependencyProperty.Register("HandleDragConstraint", typeof(DragConstraint),
			typeof(DragHandle), new PropertyMetadata(DragConstraint.None, 
			new PropertyChangedCallback(OnHandleDragConstraintChange) ));

		/// <summary>
		/// Gets and sets the <see cref="T:DrawControls.DragConstraint">DragConstraint</see>
		/// that determines where this handle is allowed to move. Using the compass rose
		/// as a visualization tool, with north at the top of the screen, handles can be
		/// constrained to move EW, NS, NESW, SENW, N, E, S, W, NE, SE, SW, NW.
		/// </summary>
		public DragConstraint HandleDragConstraint
		{
			get { return (DragConstraint)GetValue(HandleDragConstraintProperty); }
			set { SetValue(HandleDragConstraintProperty, value); }
		}

		/// <summary>
		/// Handles the property changed event for the HandleDragConstraint DP
		/// </summary>
		/// <param name="source"></param>
		/// <param name="e"></param>
		private static void OnHandleDragConstraintChange( DependencyObject source,
			DependencyPropertyChangedEventArgs e )
		{
			((DragHandle)source).UpdateHandleDragConstraint( (DragConstraint)e.NewValue );
		}

		/// <summary>
		/// Called from the HandleDragConstraint property changed event handler to
		/// update the HandleDragConstraint
		/// </summary>
		/// <param name="d">DragConstraint to be applied</param>
		private void UpdateHandleDragConstraint( DragConstraint d )
		{
			SetDragConstraint( d );
		}

		/// <summary>
		/// Backing DP for the HandleWidth property
		/// </summary>
		public static readonly DependencyProperty HandleWidthProperty =
			DependencyProperty.Register("HandleWidth", typeof(double),
			typeof(DragHandle), new PropertyMetadata(2.0,
			new PropertyChangedCallback(OnHandleWidthChange)));

		/// <summary>
		/// Gets and sets the width of the handle in pixels. Setting this
		/// property will set the width on the handle's shape, as well as
		/// the backing canvas.
		/// </summary>
		public double HandleWidth
		{
			get { return (double)GetValue(HandleWidthProperty); }
			set { SetValue(HandleWidthProperty, value); }
		}

		/// <summary>
		/// Handles property change events for the HandleWidth DP.
		/// </summary>
		private static void OnHandleWidthChange(DependencyObject source,
			DependencyPropertyChangedEventArgs e)
		{
			((DragHandle)source).UpdateHandleWidth((double)e.NewValue);
		}

		/// <summary>
		/// Called from the property change handler for the HandleWidth
		/// property.
		/// </summary>
		/// <param name="newWidth">A double representing the new width</param>
		private void UpdateHandleWidth(double newWidth)
		{
			this.Width = newWidth;
			if (null != _shape)
				_shape.Width = newWidth;
		}

		/// <summary>
		/// Backing DP for the HandleHeight property
		/// </summary>
		public static readonly DependencyProperty HandleHeightProperty =
			DependencyProperty.Register("HandleHeight", typeof(double),
			typeof(DragHandle), new PropertyMetadata(2.0,
			new PropertyChangedCallback(OnHandleHeightChange)));

		/// <summary>
		/// Gets and sets the height of the handle in pixels. Setting this
		/// property will set the height on the handle's shape, as well as
		/// the backing canvas.
		/// </summary>
		public double HandleHeight
		{
			get { return (double)GetValue(HandleHeightProperty); }
			set { SetValue(HandleHeightProperty, value); }
		}

		/// <summary>
		/// Handles property change events for the HandleWidth DP.
		/// </summary>
		private static void OnHandleHeightChange(DependencyObject source,
			DependencyPropertyChangedEventArgs e)
		{
			((DragHandle)source).UpdateHandleHeight((double)e.NewValue);
		}

		/// <summary>
		/// Called from the property change handler for the HandleHeight
		/// property.
		/// </summary>
		/// <param name="newWidth">A double representing the new height</param>
		private void UpdateHandleHeight(double newHeight)
		{
			this.Height = newHeight;
			if (null != _shape)
				_shape.Height = newHeight;
		}

		/// <summary>
		/// Backing DP for the HandleOffsetX property
		/// </summary>
		public static readonly DependencyProperty HandleOffsetXProperty =
			DependencyProperty.Register("HandleOffsetX", typeof(double),
			typeof(DragHandle), new PropertyMetadata(0.0, 
			new PropertyChangedCallback(OnHandleOffsetXChange)));

		/// <summary>
		/// Gets and sets the offset, in pixels, from the handle's reported position
		/// to its top-left corner. When the handle is drawn its x coordinate 
		/// will be adjusted by adding HandleOffsetX.
		/// </summary>
		public double HandleOffsetX
		{
			get { return (double)GetValue(HandleOffsetXProperty); }
			set { SetValue(HandleOffsetXProperty, value); }
		}

		/// <summary>
		/// Handles property change events for the HandleOffsetX DP
		/// </summary>
		private static void OnHandleOffsetXChange( DependencyObject source,
			DependencyPropertyChangedEventArgs e )
		{
			((DragHandle)source).UpdateHandleOffsetX( (double)e.NewValue );
		}

		/// <summary>
		/// Called from the property change event handler for HandleOffsetX
		/// DP. Updates the control's position on the parent canvas.
		/// </summary>
		/// <param name="newOffset">A double containing the offset</param>
		private void UpdateHandleOffsetX( double newOffset )
		{
			Canvas.SetLeft( this, HandlePosition.X + HandleOffsetX );
		}

		/// <summary>
		/// Backing DP for the HandleOffsetY property
		/// </summary>
		public static readonly DependencyProperty HandleOffsetYProperty =
			DependencyProperty.Register("HandleOffsetY", typeof(double),
			typeof(DragHandle), new PropertyMetadata(0.0, 
			new PropertyChangedCallback(OnHandleOffsetYChange)));

		/// <summary>
		/// Gets and sets the offset from the handle's reported position
		/// to its top-left corner. When the handle is drawn its y coordinate 
		/// will be adjusted by adding HandleOffsetY.
		/// </summary>
		public double HandleOffsetY
		{
			get { return (double)GetValue(HandleOffsetYProperty); }
			set { SetValue(HandleOffsetYProperty, value); }
		}

		/// <summary>
		/// Handles property change events for the HandleOffsetY DP
		/// </summary>
		private static void OnHandleOffsetYChange(DependencyObject source,
			DependencyPropertyChangedEventArgs e)
		{
			((DragHandle)source).UpdateHandleOffsetY((double)e.NewValue);
		}

		/// <summary>
		/// Called from the property change event handler for HandleOffsetX
		/// DP. Updates the control's position on the parent canvas.
		/// </summary>
		/// <param name="newOffset">A double containing the offset</param>
		private void UpdateHandleOffsetY(double newOffset)
		{
			Canvas.SetTop(this, HandlePosition.Y + HandleOffsetY);
		}

		/// <summary>
		/// Backing DP for the HandlePosition property
		/// </summary>
		public static readonly DependencyProperty HandlePositionProperty =
			DependencyProperty.Register("HandlePosition", typeof(Point),
			typeof(DragHandle), new PropertyMetadata(new Point(0, 0),
			new PropertyChangedCallback(OnHandlePositionChange)));

		/// <summary>
		/// Gets and sets the position of the handle. Unless a
		/// <see cref="T:DrawControls.DragHandle.ReferenceElement">reference element</see>
		/// has been set, this position will be relative to the Silverlight plug-in's
		/// overall screen area.
		/// </summary>
		public Point HandlePosition
		{
			get { return (Point)GetValue(HandlePositionProperty); }
			set { SetValue(HandlePositionProperty, value); }
		}

		/// <summary>
		/// Handles property change events for the HandlePosition DP
		/// </summary>
		private static void OnHandlePositionChange(DependencyObject source,
			DependencyPropertyChangedEventArgs e)
		{
			((DragHandle)source).UpdateHandlePosition((Point)e.NewValue);
		}

		/// <summary>
		/// Called from the property change event handler for the HandlePosition
		/// DP/
		/// </summary>
		/// <param name="newPos">A Point containing the new position</param>
		private void UpdateHandlePosition(Point newPos)
		{
			Canvas.SetLeft( this, newPos.X + HandleOffsetX );
			Canvas.SetTop(this, newPos.Y + HandleOffsetY );
		}

		/// <summary>
		/// Backing DP for the HandleOrigin property
		/// </summary>
		public static readonly DependencyProperty HandleOriginProperty =
			DependencyProperty.Register("HandleOrigin", typeof(Point),
			typeof(DragHandle), new PropertyMetadata(new Point(0, 0), null));

		/// <summary>
		/// Gets and sets the <see cref="T:System.Windows.Point">Point</see>
		/// that serves as the handle's origin, or point of reference for
		/// movement calculations.
		/// </summary>
		public Point HandleOrigin
		{
			get { return (Point)GetValue(HandleOriginProperty); }
			set { SetValue(HandleOriginProperty, value); }
		}

		/// <summary>
		/// Backing DP for the HandleShape property
		/// </summary>
		public static readonly DependencyProperty HandleShapeProperty =
			DependencyProperty.Register("HandleShape", typeof(Shape),
			typeof(DragHandle), new PropertyMetadata(null,
			new PropertyChangedCallback(OnHandleShapeChange)));

		/// <summary>
		/// Gets and sets the shape used to display the handle. By default
		/// the handle creates a rectangle and displays it on an unfilled
		/// canvas. Setting this property to a different shape will replace
		/// the rectangle with that shape. 
		/// </summary>
		public Shape HandleShape
		{
			get { return (Shape)GetValue(HandleShapeProperty); }
			set { SetValue(HandleShapeProperty, value); }
		}

		/// <summary>
		/// Handles property change events for the HandleShape DP
		/// </summary>
		/// <param name="source"></param>
		/// <param name="e"></param>
		private static void OnHandleShapeChange(DependencyObject source,
			DependencyPropertyChangedEventArgs e)
		{
			((DragHandle)source).UpdateHandleShape((Shape)e.NewValue);
		}

		/// <summary>
		/// Called from the property change event handler for the HandleShape
		/// DP. Calls UnwireShape() to disconnect event handles from the current
		/// shape, if any, then sets up the new shape's properties, adds it to
		/// the handle canvas, and calls WireShape() to connect its event
		/// handlers.
		/// </summary>
		/// <param name="newShape">The new shape for the handle</param>
		public void UpdateHandleShape(Shape newShape)
		{
			if (null != _shape)
			{
				UnwireShape();
				HandleCanvas.Children.Remove(_shape);
			}
			_shape = newShape;
			_shape.Width = HandleWidth;
			_shape.Height = HandleHeight;
			_shape.Fill = HandleFill;
			_shape.Stroke = HandleStroke;
			_shape.Cursor = HandleCursor;
			HandleCanvas.Children.Add(_shape);
			WireShape();
		}

		/// <summary>
		/// The backing DP for the HandleFill property
		/// </summary>
		public static readonly DependencyProperty HandleFillProperty =
			DependencyProperty.Register("HandleFill", typeof(Brush),
			typeof(DragHandle), new PropertyMetadata(null,
			new PropertyChangedCallback(OnHandleFillChange)));

		/// <summary>
		/// Gets and sets the <see cref="T:System.Windows.Brush">brush</see>
		/// used to fill the handle shape. Defaults to null, which will result
		/// in an unfilled shape.
		/// </summary>
		public Brush HandleFill
		{
			get { return (Brush)GetValue(HandleFillProperty); }
			set { SetValue(HandleFillProperty, value); }
		}

		/// <summary>
		/// Property change event handler for the HandleFill DP
		/// </summary>
		private static void OnHandleFillChange(DependencyObject source,
			DependencyPropertyChangedEventArgs e)
		{
			((DragHandle)source).UpdateHandleFill((Brush)e.NewValue);
		}

		/// <summary>
		/// Called from the HandleFill property change event handler to
		/// update the current shape's fill.
		/// </summary>
		/// <param name="newFill">A Brush which will be used to fill the handle shape</param>
		private void UpdateHandleFill(Brush newFill)
		{
			if (null != _shape)
				_shape.Fill = newFill;
		}

		/// <summary>
		/// Backing DP for the HandleStroke property
		/// </summary>
		public static readonly DependencyProperty HandleStrokeProperty =
			DependencyProperty.Register("HandleStroke", typeof(Brush),
			typeof(DragHandle), new PropertyMetadata(null,
			new PropertyChangedCallback(OnHandleStrokeChange)));

		/// <summary>
		/// Gets and sets the <see cref="T:System.Windows.Brush">brush</see>
		/// used to outline the handle shape. Defaults to null, which results
		/// in an unoutlined shape. If <see cref="T:DrawControls.DragHandle.HandleFill">
		/// HandleFill</see> is also null the handle will be invisible.
		/// </summary>
		public Brush HandleStroke
		{
			get { return (Brush)GetValue(HandleStrokeProperty); }
			set { SetValue(HandleStrokeProperty, value); }
		}

		/// <summary>
		/// Property change event handler for the HandleStroke DP
		/// </summary>
		private static void OnHandleStrokeChange(DependencyObject source,
			DependencyPropertyChangedEventArgs e)
		{
			((DragHandle)source).UpdateHandleStroke((Brush)e.NewValue);
		}

		/// <summary>
		/// Called from the HandleStroke property change event handler to
		/// update the stroke brush for the current shape, if any.
		/// </summary>
		/// <param name="newStroke">A Brush which will be used to outline the handle shape</param>
		private void UpdateHandleStroke(Brush newStroke)
		{
			if (null != _shape)
				_shape.Stroke = newStroke;
		}

		/// <summary>
		/// Backing DP for the HandleStrokeThickness property
		/// </summary>
		public static readonly DependencyProperty HandleStrokeThicknessProperty =
			DependencyProperty.Register("HandleStrokeThickness", typeof(double),
			typeof(DragHandle), new PropertyMetadata(1.0,
			new PropertyChangedCallback(OnHandleStrokeThicknessChange)));

		/// <summary>
		/// Gets and sets the thickness of the stroke used to outline the handle
		/// shape. Defaults to a thickness of 1.0.
		/// </summary>
		public double HandleStrokeThickness
		{
			get { return (double)GetValue(HandleStrokeThicknessProperty); }
			set { SetValue(HandleStrokeThicknessProperty, value); }
		}

		/// <summary>
		/// Property change event handler for the HandleStrokeThickness DP.
		/// </summary>
		private static void OnHandleStrokeThicknessChange(DependencyObject source,
			DependencyPropertyChangedEventArgs e)
		{
			((DragHandle)source).UpdateHandleStrokeThickness((double)e.NewValue);
		}

		/// <summary>
		/// Called from the HandleStrokeThickness property change event handler
		/// to update the stroke thickness for the current shape, if any.
		/// </summary>
		/// <param name="newThickness">A double containing the thickness</param>
		private void UpdateHandleStrokeThickness(double newThickness)
		{
			if (null != _shape)
				_shape.StrokeThickness = newThickness;
		}

		/// <summary>
		/// Backing DP for the ReferenceElement property
		/// </summary>
		public static readonly DependencyProperty ReferenceElementProperty =
			DependencyProperty.Register("ReferenceElement", typeof(UIElement),
			typeof(DragHandle), new PropertyMetadata(null, null));

		/// <summary>
		/// Gets and sets the UIElement whose coordinate space will be used as
		/// the frame or reference for reported handle movements. If this
		/// property is left at the default null value then reported movement
		/// will be relative to the overall Silverlight plug-in onscreen
		/// area.
		/// </summary>
		public UIElement ReferenceElement
		{
			get { return (UIElement)GetValue(ReferenceElementProperty); }
			set { SetValue(ReferenceElementProperty, value); }
		}

		/// <summary>
		/// Backing DP for the HandleMinX property
		/// </summary>
		public static readonly DependencyProperty HandleMinXProperty =
			DependencyProperty.Register("HandleMinX", typeof(double),
			typeof(DragHandle), new PropertyMetadata(Double.MinValue, null));

		/// <summary>
		/// Gets and sets the minimum x coordinate to which the DragHandle will
		/// be allowed to move, regardless of other movement constraints.
		/// </summary>
		public double HandleMinX
		{
			get { return (double)GetValue(HandleMinXProperty); }
			set { SetValue(HandleMinXProperty, value); }
		}

		/// <summary>
		/// Backing DP for the HandleMaxX property
		/// </summary>
		public static readonly DependencyProperty HandleMaxXProperty =
			DependencyProperty.Register("HandleMaxX", typeof(double),
			typeof(DragHandle), new PropertyMetadata(Double.MaxValue, null));

		/// <summary>
		/// Gets and sets the maximum x coordinate to which the DragHandle will
		/// be allowed to move, regardless of other movement constraints.
		/// </summary>
		public double HandleMaxX
		{
			get { return (double)GetValue(HandleMaxXProperty); }
			set { SetValue(HandleMaxXProperty, value); }
		}

		/// <summary>
		/// Backing DP for the HandleMinY property
		/// </summary>
		public static readonly DependencyProperty HandleMinYProperty =
			DependencyProperty.Register("HandleMinY", typeof(double),
			typeof(DragHandle), new PropertyMetadata(Double.MinValue, null));

		/// <summary>
		/// Gets and sets the minimum y coordinate to which the DragHandle will
		/// be allowed to move, regardless of other movement constraints.
		/// </summary>
		public double HandleMinY
		{
			get { return (double)GetValue(HandleMinYProperty); }
			set { SetValue(HandleMinYProperty, value); }
		}

		/// <summary>
		/// Backing DP for the HandleMaxY property
		/// </summary>
		public static readonly DependencyProperty HandleMaxYProperty =
			DependencyProperty.Register("HandleMaxY", typeof(double),
			typeof(DragHandle), new PropertyMetadata(Double.MaxValue, null));

		/// <summary>
		/// Gets and sets the maximum y coordinate to which the DragHandle will
		/// be allowed to move, regardless of other movement constraints.
		/// </summary>
		public double HandleMaxY
		{
			get { return (double)GetValue(HandleMaxYProperty); }
			set { SetValue(HandleMaxYProperty, value); }
		}

		/// <summary>
		/// Called to raise the Click event on the DragHandle
		/// </summary>
		/// <param name="e">DragHandleEventArgs</param>
		protected void OnClick(DragHandleEventArgs e)
		{
			if ( null != Click )
			Click( this, e );
		}

		/// <summary>
		/// Called to raise the BeginDrag event on the DragHandle
		/// </summary>
		/// <param name="e">DragHandleEventArgs</param>
		protected void OnBeginDrag(DragHandleEventArgs e)
		{
			if (null != BeginDrag)
			BeginDrag(this, e);
		}

		/// <summary>
		/// Called to raise the Drag event on the DragHandle
		/// </summary>
		/// <param name="e">DragHandleEventArgs</param>
		protected void OnDrag(DragHandleEventArgs e)
		{
			if (null != Drag)
			Drag(this, e);
		}

		/// <summary>
		/// Called to raise the EndDrag event on the DragHandle
		/// </summary>
		/// <param name="e">DragHandleEventArgs</param>
		protected void OnEndDrag(DragHandleEventArgs e)
		{
			if (null != EndDrag)
			EndDrag(this, e);
		}

		/// <summary>
		/// Sets up the default handle with a Rectangle for a shape
		/// </summary>
		private void InitHandle()
		{
			HandleShape = new Rectangle();
			HandleOrigin = HandlePosition;
		}

		/// <summary>
		/// Called at intialization, and when the handle shape changes. Wires up
		/// the shape's mouse events to handlers
		/// </summary>
		private void WireShape()
		{
			_shape.MouseLeftButtonDown += new MouseButtonEventHandler(_shape_MouseLeftButtonDown);
			_shape.MouseMove += new MouseEventHandler(_shape_MouseMove);
			_shape.MouseLeftButtonUp += new MouseButtonEventHandler(_shape_MouseLeftButtonUp);
		}

		/// <summary>
		/// Called when the handle's shape changes. Disconnects event handlers from the
		/// current shape.
		/// </summary>
		private void UnwireShape()
		{
			_shape.MouseLeftButtonDown -= new MouseButtonEventHandler(_shape_MouseLeftButtonDown);
			_shape.MouseMove -= new MouseEventHandler(_shape_MouseMove);
			_shape.MouseLeftButtonUp -= new MouseButtonEventHandler(_shape_MouseLeftButtonUp);
		}

		// The mouse event handlers operate according to the following rules:
		//
		// - If the left button is pressed on the shape the mouse is captured and
		//   Click is raised.
		// - If the mouse is moved over the shape with the left button down and
		//   the shape is not dragging it goes into drag state and BeginDrag is
		//   raised.
		// - If the mouse is moved over the shape with the left button down and
		//   the shape is dragging Drag is raised.
		// - If the mouse left button is released the mouse capture is released and
		//   EndDrag is raised.


		/// <summary>
		/// Handles the MouseLeftButtonDown event on the handle's shape
		/// </summary>
		/// <param name="sender">A Shape object</param>
		/// <param name="e">MouseButtonEventArgs</param>
		private void _shape_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			_shape.CaptureMouse();
			DragHandleEventArgs args = new DragHandleEventArgs();
			args.Position = e.GetPosition(ReferenceElement);
			args.Origin = HandleOrigin;
			OnClick(args);
			_mouseDown = true;
			e.Handled = true;
		}

		/// <summary>
		/// Handles the MouseLeftButtonUp event on the handle's shape
		/// </summary>
		/// <param name="sender">A Shape object</param>
		/// <param name="e">MouseButtonEventArgs</param>
		private void _shape_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			_shape.ReleaseMouseCapture();
			if ( _dragging )
			{
				DragHandleEventArgs args = new DragHandleEventArgs();
				args.Position = e.GetPosition(ReferenceElement);
				args.Origin = HandleOrigin;
				_dragging = false;
				OnEndDrag(args);
			}
			_mouseDown = false;
			e.Handled = true;
		}

		/// <summary>
		/// Handles the MouseMove event on the handle's shape
		/// </summary>
		/// <param name="sender">A Shape object</param>
		/// <param name="e">MouseEventArgs</param>
		private void _shape_MouseMove(object sender, MouseEventArgs e)
		{
			if (_mouseDown)
			{
				Point pos = e.GetPosition(ReferenceElement);
				if (RequestDragTo(ref pos))
				{
					HandlePosition = pos;
					DragHandleEventArgs args = new DragHandleEventArgs();
					args.Position = pos;
					args.Origin = HandleOrigin;

					if ( !_dragging )
					{
						_dragging = true;
						OnBeginDrag( args );
					}
					else OnDrag(args);
				}
			}
		}

		/// <summary>
		/// Called when the HandleDragConstraint property changes. This method
		/// sets up the appropriate eval delegate to be called when the handle
		/// moves.
		/// </summary>
		/// <param name="d">DragConstraint to be applied</param>
		private void SetDragConstraint( DragConstraint d )
		{
			switch ( d )
			{
				case DragConstraint.None:
					_evalMove = null;
					break;

				case DragConstraint.E:
					_evalMove = new MovementEvalCallback(Eval_MoveE);
					break;

				case DragConstraint.W:
					_evalMove = new MovementEvalCallback(Eval_MoveW);
					break;

				case DragConstraint.N:
					_evalMove = new MovementEvalCallback(Eval_MoveN);
					break;

				case DragConstraint.S:
					_evalMove = new MovementEvalCallback(Eval_MoveS);
					break;

				case DragConstraint.EW:
					_evalMove = new MovementEvalCallback(Eval_MoveEW);
					break;

				case DragConstraint.NS:
					_evalMove = new MovementEvalCallback(Eval_MoveNS);
					break;

				case DragConstraint.NESW:
					_evalMove = new MovementEvalCallback(Eval_MoveNESW);
					break;

				case DragConstraint.SENW:
					_evalMove = new MovementEvalCallback(Eval_MoveSENW);
					break;

				case DragConstraint.NE:
					_evalMove = new MovementEvalCallback(Eval_MoveNE);
					break;

				case DragConstraint.SW:
					_evalMove = new MovementEvalCallback(Eval_MoveSW);
					break;

				case DragConstraint.SE:
					_evalMove = new MovementEvalCallback(Eval_MoveSE);
					break;

				case DragConstraint.NW:
					_evalMove = new MovementEvalCallback(Eval_MoveSW);
					break;
			}
		}

		// The following small methods each provide movement evaluation
		// for a specific DragConstraint

		/// <summary>
		/// Evaluate movement with DragConstraint.None applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveNone( ref Point dest )
		{
			return true;
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.E applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveE(ref Point dest)
		{
			if (dest.X >= HandleOrigin.X)
			{
				dest.Y = HandleOrigin.Y;
				return true;
			}
			else return false;
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.W applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveW(ref Point dest)
		{
			if (dest.X <= HandleOrigin.X)
			{
				dest.Y = HandleOrigin.Y;
				return true;
			}
			else return false;
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.N applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveN(ref Point dest)
		{
			if (dest.Y <= HandleOrigin.Y)
			{
				dest.X = HandleOrigin.X;
				return true;
			}
			else return false;
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.S applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveS(ref Point dest)
		{
			if (dest.Y >= HandleOrigin.Y)
			{
				dest.X = HandleOrigin.X;
				return true;
			}
			else return false;
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.EW applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveEW(ref Point dest)
		{
			dest.Y = HandleOrigin.Y;
			return true;
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.NS applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveNS(ref Point dest)
		{
			dest.X = HandleOrigin.X;
			return true;
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.NESW applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveNESW(ref Point dest)
		{
			dest.Y = HandleOrigin.Y - (dest.X - HandleOrigin.X);
			return true;
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.SENW applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveSENW(ref Point dest)
		{
			dest.Y = HandleOrigin.Y + (dest.X - HandleOrigin.X);
			return true;
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.NE applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveNE(ref Point dest)
		{
			if (dest.X < HandleOrigin.X)
				return false;
			else
			{
				dest.Y = HandleOrigin.Y - (dest.X - HandleOrigin.X);
				return true;
			}
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.SW applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveSW(ref Point dest)
		{
			if (dest.X > HandleOrigin.X)
				return false;
			else
			{
				dest.Y = HandleOrigin.Y - (dest.X - HandleOrigin.X);
				return true;
			}
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.SE applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveSE(ref Point dest)
		{
			if (dest.X < HandleOrigin.X)
				return false;
			else
			{
				dest.Y = HandleOrigin.Y + (dest.X - HandleOrigin.X);
				return true;
			}
		}

		/// <summary>
		/// Evaluate movement with DragConstraint.NW applied
		/// </summary>
		/// <param name="dest">The point the DragHandle wants to move to</param>
		/// <returns>True if the movement is permitted, otherwise false. Note that
		/// even when the return value is true the values of dest.X and dest.Y
		/// may have changed to constrain movement.</returns>
		private bool Eval_MoveNW(ref Point dest)
		{
			if (dest.X > HandleOrigin.X)
				return false;
			else
			{
				dest.Y = HandleOrigin.Y + (dest.X - HandleOrigin.X);
				return true;
			}
		}

		/// <summary>
		/// Called when the handle is dragged to a new position.
		/// </summary>
		/// <param name="dest">The Point to which the handle was moved. 
		/// Parameter may be modified by the evaluation.</param>
		/// <returns>True if movement is allowed, otherwise false</returns>
		private bool RequestDragTo(ref Point dest)
		{
			if (dest == HandleOrigin) 
				return true;

			if (dest.X < HandleMinX || dest.X > HandleMaxX ||
				dest.Y < HandleMinY || dest.Y > HandleMaxY)
				return false;

				return (null != _evalMove ? _evalMove(ref dest) : true);

		}

		private delegate bool MovementEvalCallback(ref Point p);

		private MovementEvalCallback _evalMove;

		private Shape _shape;
		private bool _dragging;
		private bool _mouseDown;
	}
}

Playing With Blocks Listing 1 – DragHandle.xaml

<!--
/******************************************************************************
 * DragHandle.xaml
 * 
 * This module the XAML markup for the DragHandle class.
 *  
 * Date:   2/2009
 * 
 * Copyright (c) 2009, Mark Betz 
 * 
 * All rights reserved. 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 * 
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer. 
 *   * Redistributions in binary form must reproduce the above copyright 
 *     notice, this list of conditions and the following disclaimer in the 
 *     documentation and/or other materials provided with the distribution. 
 *   * Neither the name of the Author nor the names of contributors may be 
 *     used to endorse or promote products derived from this software
 *     without specific prior written permission. 
 *     
 * THIS SOFTWARE IS PROVIDED BY MARK BETZ ''AS IS'' AND ANY  EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL MARK BETZ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 * 
 */
-->
<UserControl x:Class="DrawControls.DragHandle"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
	<Grid>
		<Canvas x:Name="HandleCanvas">
		</Canvas>
	</Grid>
</UserControl>

AvalonLife Listing 4 – ALMainWin.xaml.cs

using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Documents;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Shapes;

namespace AvalonLife
{
	/// <summary>
	/// UIStateChanges
	/// 
	/// This enum provides indicators for the user interface states
	/// that the program can be in. Used to call UIStateChange().
	/// </summary>
	enum UIStateChanges
	{
		ModelCreated,
		ModelSaved,
		ModelSavedAs,
		ModelLoadedFromFile,
		ModelLoadedFromNet,
		ModelCellEdited,
		ModelRun,
		ModelPaused,
		ModelHalted,
		ModelReset,
		ModelPropertiesEdited    
	}
    
	/// <summary>
	/// Used to indicate the current type of cell brush used to pain
	/// the grid
	/// </summary>
	enum CellBrushType
	{
		Radial,
		Linear,
		Solid
	}
    
	enum ALFileType
	{
		None,
		AVL,
		Cells
	}
    
	public partial class ALMainWin : System.Windows.Window
	{

		#region ReticleAdorner class
        
		/// <summary>
		/// ReticleAdorner
		/// 
		/// This class is derived from Adorner, and renders the reticle (crosshairs)
		/// over the main game grid.
		/// </summary>
		public class ReticleAdorner : Adorner
		{
			public ReticleAdorner( UIElement target )
			: base(target)
			{
				_reticleColor = ALSettings.Default.ReticleColor;
				_reticlePen = new Pen(new SolidColorBrush(_reticleColor), 1);
			}
            
			protected override void OnRender( DrawingContext dctxt )
			{
				double height = ((Grid)(this.AdornedElement)).ActualHeight;
				double width = ((Grid)(this.AdornedElement)).ActualWidth;

				Point start = new Point( 0, height / 2 );
				Point end = new Point( width, height / 2 );
				dctxt.DrawLine( _reticlePen, start, end );

				start = new Point( width / 2, 0 );
				end = new Point( width / 2, height );
				dctxt.DrawLine( _reticlePen, start, end );
			}           
        
			/// <summary>
			/// Set this property to change the color of the reticle
			/// </summary>
			private Color _reticleColor;
			public Color ReticleColor
			{
				get
				{
					return _reticleColor;
				}
				set
				{
					_reticleColor = value;
					ALSettings.Default.ReticleColor = _reticleColor;
					_reticlePen = new Pen(new SolidColorBrush(_reticleColor), 1);
				}
			}

			private Pen _reticlePen;
        }
        
        #endregion
        
		public ALMainWin()
		{
			InitializeComponent();
		}

		/// <summary>
		/// PrepMessage( string, int )
		/// 
		/// A simple function to insert line breaks into a string destined for a messagebox.
		/// MessageBox.Show() seems to work differently on XP and Vista. On Vista a long
		/// message is sensibly wrapped at word boundaries. On XP it is not. If you insert
		/// line breaks to look right in XP, they don't look right in Vista. So this is an
		/// attempt to get some consistency.
		/// </summary>
		/// <param name="msg"></param>
		/// <param name="lineLen"></param>
		/// <returns></returns>
		public string PrepMessage(string msg, int lineLen)
		{
			string strout = "";
			int j = 0;
			for (int i = 0; i < msg.Length; i++)
			{
				if ((j >= lineLen && msg[i] == ' ') || i == msg.Length - 1)
				{
					strout += msg.Substring(i - j, j);
					strout += "\n";
					j = 0;
				}
				else j++;
			}
			return strout;
		}

		#region menu bar event handlers

		/// <summary>
		/// Menu_OnGameNew(Object, RoutedEventArgs)
		/// 
		/// Handles the click event for the game menu, new command. Prompts the user and
		/// on yes rebuilds the simulation and life models and resets the grid.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnGameNew(Object sender, RoutedEventArgs e)
		{
			Cursor oldCursor = LifeGrid.Cursor;
			LifeGrid.Cursor = Cursors.Wait;

			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			if ( ExecNew(false) )
				oldCursor = LifeGrid.Cursor;
			else
				_ls.IsPaused = paused;

			LifeGrid.Cursor = oldCursor;
		}
        
		/// <summary>
		/// Menu_OnGameReset(Object, RoutedEventArgs)
		/// 
		/// Handles a click event on the Game menu, Reset item. Prompts the user, and on 'Yes' 
		/// rebuilds the model and simulation controller, and resets various parameters.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnGameReset(Object sender, RoutedEventArgs e)
		{
			Cursor oldCursor = LifeGrid.Cursor;
			LifeGrid.Cursor = Cursors.Wait;

			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			if (MessageBox.Show(this, Properties.Resources.UI_MB_PromptText2, Properties.Resources.UI_MB_CaptionText2,
			MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
			{
				_ls.ResetSim();
				UIStateChange(UIStateChanges.ModelReset);
				oldCursor = LifeGrid.Cursor;
			}
			else
				_ls.IsPaused = paused;

			LifeGrid.Cursor = oldCursor;
		}
        
		/// <summary>
		/// Menu_OnGameSave(Object, RoutedEventArgs)
		/// 
		/// Handles the click event for the game menu, save command. Pauses the sim if
		/// it is not already, and then calls ExecSave()
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnGameSave(Object sender, RoutedEventArgs e)
		{
			Cursor oldCursor = LifeGrid.Cursor;
			LifeGrid.Cursor = Cursors.Wait;

			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			ExecSave(false);

			_ls.IsPaused = paused;

			LifeGrid.Cursor = oldCursor;
		}

		/// <summary>
		/// Menu_OnGameSaveAs(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the "save as" command in the Game menu. The only difference
		/// from the function above is that this one calls ExecSave with true, causing
		/// a new filename to be chosen.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnGameSaveAs(Object sender, RoutedEventArgs e)
		{
			Cursor oldCursor = LifeGrid.Cursor;
			LifeGrid.Cursor = Cursors.Wait;

			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			ExecSave(true);

			_ls.IsPaused = paused;

			LifeGrid.Cursor = oldCursor;
		}

		/// <summary>
		/// Menu_OnGameLoad(Object, RoutedEventArgs)
		/// 
		/// Handles the click event for the game menu, load command. Pauses the sim and
		/// then checks to see if the current game is dirty. If so it pops a save dialog 
		/// and calls ExecSave() if the user answers affirm. It then calls ExecLoad(). If
		/// load fails (ExecLoad returns false) the function unpauses the game (if it was
		/// paused on entry). 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnGameLoad(Object sender, RoutedEventArgs e)
		{
			Cursor oldCursor = LifeGrid.Cursor;
			LifeGrid.Cursor = Cursors.Wait;

			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;
			bool final = false;

			System.Windows.Forms.OpenFileDialog opendlg =
			new System.Windows.Forms.OpenFileDialog();

			opendlg.DefaultExt = ".avl";
			opendlg.Filter = "AvalonLife Saved Games (.avl)|*.avl|Life Lexicon Cells (.cells)|*.cells";
			opendlg.Title = "Load Saved Model";
			if (opendlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				if ( ExecLoadFile(opendlg.FileName) )
				{
					final = true;
				}
			}
			if ( !final )
				_ls.IsPaused = paused;

			LifeGrid.Cursor = oldCursor;
		}

		/// <summary>
		/// Menu_OnGameExit(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the game menu, exit command, and shuts down the application
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnGameExit(Object sender, RoutedEventArgs e)
		{
			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			if ( CheckSave() == true )
				Application.Current.Shutdown();

			_ls.IsPaused = paused;
		}

		/// <summary>
		/// Menu_OnSettingsGridLines(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the settings menu, show grid command, and turns the
		/// grid lines on/off accordingly.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsGridLines(Object sender, RoutedEventArgs e)
		{
			if (MenuSettingsGridLines.IsChecked == false)
			{
				ALSettings.Default.GridOn = true;
				LifeGrid.ShowGridLines = true;
				MenuSettingsGridLines.IsChecked = true;
			}
			else
			{
				ALSettings.Default.GridOn = false;
				LifeGrid.ShowGridLines = false;
				MenuSettingsGridLines.IsChecked = false;
			}
		}

		/// <summary>
		/// Menu_OnSettingsReticle(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the settings menu, show reticle command, and turns the
		/// reticle (crosshairs) on/off accordingly.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsReticle(Object sender, RoutedEventArgs e)
		{
			if ( MenuSettingsReticle.IsChecked == false )
			{
				ALSettings.Default.ReticleOn = true;
				_gridAdornerLayer.GetAdorners(LifeGrid)[0].Visibility = Visibility.Visible;
				MenuSettingsReticle.IsChecked = true;
			}
			else
			{
				ALSettings.Default.ReticleOn = false;
				_gridAdornerLayer.GetAdorners(LifeGrid)[0].Visibility = Visibility.Hidden;
				MenuSettingsReticle.IsChecked = false;
			}
		}

		/// <summary>
		/// Menu_OnSettingsHaltStable(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the Settings | Halt Stable Model command. Flips the
		/// state of this property in ALSettings. Controls whether the simulation will
		/// halt when a model ceases evolving.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsHaltStable(Object sender, RoutedEventArgs e)
		{
			if (MenuSettingsHaltStable.IsChecked == false)
			{    
				MenuSettingsHaltStable.IsChecked = true;
				ALSettings.Default.HaltOnStability = true;
			}
			else
			{
				MenuSettingsHaltStable.IsChecked = false;
				ALSettings.Default.HaltOnStability = false;
			}
		}

		/// <summary>
		/// Menu_OnSettingsGridBackground(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the settings menu, grid background command. Opens a color picker
		/// dialog to let the user choose the grid background color. I'm sure there must be an easier
		/// way to deal with the collision between System.Windows.Media.xxx, which is used by
		/// the WPF shapes and brushes, and System.Drawing.xxx which is used by the standard color
		/// dialog in System.Windows.Forms, but I haven't taken the time to research it yet. So
		/// for the moment this function is full of ugly conversions. Works, though.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsGridBackground(Object sender, RoutedEventArgs e)
		{
			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			System.Windows.Forms.ColorDialog clrDlg = new System.Windows.Forms.ColorDialog();

			clrDlg.AllowFullOpen = true;
			clrDlg.SolidColorOnly = true;
			System.Drawing.Color origColor = System.Drawing.Color.FromArgb(
			((SolidColorBrush)LifeGrid.Background).Color.A,
			((SolidColorBrush)LifeGrid.Background).Color.R,
			((SolidColorBrush)LifeGrid.Background).Color.G,
			((SolidColorBrush)LifeGrid.Background).Color.B );

			clrDlg.Color = origColor;
			if ( clrDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK )
			{
				Color newColor = Color.FromArgb(clrDlg.Color.A, clrDlg.Color.R, clrDlg.Color.G, clrDlg.Color.B);
				ALSettings.Default.GridBackground = newColor;
				SolidColorBrush gridBkgBrush = new SolidColorBrush( newColor );
				LifeGrid.Background = gridBkgBrush; 
			}
			_ls.IsPaused = paused;
		}
        
		/// <summary>
		/// Menu_OnSettingsReticleColor(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the settings menu, reticle color command. Opens a color
		/// picker and allows the user to choose a new color for drawing the reticle.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsReticleColor(Object sender, RoutedEventArgs e)
		{
			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;
			ReticleAdorner ad = _gridAdornerLayer.GetAdorners(LifeGrid)[0] as ReticleAdorner;

			System.Windows.Forms.ColorDialog clrDlg = new System.Windows.Forms.ColorDialog();

			clrDlg.AllowFullOpen = true;
			clrDlg.SolidColorOnly = true;

			if ( clrDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK )
			{
				Color newColor = Color.FromArgb(clrDlg.Color.A, clrDlg.Color.R, clrDlg.Color.G, clrDlg.Color.B);
				ad.ReticleColor = newColor;
				ALSettings.Default.ReticleColor = newColor;
				ad.InvalidateVisual();
			}
			_ls.IsPaused = paused;
		}

		/// <summary>
		/// Menu_OnSettingsCellBrush(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the Settings | Cell Brush command, and pops the brush definer
		/// dialog. Calls ApplyCellBrush on return if the user accepted the dialog.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsCellBrush(Object sender, RoutedEventArgs e)
		{
			Cursor oldCursor = LifeGrid.Cursor;

			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			ALBrushDlg dlg = new ALBrushDlg();

			dlg.Owner = this;
			if ( dlg.ShowDialog() == true )
			{
				LifeGrid.Cursor = Cursors.Wait;
				ApplyRectStyle();
				LifeGrid.Cursor = oldCursor;
			}    
			_ls.IsPaused = paused;
		}
        
		/// <summary>
		/// Menu_OnSettingsGridType(Object, RoutedEventArgs)
		/// 
		/// Handles a click on one of the grid type checkable menuitems in Settings | Grid Type.
		/// The Tag property of these controls has been set to the appropriate value from
		/// the GridType enumeration, so the tag can just be cast and passed through to
		/// SetGridType.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsGridType(Object sender, RoutedEventArgs e)
		{
			GridType gridType = (GridType)((MenuItem)sender).Tag;
			if (_ls.Model.LifeGridType != gridType)
			{
				_ls.Model.LifeGridType = gridType;
				SetGridType(gridType);
				UIStateChange(UIStateChanges.ModelPropertiesEdited);
			}
		}

		/// <summary>
		/// Menu_OnSettingsGridSize(Object, RoutedEventArgs)
		/// 
		/// Handles a click on one of the pre-defined grid sizes in the Settings | Grid Size
		/// | [predefined grid size] menu. Since the predefined model sizes are square we can
		/// just store one dimensionin the object tag at startup, grab it here, and use it to
		/// figure out what size was requested.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsGridSize(Object sender, RoutedEventArgs e)
		{
			Cursor oldCursor = LifeGrid.Cursor;
			LifeGrid.Cursor = Cursors.Wait;

			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			int size = (int)(((MenuItem)sender).Tag);
			if ( _ls.Model.ResizeModel(size, size) )
			{
				InitUIState();
				UIStateChange(UIStateChanges.ModelPropertiesEdited);
			}
			else
				MessageBox.Show(this, Properties.Resources.UI_MB_ResizeFailedMsg, Properties.Resources.UI_MB_ResizeFailedCaption,
					MessageBoxButton.OK, MessageBoxImage.Error);

			_ls.IsPaused = paused;
			LifeGrid.Cursor = oldCursor;    
		}

		/// <summary>
		/// Menu_OnSettingsGridSizeCustom(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the Settings | Grid Size | Custom menu. The dialog enforces type
		/// constraints on the edit, so we know we're getting ints, but we need to check how
		/// large a model the user has asked for. You can ask the sim to make a 1000 x 1000 grid.
		/// I did, and by the time I killed the process five minutes later it had a 1.7 gig
		/// working set. A grid that size requires the system to create about 3 million objects,
		/// plus or minus a couple hundred thousand. So we check to see if the aggregate grid
		/// size is greater than 10k cells, and pop a warning dialog. If the user wants to go
		/// ahead we let them. I've done a 200 x 200 grid and it loads relatively quickly and will
		/// actually run, but not smoothly.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsGridSizeCustom(Object sender, RoutedEventArgs e)
		{
			Cursor oldCursor = LifeGrid.Cursor;
			LifeGrid.Cursor = Cursors.Wait;

			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			ALModelSizeDlg dlg = new ALModelSizeDlg();
			dlg.Owner = this;
			dlg.Tag = _ls.Model;
			dlg.Resize = true;

			if ( dlg.ShowDialog() == true )
			{
				int rows = dlg.Rows;
				int cols = dlg.Columns;
				if ( rows * cols > 10000 )
				{
					string msg = PrepMessage(Properties.Resources.UI_MB_LargeModelMsg, 80);
					msg += "\n\nRequested model size will cause the program to create approximately " 
					+ Convert.ToString((rows * cols) * 3) + " objects.";
					if ( MessageBox.Show(this, msg, Properties.Resources.UI_MB_LargeModelCaption, 
						MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.Cancel )
					{    
						LifeGrid.Cursor = oldCursor;
						return;
					}
				}

				if (_ls.Model.ResizeModel(rows, cols))
				{
					InitUIState();
					UIStateChange(UIStateChanges.ModelPropertiesEdited);
				}
				else
					MessageBox.Show(this, Properties.Resources.UI_MB_ResizeFailedMsg, Properties.Resources.UI_MB_ResizeFailedCaption,
						MessageBoxButton.OK, MessageBoxImage.Error);
			}
			_ls.IsPaused = paused;
			LifeGrid.Cursor = oldCursor;
		}

		/// <summary>
		/// Menu_OnSettingsGridSizeShrink(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the Settings | Grid Size | Shrink to Model menu item. Calls
		/// the model to resize the grid to the model size. See LifeModel.ResizeModel() for
		/// failure conditions.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsGridSizeShrink(Object sender, RoutedEventArgs e)
		{
			Cursor oldCursor = LifeGrid.Cursor;
			LifeGrid.Cursor = Cursors.Wait;

			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;

			if (_ls.Model.ResizeModel(0, 0))
			{
				InitUIState();
				UIStateChange(UIStateChanges.ModelPropertiesEdited);
			}
			else
				MessageBox.Show(this, Properties.Resources.UI_MB_ResizeFailedMsg, Properties.Resources.UI_MB_ResizeFailedCaption,
					MessageBoxButton.OK, MessageBoxImage.Error);

			_ls.IsPaused = paused;
			LifeGrid.Cursor = oldCursor;
		}

		/// <summary>
		/// Menu_OnSettingsGridSettings(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the grid settings menuitem in Settings | Grid Settings. Pops
		/// the grid settings dialog window.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsGridSettings(Object sender, RoutedEventArgs e)
		{
			ALGridDlg dlg = new ALGridDlg();
			dlg.Owner = this;
			dlg.ShowDialog();
		}

		/// <summary>
		/// Menu_OnSettingsModelName(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the model name menuitem in Settings. Pops the model
		/// name dialog window. Tracks changes to the name of the model so it can
		/// update the window title bar after the dialog closes.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnSettingsModelName(Object sender, RoutedEventArgs e)
		{
			ALModelNameDlg dlg = new ALModelNameDlg();
			dlg.Owner = this;
			dlg.Tag = _ls;
			if ( dlg.ShowDialog() == true )
				UIStateChange(UIStateChanges.ModelPropertiesEdited);
		}

		/// <summary>
		/// Menu_OnHelpHowTo(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the help menu, How to Play command. 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnHelpHowTo(Object sender, RoutedEventArgs e)
		{
			System.Windows.Forms.HelpNavigator nav =
			System.Windows.Forms.HelpNavigator.TopicId;

			System.Windows.Forms.Help.ShowHelp(null, @"avalonlife.chm", nav, "1020");
		}

		/// <summary>
		/// Menu_OnHelpAbout(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the help menu, about AvalonLife command.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnHelpAbout(Object sender, RoutedEventArgs e)
		{
			System.Windows.Forms.HelpNavigator nav =
			System.Windows.Forms.HelpNavigator.TopicId;

			System.Windows.Forms.Help.ShowHelp(null, @"avalonlife.chm", nav, "1000");
		}

		/// <summary>
		/// Menu_OnHelpAboutLife(Object, RoutedEventArgs)
		/// 
		/// Handles a click on the help menu, about the Game of Life command.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Menu_OnHelpAboutLife(Object sender, RoutedEventArgs e)
		{
			System.Windows.Forms.HelpNavigator nav =
			System.Windows.Forms.HelpNavigator.TopicId;

			System.Windows.Forms.Help.ShowHelp(null, @"avalonlife.chm", nav, "1010");
		}
        
		#endregion

		#region other ui event handlers

		/// <summary>
		/// ALMainWin_OnLoaded()
		/// 
		/// Handles the loaded event for the main window. Initializes the simulation
		/// model, controller, and display grid.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void ALMainWin_OnLoaded(Object sender, RoutedEventArgs e)
		{
			ExecNew(true);
			RunSpeedSlider.ToolTip = Properties.Resources.UI_RunSpeedSlider_ToolTip;
			StatusGenCount.ToolTip = Properties.Resources.UI_StatusGenCount_ToolTip;
			CellBirthCount.ToolTip = Properties.Resources.UI_CellBirthCount_ToolTip;
			CellDeathCount.ToolTip = Properties.Resources.UI_CellDeathCount_ToolTip;
			PopulationCount.ToolTip = Properties.Resources.UI_PopulationCount_ToolTip;
			PeakPopulationCount.ToolTip = Properties.Resources.UI_PeakPopulationCount_ToolTip;
			MenuGridSizeText.ToolTip = Properties.Resources.UI_GridSize_ToolTip;

			_gridAdornerLayer = AdornerLayer.GetAdornerLayer(LifeGrid);
			ReticleAdorner ad = new ReticleAdorner(LifeGrid);
			_gridAdornerLayer.Add(ad);

			MenuSettingsReticle.IsChecked = ALSettings.Default.ReticleOn;
			if ( MenuSettingsReticle.IsChecked )
				ad.Visibility = Visibility.Visible;
			else    
				ad.Visibility = Visibility.Hidden;


			LifeGrid.Drop += new DragEventHandler( Grid_OnDrop );
			LifeGrid.MouseUp += new MouseButtonEventHandler( Grid_OnMouseUp );
			this.MouseLeave += new MouseEventHandler( Window_OnMouseLeave ); 
			this.Closing += new CancelEventHandler( ALMainWin_OnClosing );

			Application.Current.SessionEnding += new SessionEndingCancelEventHandler( ALMainWin_OnClosing );

			LifeGrid.Background = new SolidColorBrush(ALSettings.Default.GridBackground);
			LifeGrid.ShowGridLines = ALSettings.Default.GridOn;
			MenuSettingsGridLines.IsChecked = LifeGrid.ShowGridLines;

			MenuSettingsGridTorus.Tag = GridType.Torus;
			MenuSettingsGridXCyl.Tag = GridType.XCylinder;
			MenuSettingsGridYCyl.Tag = GridType.YCylinder;
			MenuSettingsGridFinite.Tag = GridType.Finite;

			MenuSettingsGrid40x40.Tag = 40;
			MenuSettingsGrid50x50.Tag = 50;
			MenuSettingsGrid60x60.Tag = 60;
			MenuSettingsGrid70x70.Tag = 70;

			if ( ALSettings.Default.HaltOnStability )
				MenuSettingsHaltStable.IsChecked = true;
			else
				MenuSettingsHaltStable.IsChecked = false;
		}
        
		/// <summary>
		/// ALMainWin_OnClosing(Object, CancelEventArgs)
		/// 
		/// This handles the closing event for the main window. If the game is not
		/// dirty or the user saves it using 'Ok' in CheckSave, then this handler
		/// will commit any settings changes and allow the close to continue. Other
		/// wise if the user clicks Cancel in CheckSave it will cancel.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void ALMainWin_OnClosing(Object sender, CancelEventArgs e )
		{
			bool paused = _ls.IsPaused;
			_ls.IsPaused = true;
			if ( CheckSave() == true )
			{
				if ( ALSettings.Default.Changed )
					ALSettings.Default.Save();
			}
			else
			{
				e.Cancel = true;
			}
			_ls.IsPaused = paused;
		}

		/// <summary>
		/// RunButton_OnClick()
		/// 
		/// Handles the click event for the run button on the main window. The button
		/// changes state when it is clicked. The behavior of the sim drives off of
		/// the IsPaused property of _ls (LifeSim). When the sim is paused the UI allows
		/// editing of the grid cells.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void RunButton_OnClick(Object sender, RoutedEventArgs e)
		{
			if ( _ls.IsPaused )
			{
				_ls.IsPaused = false;
				UIStateChange(UIStateChanges.ModelRun);
			}
			else
			{
				_ls.IsPaused = true;
				UIStateChange(UIStateChanges.ModelPaused);
			}
		}

		/// <summary>
		/// Rect_OnMouseDown(Object, MouseButtonEventArgs)
		/// 
		/// Handles the mouse down event on a Rectangle in the grid, and if the game is
		/// in the paused state (editable) flips the cell state. Since we allow dragging
		/// _lastMouseCell is used to avoid the effects of repeated MouseEnter events
		/// getting fired.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Rect_OnMouseDown(Object sender, MouseButtonEventArgs e)
		{
			if ( _ls.IsPaused && (_ls.Generation == 0) )
			{
				LifeCell lc = ((Rectangle)sender).DataContext as LifeCell;
				if ( lc != null )
				{
					_startEdit = true;
					_lastMouseCell = lc;
					lc.IsAlive = !lc.IsAlive;
					UIStateChange(UIStateChanges.ModelCellEdited);
				}
				else throw (new System.InvalidOperationException("Rect_OnMouseDown"));
			}
		}
        
		/// <summary>
		/// Rect_OnMouseEnter(Object, MouseEventArgs)
		/// 
		/// Handles the mouseover event for an Ellipse. If the game is paused and the mouse
		/// is entering a new cell, flip that cell's state.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Rect_OnMouseEnter(Object sender, MouseEventArgs e)
		{
			if ( _startEdit )
			{
				LifeCell lc = ((Rectangle)sender).DataContext as LifeCell;
				if ( lc != null && lc != _lastMouseCell )
				{
					_lastMouseCell = lc;
					lc.IsAlive = !lc.IsAlive;    
				}
				else if ( lc == null )
					throw (new System.InvalidOperationException("Rect_OnMouseEnter"));
			}
		}
        
		/// <summary>
		/// Grid_OnMouseUp(Object, MouseEventArgs)
		/// 
		/// Handles the mouse left button up event for the grid. We use it to check
		/// the grid state and enable/disable menu items accordingly.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Grid_OnMouseUp(Object sender, MouseButtonEventArgs e)
		{
			if ( _startEdit )
			{
				_startEdit = false;
				if ( _ls.Model.IsEmpty() )
				{
					UIStateChange(UIStateChanges.ModelCreated);
				}
			}
		}

		/// <summary>
		/// Window_OnMouseLeave(Object, MouseEventArgs)
		/// 
		/// This function makes sure we handle things correctly if the user drags
		/// the mouse out of the window while drawing cells. Ordinarily you would
		/// capture the mouse and not release it until mouse up, but that's
		/// cumbersome here, because we're getting events at the rectangle level
		/// but can't capture the mouse there. If we capture it at the grid the
		/// rectangles stop getting events. So this is the next best alternative.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Window_OnMouseLeave(Object sender, MouseEventArgs e)
		{
			if ( _startEdit )
			{
				_startEdit = false;
				if (_ls.Model.IsEmpty())
				{
					UIStateChange(UIStateChanges.ModelCreated);
				}
			}    
		}
        
		/// <summary>
		/// Grid_OnDrop(Object, DragEventArgs)
		/// 
		/// This function gets called when an object is dropped on the grid. It handles drops
		/// of .cells data from the Life Lexicon website, as well as .avl and .cells saved files.
		/// The first half of the function detects a drop of a link from the Lexicon, and calls
		/// ExecLoadWeb to get the data from the net. The second half detects file drops, and hands
		/// off to ExecFileLoad. 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void Grid_OnDrop(Object sender, DragEventArgs e)
		{
			Cursor old = this.Cursor;
			bool force = this.ForceCursor;
			this.ForceCursor = true;
			this.Cursor = Cursors.Wait;

			if (e.Data.GetDataPresent(DataFormats.Text))
			{
				if ( (e.AllowedEffects & DragDropEffects.Link) == DragDropEffects.Link )
				{    
					string url = e.Data.GetData(DataFormats.Text) as string;
					if ( Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) )
					{
						ExecLoadWeb( url );
					}
				}
			}
			else if (e.Data.GetDataPresent(DataFormats.FileDrop) )
			{
				string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];
				ExecLoadFile(files[files.Length - 1]);
			}
			this.Cursor = old;
			this.ForceCursor = force;
		}

		/// <summary>
		/// SimStatusCallback()
		/// 
		/// This function handles a callback from the simulation controller when it detects
		/// that model evolution has halted. Will only be called if ALSettings.HaltOnStability
		/// is true.
		/// </summary>
		/// <returns></returns>
		public void SimStatusCallback()
		{
			string msg = null;

			if ( _ls.Model.EvolutionHalted )
			{
				msg = Properties.Resources.UI_SimStatus_HaltMsg;
				msg += " " + _ls.Generation.ToString();
				msg += "\n\nCell births: " + _ls.Model.CellBirths.ToString();
				msg += "\nCell deaths: " + _ls.Model.CellDeaths.ToString();
				msg += "\nPopulation: " + _ls.Model.Population.ToString();
				msg += "\nPeak population: " + _ls.Model.PeakPopulation.ToString();
				msg += "\n\nThe simulation has been halted.";

				MessageBox.Show(this, msg, "Simulation Status", MessageBoxButton.OK, MessageBoxImage.Information);

				UIStateChange(UIStateChanges.ModelHalted);
			}
		}

		#endregion

		#region ALMainWin private methods

		/// <summary>
		/// UIStateChange(UIStateChanges)
		/// 
		/// This function wraps up all the UI state changes that the program goes through.
		/// The possible states are defined in the AvalonLIfe.UIStateChanges enum. Each
		/// case of the switch statement handles setting the UI controls and some variables
		/// to appropriate settings for a given state.
		/// </summary>
		/// <param name="uis"></param>
		private void UIStateChange(UIStateChanges uis)
		{
			switch (uis)
			{
				case UIStateChanges.ModelCreated:
					RunButton.Content = Properties.Resources.UI_RunButton_Content1;
					RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
					LifeGrid.ToolTip = Properties.Resources.UI_LifeGrid_ToolTip;
					LifeGrid.Cursor = Cursors.Hand;
					RunButton.IsEnabled = false;
					MenuGameSave.IsEnabled = false;
					MenuGameSaveAs.IsEnabled = false;
					MenuGameReset.IsEnabled = false;
					SetGameDirty(false);
					_currentModelFileBase = null;
					break;

				case UIStateChanges.ModelSaved:
				case UIStateChanges.ModelSavedAs:
					MenuGameSave.IsEnabled = false;
					SetGameDirty(false);
					break;

				case UIStateChanges.ModelLoadedFromFile:
					RunButton.Content = Properties.Resources.UI_RunButton_Content1;
					RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
					RunButton.IsEnabled = true;
					LifeGrid.ToolTip = Properties.Resources.UI_LifeGrid_ToolTip;
					LifeGrid.Cursor = Cursors.Hand;
					MenuGameSave.IsEnabled = false;
					MenuGameSaveAs.IsEnabled = true;
					SetGameDirty(false);
					break;

				case UIStateChanges.ModelLoadedFromNet:
					RunButton.Content = Properties.Resources.UI_RunButton_Content1;
					RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
					RunButton.IsEnabled = true;
					LifeGrid.ToolTip = Properties.Resources.UI_LifeGrid_ToolTip;
					LifeGrid.Cursor = Cursors.Hand;
					MenuGameSave.IsEnabled = true;
					MenuGameSaveAs.IsEnabled = true;
					SetGameDirty(true);
					break;

				case UIStateChanges.ModelCellEdited:
					RunButton.IsEnabled = true;
					MenuGameSave.IsEnabled = true;
					MenuGameSaveAs.IsEnabled = true;
					SetGameDirty(true);
					break;

				case UIStateChanges.ModelRun:
					RunButton.Content = Properties.Resources.UI_RunButton_Content2;
					RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip2;
					LifeGrid.Cursor = Cursors.Arrow;
					LifeGrid.ToolTip = null;
					MenuGameReset.IsEnabled = true;
					break;

				case UIStateChanges.ModelPaused:
					RunButton.Content = Properties.Resources.UI_RunButton_Content1;
					RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
					break;

				case UIStateChanges.ModelHalted:
					RunButton.Content = Properties.Resources.UI_RunButton_Content1;
					RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
					RunButton.IsEnabled = false;
					break;

				case UIStateChanges.ModelReset:
					RunButton.Content = Properties.Resources.UI_RunButton_Content1;
					RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
					RunButton.IsEnabled = true;
					LifeGrid.ToolTip = Properties.Resources.UI_LifeGrid_ToolTip;
					LifeGrid.Cursor = Cursors.Hand;
					MenuGameReset.IsEnabled = false;
					break;

				case UIStateChanges.ModelPropertiesEdited:
					MenuGameSave.IsEnabled = true;
					SetGameDirty(true);
					break;
			}
		}

		/// <summary>
		/// UpdateWrapInidicators(bool, bool, bool, bool)
		/// 
		/// This method is called from SetGridType() to enable/disable the wrap indicator
		/// bars that border the grid.
		/// </summary>
		/// <param name="left">True if the left bar is on, else false</param>
		/// <param name="top">True if the top bar is on, else false</param>
		/// <param name="right">True if the right bar is on, else false</param>
		/// <param name="bottom">True if the bottom bar is on, else false</param>
		private void UpdateWrapIndicators( bool left, bool top, bool right, bool bottom )
		{
			if ( left )
				LeftWrapIndicator.Width = 4;
			else
				LeftWrapIndicator.Width = 0;

			if (top)
				TopWrapIndicator.Height = 5;
			else
				TopWrapIndicator.Height = 0;

			if (right)
				RightWrapIndicator.Width = 4;
			else
				RightWrapIndicator.Width = 0;

			if (bottom)
				BottomWrapIndicator.Height = 5;
			else
				BottomWrapIndicator.Height = 0;
		}
        
		/// <summary>
		/// SetGridType( GridType )
		/// 
		/// This method is called from ExecLoad or Menu_OnGridXXX to update the menu and UI to
		/// correspond with the grid type of the loaded model. 
		/// </summary>
		/// <param name="gridType"></param>
		private void SetGridType( GridType gridType )
		{
			MenuSettingsGridTorus.IsChecked = false;
			MenuSettingsGridXCyl.IsChecked = false;
			MenuSettingsGridYCyl.IsChecked = false;
			MenuSettingsGridFinite.IsChecked = false;

			switch ( gridType )
			{
				case GridType.Torus:
					MenuSettingsGridTorus.IsChecked = true;
					UpdateWrapIndicators(false, false, false, false);
					break;

				case GridType.XCylinder:
					MenuSettingsGridXCyl.IsChecked = true;
					UpdateWrapIndicators(false, true, false, true);
					break;

				case GridType.YCylinder:
					MenuSettingsGridYCyl.IsChecked = true;
					UpdateWrapIndicators(true, false, true, false);
					break;

				case GridType.Finite:
					MenuSettingsGridFinite.IsChecked = true;
					UpdateWrapIndicators(true, true, true, true);
					break;
			}
		}
        
		/// <summary>
		/// SetWindowTitle()
		/// 
		/// Called from one or two spots to set the proper window title.
		/// </summary>
		private void SetWindowTitle()
		{
			this.Title = _winTitleBase;
			if (_ls.Model.ModelName != null && _ls.Model.ModelName.Length > 0)
				this.Title += " [" + _ls.Model.ModelName;
			else
				this.Title += " [Untitled";

			if (_gameIsDirty)
				this.Title += "*";

			this.Title += "]";
		}

		/// <summary>
		/// SetGameDirty(bool)
		/// 
		/// Called from various places where the current game becomes "dirty", or in need
		/// of saving to disk. Called with true if the game has become dirty. Updates
		/// the _gameIsDirty flag and window title on a state change.
		/// </summary>
		/// <param name="dirty"></param>
		private void SetGameDirty( bool dirty )
		{
			_gameIsDirty = dirty;
			SetWindowTitle();
		}
        
		/// <summary>
		/// SetGridSizeMenu()
		/// 
		/// Called from InitUIState to update the state of the grid size menu
		/// to reflect the size of a model.
		/// </summary>
		private void SetGridSizeMenu()
		{
			MenuSettingsGrid40x40.IsChecked = false;
			MenuSettingsGrid50x50.IsChecked = false;
			MenuSettingsGrid60x60.IsChecked = false;
			MenuSettingsGrid70x70.IsChecked = false;
			MenuSettingsGridCustom.IsChecked = false;

			if ((_ls.Model.Columns != _ls.Model.Rows) ||
			(_ls.Model.Columns != 40 && _ls.Model.Columns != 50 && 
			_ls.Model.Columns != 60 && _ls.Model.Columns != 70))
			{
				MenuSettingsGridCustom.IsChecked = true;
			}
			else
			{
				switch (_ls.Model.Rows)
				{
					case 40:
						MenuSettingsGrid40x40.IsChecked = true;
						break;
					case 50:
						MenuSettingsGrid50x50.IsChecked = true;
						break;
					case 60:
						MenuSettingsGrid60x60.IsChecked = true;
						break;
					case 70:
						MenuSettingsGrid70x70.IsChecked = true;
						break;
				}
			}
			MenuGridSizeText.Text = "r:" + _ls.Model.Rows.ToString() + " c:" + _ls.Model.Columns.ToString();
		}
        
		/// <summary>
		/// InitUIState()
		/// 
		/// This function peforms common setup work when a game is created or loaded.
		/// Initializes the grid and populates it, sets up some data contexts, and sets
		/// the window title.
		/// </summary>
		private void InitUIState()
		{
			InitGrid();
			PopulateGrid();
			ApplyRectStyle();
			SetGridSizeMenu();
			SetWindowTitle();
			SetGridType(_ls.Model.LifeGridType);

			StatusGenCount.DataContext = _ls;
			RunSpeedSlider.DataContext = _ls;
			CellBirthCount.DataContext = _ls.Model;
			CellDeathCount.DataContext = _ls.Model;
			PopulationCount.DataContext = _ls.Model;
			PeakPopulationCount.DataContext = _ls.Model;
		}

		/// <summary>
		/// ExecNew()
		/// 
		/// Does the grunt work of initializing a new game with an empty grid. Initializes
		/// the model and controller, populates the grid, and wires up some UI fields by setting
		/// data contexts for items with property bindings.
		/// </summary>
		private bool ExecNew(bool defaults)
		{
			bool result = false;

			if ( CheckSave() == true )
			{
				if ( !defaults)
				{
					ALModelSizeDlg dlg = new ALModelSizeDlg();
					dlg.Owner = this;
					dlg.Tag = _ls.Model;
					if ( dlg.ShowDialog() == true )
					{
						int rows = dlg.Rows;
						int cols = dlg.Columns;
						if ( rows * cols > 10000 )
						{
							string msg = PrepMessage(Properties.Resources.UI_MB_LargeModelMsg, 80);
							msg += "\n\nRequested model size will cause the program to create approximately " 
							+ Convert.ToString((rows * cols) * 3) + " objects.";
							if ( MessageBox.Show(this, msg, Properties.Resources.UI_MB_LargeModelCaption, 
								MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.Cancel )
							{    
								return result;
							}
						}
						if (_ls == null)
						{
							_ls = new LifeSim(rows, cols);
							_ls.UICallback = SimStatusCallback;
						}
						else
							_ls.NewModel(rows, cols);
					}
					else return result;
				}
				else
				{
					if (_ls == null)
					{
						_ls = new LifeSim();
						_ls.UICallback = SimStatusCallback;
					}
					else
						_ls.NewModel();
				}
				InitUIState();
				UIStateChange(UIStateChanges.ModelCreated);
				result = true;
			}
			return result;
		}
        
		/// <summary>
		/// ExecLoadWeb(string)
		/// 
		/// This function handles loading a stream of cells from the Life Lexicon website.
		/// Most of the work is actually done in LifeModel.LifeModel(Stream), which is called
		/// from LifeSim.NewModel(Stream).
		/// </summary>
		/// <param name="url"></param>
		/// <returns></returns>
		private bool ExecLoadWeb( string url )
		{
			bool result = false;
			if (CheckSave() == true)
			{
				Uri uri = new Uri(url);

				char[] sep = { '.' };
				string[] splitstr = uri.LocalPath.Split(sep);
				if (string.Compare("cells", splitstr[splitstr.Length - 1], true) == 0)
				{
					HttpWebRequest cellReq = (HttpWebRequest)HttpWebRequest.Create(uri);
					cellReq.Timeout = 10000;
					cellReq.UserAgent = "AvalonLife_1_0";
					try
					{
						HttpWebResponse cellRes = (HttpWebResponse)cellReq.GetResponse();
						if ( _ls.NewModel(cellRes.GetResponseStream()) )
						{
							InitUIState();
							UIStateChange(UIStateChanges.ModelLoadedFromNet);
							_currentModelFileType = ALFileType.None;
							result = true;
						}
						else
							MessageBox.Show(this, "Failed to load model data.", "Load Error",
								MessageBoxButton.OK, MessageBoxImage.Error);

						cellRes.Close();
					}
					catch (WebException wex)
					{
						string msg = "Failed to retrieve cell data. Response: ";
						msg += wex.Response;
						MessageBox.Show(this, msg, "Network Error",
							MessageBoxButton.OK, MessageBoxImage.Error);
					}
				}
				else
				{
					string msg = "Invalid data type: " + url;
					MessageBox.Show(this, msg, "Invalid Data",
						MessageBoxButton.OK, MessageBoxImage.Error);
				}
			}
			return result;
		}
       
		/// <summary>
		/// ExecLoadFile(string)
		/// 
		/// Does the work of loading a game from a file. Handles saved .avl files as
		/// well as .cells files saved from Life Lexicon data.
		/// </summary>
		/// <returns></returns>
		private bool ExecLoadFile(string fileName)
		{
			bool result = false;

			if (CheckSave() == true)
			{
				ALFileType ft = GetFileType(fileName);
				if ( ft == ALFileType.AVL )
				{
					BinaryFormatter formatter = new BinaryFormatter();
					Stream str = File.OpenRead(fileName);
					try
					{
						_ls = formatter.Deserialize(str) as LifeSim;
						_ls.UICallback = SimStatusCallback;
						SetFileType(fileName);
						InitUIState();
						UIStateChange(UIStateChanges.ModelLoadedFromFile);
						result = true;
					}
					catch (System.Runtime.Serialization.SerializationException ex)
					{
						MessageBox.Show(this, Properties.Resources.UI_MB_LoadFailedMsg + " " + ex.Message,
							Properties.Resources.UI_MB_LoadFailedCaption,
							MessageBoxButton.OK, MessageBoxImage.Error);
					}
					finally
					{
						str.Close();
					}
					SetGameDirty(false);
				}
				else if ( ft == ALFileType.Cells )
				{
					Stream str = File.OpenRead(fileName);
					if ( _ls.NewModel(str) )
					{
						SetFileType(fileName);
						InitUIState();
						UIStateChange(UIStateChanges.ModelLoadedFromFile);
						result = true;
					}
					else
						MessageBox.Show(this, "Failed to load model data.", "Load Error",
							MessageBoxButton.OK, MessageBoxImage.Error);
					str.Close();
				}
				else
				{
					string msg = "Invalid file type: " + fileName;
					MessageBox.Show(this, msg, "Invalid File",
					MessageBoxButton.OK, MessageBoxImage.Error);
				}
			}                      
			return result;
		}
        
		/// <summary>
		/// ExecSave()
		/// 
		/// Called from the menu savegame handler, or from the load game handler
		/// if necessary. Saves the current model to disk.
		/// </summary>
		private void ExecSave( bool saveAs )
		{
			System.Windows.Forms.SaveFileDialog savedlg =
				new System.Windows.Forms.SaveFileDialog();

			savedlg.AddExtension = true;
			savedlg.Filter = "AvalonLife Saved Games (.avl)|*.avl|Life Lexicon Cells (.cells)|*.cells";
			bool haveFile = false;

			if (_currentModelFileBase != null && _currentModelFileType != ALFileType.None)
			{
				haveFile = true;
				savedlg.FileName = _currentModelFileBase;
				if ( _currentModelFileType == ALFileType.Cells )
				{
					savedlg.DefaultExt = ".cells";
					savedlg.FilterIndex = 2;
				}
				else if ( _currentModelFileType == ALFileType.AVL )
				{    
					savedlg.DefaultExt = ".avl";
					savedlg.FilterIndex = 1;
				}
			}
			else
			{
				savedlg.FileName = "AvalonLife Saved Game";
				savedlg.DefaultExt = ".avl";
				savedlg.FilterIndex = 1;
			}

			if ( saveAs == true )
				savedlg.Title = "Save Model As";
			else
				savedlg.Title = "Save Model";

			if (savedlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{

				ALFileType ft = GetFileType(savedlg.FileName);

				if ( ft == ALFileType.AVL )
				{
					BinaryFormatter formatter = new BinaryFormatter();
					Stream str = File.OpenWrite(savedlg.FileName);
					try
					{
						formatter.Serialize(str, _ls);
						if ( !saveAs || !haveFile )
							SetFileType(savedlg.FileName);
						UIStateChange(UIStateChanges.ModelSaved);
					}
					catch (System.Exception ex)
					{
						MessageBox.Show(this, Properties.Resources.UI_MB_SaveFailedMsg + " " + ex.InnerException.Message,
						Properties.Resources.UI_MB_SaveFailedCaption,
						MessageBoxButton.OK, MessageBoxImage.Error);
					}
					finally
					{
						str.Close();
					}
				}
				else if ( ft == ALFileType.Cells )
				{
					Stream str = File.OpenWrite(savedlg.FileName);
					try
					{
						_ls.Model.StreamCells(str);
						if (!saveAs || !haveFile)
							SetFileType(savedlg.FileName);
						UIStateChange(UIStateChanges.ModelSaved);
					}
					catch (System.Exception)
					{
					}
					finally
					{
						str.Close();
					}
				}
				else
				{
					string msg = "Invalid file type: " + savedlg.FileName;
					MessageBox.Show(this, msg, "Invalid File",
						MessageBoxButton.OK, MessageBoxImage.Error);
				}
			}
		}
        
		/// <summary>
		/// CheckSave()
		/// 
		/// This utility function checks to see if the current game is dirty. If it is
		/// it asks the user whether they want to save the game before continuing with
		/// whatever operation called this function. Returns true if the operation is
		/// good to proceed, false if the user clicks cancel.
		/// </summary>
		/// <returns></returns>
		private bool CheckSave()
		{
			bool result = true;

			if (_gameIsDirty)
			{
				MessageBoxResult mbres = MessageBox.Show( this, Properties.Resources.UI_MB_PromptText3, Properties.Resources.UI_MB_CaptionText3,
				MessageBoxButton.YesNoCancel, MessageBoxImage.Question );
				if ( mbres == MessageBoxResult.Yes )
					ExecSave(false);
				else if ( mbres == MessageBoxResult.No )
					SetGameDirty(false);
				else if ( mbres == MessageBoxResult.Cancel )
					result = false;
			}
			return result;
		}
        
		/// <summary>
		/// InitGrid()
		/// 
		/// Called from the OnLoaded event handler for the main window to initialize the
		/// UI display grid with the appropriate number of rows and columns based on the
		/// _lm.Rows and _lm.Columns properties. It then adds an ellipse to each cell and
		/// sets its style.
		/// </summary>
		private void InitGrid()
		{
			LifeGrid.Children.Clear();
			LifeGrid.RowDefinitions.Clear();
			LifeGrid.ColumnDefinitions.Clear();

			for (int i = 0; i < _ls.Model.Rows; i++)
			{
				LifeGrid.RowDefinitions.Add(new RowDefinition());
			}
			for (int i = 0; i < _ls.Model.Columns; i++)
			{
				LifeGrid.ColumnDefinitions.Add(new ColumnDefinition());
			}
		}
        
/// <summary>
/// GetCellBrush()
/// 
/// Constructs a brush from the settings in the config file.
/// </summary>
/// <returns></returns>
private Brush GetCellBrush()
{
	CellBrushType brushType = ALSettings.Default.LifeCellBrushType;

	if ( brushType == CellBrushType.Radial )
	{
		RadialGradientBrush brush = new RadialGradientBrush();
		brush.GradientOrigin = new Point(0.5, 0.5);
		brush.RadiusX = 0.5; 
		brush.RadiusY = 0.5;

		brush.GradientStops.Add( new GradientStop(ALSettings.Default.CellBrushC1, ALSettings.Default.CellBrushC1Off) );
		brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC2, ALSettings.Default.CellBrushC2Off));
		brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC3, ALSettings.Default.CellBrushC3Off));

		brush.Freeze();

		return brush;
	}
	else if ( brushType == CellBrushType.Linear )
	{
		LinearGradientBrush brush = new LinearGradientBrush();
		brush.StartPoint = new Point(0, 0);
		brush.EndPoint = new Point(1, 1);

		brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC1, ALSettings.Default.CellBrushC1Off));
		brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC2, ALSettings.Default.CellBrushC2Off));
		brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC3, ALSettings.Default.CellBrushC3Off));

		brush.Freeze();

		return brush;
	}
	else if ( brushType == CellBrushType.Solid )
	{
		SolidColorBrush brush = new SolidColorBrush(ALSettings.Default.CellBrushC1);
		brush.Freeze();

		return brush;
	}
	else
	{    
		SolidColorBrush brush = new SolidColorBrush(Colors.Red);
		brush.Freeze();

		return brush;
	}
}
        
		/// <summary>
		/// ApplyRectStyle()
		/// 
		/// If you look at the source you'll see that this function is only called when a new
		/// grid is being initialized, and when the cell brush has been changed. It builds a new
		/// style that sets the Fill property of a rectangle to the new brush, and then goes
		/// through the children of the grid setting this style. The style is based on an
		/// existing style that binds the opacity property to govern visibility. So why go to
		/// all this trouble to change fill brushes? Why not just set the fill property on the
		/// rects and be done with it? Here's the issue: again, opacity is controlled by a
		/// binding in a style. Element properties override style settings, and they happen at
		/// different times too. If in the process of creating a new grid I set the rectangle
		/// DataContext to point to a LifeCell, which will drive the opacity binding, and then
		/// set the Fill property directly, sometimes, depending on timing, I get a repaint
		/// before the opacity property is correctly set, and the grid renders all the cells
		/// visible. It looks messy, and I don't want the grid repainted until the state of all
		/// the cells is correct. I'm sure there must be other ways to handle suppressing the
		/// repaint, but the issue there is that the flash happens after I return control to
		/// the message pump. If I somehow surpress the repaint when will I unsurpress it? The
		/// best way around this that I have found so far is to do as I have below: change
		/// brushes by building a new style and then applying that style.
		/// </summary>
		private void ApplyRectStyle()
		{
			Brush cellBrush = GetCellBrush();

			Style style = new Style(typeof(Rectangle), (Style)LifeGrid.FindResource(typeof(Rectangle)));
			Setter setter = new Setter();
			setter.Property = Rectangle.FillProperty;
			setter.Value = cellBrush;
			style.Setters.Add(setter);
			LifeGrid.Resources.Remove("RectStyle");
			LifeGrid.Resources.Add("RectStyle", style);

			UIElementCollection rects = LifeGrid.Children;

			foreach (UIElement uie in rects)
				((Rectangle)uie).Style = (Style)(LifeGrid.Resources["RectStyle"]);
		}
        
		/// <summary>
		/// PopulateGrid()
		/// 
		/// Does the work of setting up the rectangles in the cells of the life grid. Creates
		/// the rectangles and assigns them to the grid, adds them to the child collection,
		/// sets up rectangle data contexts for the rect->cell link, sets the rectangle style,
		/// and wires up the rectangle mouse events
		/// </summary>
		private void PopulateGrid()
		{
			for (int row = 0; row < _ls.Model.Rows; row++)
			{
				for (int col = 0; col < _ls.Model.Columns; col++)
				{
					Rectangle rect = new Rectangle();
					Grid.SetRow(rect, row);
					Grid.SetColumn(rect, col);
					LifeGrid.Children.Add(rect);
					rect.DataContext = _ls.Model.CellGrid[row, col];
					rect.MouseDown += new MouseButtonEventHandler(Rect_OnMouseDown);
					rect.MouseMove += new MouseEventHandler(Rect_OnMouseEnter);
				}
			}
		}

		/// <summary>
		/// GetFileType(string)
		/// 
		/// Called from ExecSave to determine the type of file that the user selected.
		/// </summary>
		/// <param name="fileName"></param>
		/// <returns></returns>
		private ALFileType GetFileType( string fileName )
		{
			string ext = System.IO.Path.GetExtension(fileName);
			ALFileType ft = ALFileType.None;

			if (string.Compare(".avl", ext, true) == 0)
				ft = ALFileType.AVL;
			else if (string.Compare(".cells", ext, true) == 0)
				ft = ALFileType.Cells;

			return ft;
		}
        
		/// <summary>
		/// SetFileType( string )
		/// 
		/// Called from the ExecSave and ExecLoad functions. Retrieves and stores
		/// the base filename and sets the file format type.
		/// </summary>
		/// <param name="fileName"></param>
		private ALFileType SetFileType( string fileName )
		{
			_currentModelFileBase = System.IO.Path.GetFileNameWithoutExtension(fileName);
			string ext = System.IO.Path.GetExtension(fileName);

			if ( string.Compare(".avl", ext, true) == 0 )
				_currentModelFileType = ALFileType.AVL;
			else if ( string.Compare(".cells", ext, true) == 0 )
				_currentModelFileType = ALFileType.Cells;
			else 
				_currentModelFileType = ALFileType.None;

			return _currentModelFileType;
		}

		#endregion
        
		/// <summary>
		/// Holds the instance of the simulation controller
		/// </summary>
		private LifeSim _ls = null;

		/// <summary>
		/// True if the game has been modified since last save
		/// </summary>
		private bool _gameIsDirty = false;

		/// <summary>
		/// Tracks the last cell that the mouse was in
		/// </summary>
		private LifeCell _lastMouseCell = null;

		/// <summary>
		/// Container for the ReticleAdorner
		/// </summary>
		private AdornerLayer _gridAdornerLayer = null;

		/// <summary>
		/// True if we are dragging across cells in edit mode
		/// </summary>
		private bool _startEdit = false;

		/// <summary>
		/// If not null contains the last file name used to load or
		/// save the current model.
		/// </summary>
		private string _currentModelFileBase = null;

		private ALFileType _currentModelFileType = ALFileType.AVL;

		/// <summary>
		/// Self-explanatory
		/// </summary>
		private string _winTitleBase = "AvalonLife 1.0";
    }
}

AvalonLife Listing 3 – ALMainWin.xaml

<Window x:Class="AvalonLife.ALMainWin"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Title="AvalonLife 1.0" Height="600" Width="600" Name="ALMainWindow" Background="#FFFFFFFF"
	Loaded="ALMainWin_OnLoaded" >
	<Window.Resources>
		<Style x:Key="RunSpeedSliderStyle" TargetType="{x:Type Slider}">
			<Setter Property="Width" Value="120" />
			<Setter Property="Value" Value="{Binding Path=TimerInterval, Mode=TwoWay}" />
			<Setter Property="Orientation" Value="Horizontal" />
			<Setter Property="HorizontalAlignment" Value="Left" />
			<Setter Property="IsSnapToTickEnabled" Value="True" />
			<Setter Property="Minimum" Value="100" />
			<Setter Property="Maximum" Value="1000" />
			<Setter Property="TickPlacement" Value="BottomRight" />
			<Setter Property="TickFrequency" Value="100" />
			<Setter Property="IsDirectionReversed" Value="True" />
			<Setter Property="IsMoveToPointEnabled" Value="True" />
		</Style>
		<Style x:Key="GenCountStyle" TargetType="{x:Type TextBlock}">
			<Setter Property="Foreground" Value="Blue" />
			<Setter Property="Text" Value="{Binding Path=Generation}" />
		</Style>
		<Style x:Key="BirthCountStyle" TargetType="{x:Type TextBlock}">
			<Setter Property="Foreground" Value="Blue" />
			<Setter Property="Text" Value="{Binding Path=CellBirths}" />
		</Style>
		<Style x:Key="DeathCountStyle" TargetType="{x:Type TextBlock}">
			<Setter Property="Foreground" Value="Blue" />
			<Setter Property="Text" Value="{Binding Path=CellDeaths}" />
		</Style>
		<Style x:Key="PopulationCountStyle" TargetType="{x:Type TextBlock}">
			<Setter Property="Foreground" Value="Blue" />
			<Setter Property="Text" Value="{Binding Path=Population}" />
		</Style>
		<Style x:Key="PeakPopulationCountStyle" TargetType="{x:Type TextBlock}">
			<Setter Property="Foreground" Value="Blue" />
			<Setter Property="Text" Value="{Binding Path=PeakPopulation}" />
		</Style>
	</Window.Resources>
	<Grid>
		<DockPanel>
			<DockPanel DockPanel.Dock="Top" LastChildFill="False"  
				Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}">
				<Menu Margin="0,5,5,2" DockPanel.Dock="Left" Width="Auto" HorizontalAlignment="Left" 
					Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}" >
						<MenuItem Header="Game">
						<MenuItem Name="MenuGameNew" Header="New" Click="Menu_OnGameNew" />
						<MenuItem Name="MenuGameReset" Header="Reset" Click="Menu_OnGameReset" />
						<MenuItem Name="MenuGameSave" Header="Save..." Click="Menu_OnGameSave" />
						<MenuItem Name="MenuGameSaveAs" Header="Save as..." Click="Menu_OnGameSaveAs" />
						<MenuItem Name="MenuGameLoad" Header="Load..." Click="Menu_OnGameLoad" />
						<Separator />
						<MenuItem Header="Exit" Click="Menu_OnGameExit" />
					</MenuItem>
					<MenuItem Header="Settings">
						<MenuItem Name="MenuSettingsGridLines" Header="Show Grid" Click="Menu_OnSettingsGridLines" />
						<MenuItem Name="MenuSettingsReticle" Header="Show Reticle" Click="Menu_OnSettingsReticle" />
						<MenuItem Name="MenuSettingsHaltStable" Header="Halt Stable Model" Click="Menu_OnSettingsHaltStable" />
						<Separator />
						<MenuItem Name="MenuSettingsGridBkgColor" Header="Grid Background..." Click="Menu_OnSettingsGridBackground" />
						<MenuItem Name="MenuSettingsReticleColor" Header="Reticle Color..." Click="Menu_OnSettingsReticleColor" />
						<MenuItem Name="MenuSettingsCellBrush" Header="Cell Color..." Click="Menu_OnSettingsCellBrush" />
						<Separator />
						<MenuItem Header="Grid Type">
							<MenuItem Name="MenuSettingsGridTorus" Header="Torus" Click="Menu_OnSettingsGridType" />
							<MenuItem Name="MenuSettingsGridXCyl" Header="X Cylinder" Click="Menu_OnSettingsGridType" />
							<MenuItem Name="MenuSettingsGridYCyl" Header="Y Cylinder" Click="Menu_OnSettingsGridType" />
							<MenuItem Name="MenuSettingsGridFinite" Header="Finite" Click="Menu_OnSettingsGridType" />
						</MenuItem>
						<MenuItem Header="Grid Size">
							<MenuItem Name="MenuSettingsGrid40x40" Header="40 x 40" Click="Menu_OnSettingsGridSize" />
							<MenuItem Name="MenuSettingsGrid50x50" Header="50 x 50" Click="Menu_OnSettingsGridSize" />
							<MenuItem Name="MenuSettingsGrid60x60" Header="60 x 60" Click="Menu_OnSettingsGridSize" />
							<MenuItem Name="MenuSettingsGrid70x70" Header="70 x 70" Click="Menu_OnSettingsGridSize" />
							<Separator />
							<MenuItem Name="MenuSettingsGridCustom" Header="Custom..." Click="Menu_OnSettingsGridSizeCustom" />
							<Separator />
							<MenuItem Name="MenuSettingsGridShrink" Header="Shrink to Model" Click="Menu_OnSettingsGridSizeShrink" />
						</MenuItem>
						<MenuItem Name="MenuSettingsGridSettings" Header="Grid Settings..." Click="Menu_OnSettingsGridSettings" />
						<Separator />
						<MenuItem Name="MenuSettingsModelName" Header="Model Name..." Click="Menu_OnSettingsModelName" />
					</MenuItem>
					<MenuItem Header="Help">
						<MenuItem Name="MenuHelpHowTo" Header="How to Play..." Click="Menu_OnHelpHowTo" />
						<MenuItem Name="MenuHelpAbout" Header="About AvalonLife..." Click="Menu_OnHelpAbout" />
						<MenuItem Name="MenuHelpAboutLife" Header="About the Game of Life..." Click="Menu_OnHelpAboutLife" />
					</MenuItem>
				</Menu>
				<Button Margin="10,5,5,2" DockPanel.Dock="Right" Name="RunButton" VerticalAlignment="Center" HorizontalAlignment="Right" 
					Click="RunButton_OnClick" Height="20" Width="60" Content="Run" />
				<TextBlock Margin="0,5,5,2" VerticalAlignment="Center" Width="60" Foreground="Blue" Name="MenuGridSizeText" DockPanel.Dock="Right" />
				<TextBlock Margin="0,5,5,2" VerticalAlignment="Center" Width="50" Text="Grid Size:" DockPanel.Dock="Right" />
			</DockPanel>
			<Canvas Name="TopWrapIndicator" Height="5" Width="Auto" DockPanel.Dock="Top" HorizontalAlignment="Stretch" Background="Gray" />
			<StatusBar Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}" 
				Height="30" DockPanel.Dock="Bottom" Padding="4,0,4,0">
				<StatusBarItem>
					<TextBlock Width="Auto" Text="Time:" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Name="StatusGenCount" Width="30" Style="{StaticResource GenCountStyle}" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Width="Auto" Text="Census:" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Name="PopulationCount" Width="30" Style="{StaticResource PopulationCountStyle}" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Width="Auto" Text="Peak:" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Name="PeakPopulationCount" Width="30" Style="{StaticResource PeakPopulationCountStyle}" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Width="Auto" Text="Born:" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Name="CellBirthCount" Width="40" Style="{StaticResource BirthCountStyle}" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Width="Auto" Text="Died:" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Name="CellDeathCount" Width="40" Style="{StaticResource DeathCountStyle}" />
				</StatusBarItem>
				<StatusBarItem>
					<TextBlock Width="Auto" Padding="10,0,0,0">Speed:</TextBlock>
				</StatusBarItem>
				<StatusBarItem>
					<Slider Name="RunSpeedSlider" Style="{StaticResource RunSpeedSliderStyle}" />
				</StatusBarItem>
			</StatusBar>
			<Canvas Name="BottomWrapIndicator" Height="5" Width="Auto" DockPanel.Dock="Bottom" HorizontalAlignment="Stretch" Background="Gray" />
			<Canvas Name="LeftWrapIndicator" Height="Auto" Width="4" DockPanel.Dock="Left" VerticalAlignment="Stretch" Background="Gray" />
			<Canvas Name="RightWrapIndicator" Height="Auto" Width="4" DockPanel.Dock="Right" VerticalAlignment="Stretch" Background="Gray" />
			<Grid Name="LifeGrid" Background="White" ForceCursor="True" AllowDrop="True" >
				<Grid.Resources>
					<Style TargetType="{x:Type Rectangle}">
						<Setter Property="Opacity" Value="{Binding Path=IsAlive}" />
					</Style>
					<Style BasedOn="{StaticResource {x:Type Rectangle}}" TargetType="{x:Type Rectangle}" x:Key="RectStyle" >
						<Setter Property="Fill" Value="Red" />
					</Style>
				</Grid.Resources>
			</Grid>
		</DockPanel>
	</Grid>
</Window>

AvalonLife Listing 2 – LifeSim.cs

using System;
using System.IO;
using System.ComponentModel;
using System.Windows;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;

namespace AvalonLife
{
	[Serializable]
	class LifeSim : INotifyPropertyChanged, ISerializable
	{
		#region LifeSim public interface
		/// <summary>
		/// LifeSim()
		/// 
		/// Constructs an instance of the sim controller and creates an empty model
		/// for it to run. LifeSim is in the paused state after construction, but the
		/// timer is running.
		/// </summary>
		/// <param name="lm"></param>
		public LifeSim()
		{
			_lm = new LifeModel();
			_timerInterval = ALSettings.Default.TimerInterval;
			_timer = new System.Windows.Forms.Timer();
			_timer.Interval = _timerInterval;
			_timer.Tick += new EventHandler(TimerEvent);
			_timer.Tag = this;
			_timer.Start();
		}

		/// <summary>
		/// LifeSim(int, int)
		/// 
		/// Constructs an instance of the sim controller and creates an empty model
		/// for it to run, using the specified dimensions. LifeSim is in the paused
		/// state after construction, but the timer is running.
		/// </summary>
		/// <param name="rows">Height of the requested model grid</param>
		/// <param name="columns">Width of the requested model grid</param>
		public LifeSim(int rows, int columns)
		{
			_lm = new LifeModel(rows, columns);
			_timerInterval = ALSettings.Default.TimerInterval;
			_timer = new System.Windows.Forms.Timer();
			_timer.Interval = _timerInterval;
			_timer.Tick += new EventHandler(TimerEvent);
			_timer.Tag = this;
			_timer.Start();
		}

        #region ISerializable methods
        
		/// <summary>
		/// LifeSim(SerializationInfo, StreamingContext)
		/// 
		/// Called by the BinaryFormatter to construct an instance of LifeSim from a
		/// stream. Deserializes the private members, then the LifeModel deserialization
		/// is called, and finally the timer is created and started. The sim is constructed
		/// in a paused state. The UI is responsible for wiring up events.
		/// </summary>
		/// <param name="info"></param>
		/// <param name="ctxt"></param>
		public LifeSim( SerializationInfo info, StreamingContext ctxt )
		{
			_generation = (int)info.GetValue( "_generation", typeof(int) );
			_timerInterval = (int)info.GetValue( "_timerInterval", typeof(int) );

			_lm = new LifeModel( info, ctxt );

			_isPaused = true;

			_timer = new System.Windows.Forms.Timer();
			_timer.Interval = _timerInterval;
			_timer.Tick += new EventHandler(TimerEvent);
			_timer.Tag = this;
			_timer.Start();
		}
        
		/// <summary>
		/// GetObjectData(SerializationInfo, StreamingContext)
		/// 
		/// Called by the BinaryFormatter to serialize an instance of LifeSim. Serializes
		/// the private members and then calls the LifeModel serialization method directly.
		/// </summary>
		/// <param name="info"></param>
		/// <param name="ctxt"></param>
		public void GetObjectData( SerializationInfo info, StreamingContext ctxt )
		{
			info.AddValue( "_generation", _generation );
			info.AddValue( "_timerInterval", _timerInterval );

			_lm.GetObjectData( info, ctxt );
		}

		#endregion
        
		/// <summary>
		/// ~LifeSim()
		/// 
		/// I haven't verified it, but this is almost certainly unnecessary. The timer
		/// class dispose() should kill the timer and clean up. Anyway no harm done.
		/// </summary>
		~LifeSim()
		{
			if ( _timer != null )
				_timer.Stop();
		}
        
		/// <summary>
		/// TimerEvent(Object, EventArgs)
		/// 
		/// This function handles the timer tick. If the game is in the unpaused state it calls
		/// the LifeModel.Evaluate() method to iterate the model. It then increments the
		/// generation count. It's a static method so we pass in the 'this' pointer for the
		/// LifeSim instance that owns the timer in its Tag property. Useful little things, Tags.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private static void TimerEvent( Object sender, EventArgs e )
		{
			LifeSim ls = ((System.Windows.Forms.Timer)sender).Tag as LifeSim;
			if ( ls != null && !ls.IsPaused )
			{
				if ( !ls._lm.EvolutionHalted || ALSettings.Default.HaltOnStability == false )
				{
					ls._lm.Evaluate();
					ls.Generation++;
				}
				else 
				{
					ls.IsPaused = true;
					if (ls._uiCallback != null)
					{    
						ls._uiCallback();
					}
				} 
			}
			else if ( ls == null )
				throw( new System.InvalidOperationException("TimerEvent") );
		}
        
		/// <summary>
		/// ResetSim()
		/// 
		/// Called from the UI to reset the simulation to its starting condition. On
		/// exit the simulation is paused.
		/// </summary>
		public void ResetSim()
		{
			if ( !_isPaused )
				IsPaused = true;

			Generation = 0;

			_lm.ResetModel();
		}

		/// <summary>
		/// NewModel()
		/// 
		/// Called from the UI during handling of a click on Game | New.
		/// </summary>
		public void NewModel()
		{
			if ( !_isPaused )
				_isPaused = true;

			_lm = new LifeModel();
			Generation = 0;
		}

		/// <summary>
		/// NewModel(int, int)
		/// 
		/// Called from the UI during handling of a click on Game | New. Creates a
		/// new model using the passed in dimensions.
		/// </summary>
		public void NewModel(int rows, int columns)
		{
			if (!_isPaused)
				_isPaused = true;

			_lm = new LifeModel(rows, columns);
			Generation = 0;
		}

		/// <summary>
		/// NewModel(Stream)
		/// 
		/// Called to decode a stream of .cells format data into a LifeModel. The bulk
		/// of the work is done in the LifeModel.LifeModel(Stream) constructor. If
		/// construction fails we will already have warned the user in the constructor,
		/// so all we do here is restore the paused state we had on entry.
		/// </summary>
		/// <param name="str">Stream containing the .cells formated data</param>
		/// <returns></returns>
		public bool NewModel( Stream str )
		{
			bool result = false;
			bool paused = _isPaused;

			_isPaused = true;

			try
			{
				LifeModel lm = new LifeModel( str );
				_lm = lm;
				Generation = 0;
				result = true;
			}
			catch( System.Exception )
			{
				_isPaused = paused;
			}    
			return result;
		}

		#endregion

		#region INotifyPropertyChanged members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		#region LifeSim public properties
		/// <summary>
		/// Counts the generations that the current model has run
		/// </summary>
		private int _generation = 0;
		public int Generation
		{
			get
			{
				return _generation;
			}
			protected set
			{
				_generation = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("Generation"));
			}
		}
        
		/// <summary>
		/// Holds a reference to the simulation model
		/// </summary>
		private LifeModel _lm = null;
		public LifeModel Model
		{
			get
			{
				return _lm;
			}
		}

		/// <summary>
		/// Holds the timer interval, set to default on start
		/// </summary>
		private int _timerInterval = 0;
		public int TimerInterval
		{
			get
			{
				return _timerInterval;
			}
			set
			{
				_timerInterval = value;
				_timer.Interval = _timerInterval;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("TimerInterval"));
			}
		}
        
		/// <summary>
		/// Holds the simulation run state: false if running, true if paused. 
		/// </summary>
		private bool _isPaused = true;
		public bool IsPaused
		{
			get
			{
				return _isPaused;
			}
			set
			{
				_isPaused = value;
			}
		}

		/// <summary>
		/// The timer handler may detect that the model has ceased evolving,
		/// i.e. entered a stable state. In that case it will make a call to
		/// the function in this delegate to inform the UI. The UI is responsible
		/// for setting a function-typed value to this delegate member if it
		/// wants to receive this callback.
		/// </summary>
		public delegate void UISimStatusCallback();

		private UISimStatusCallback _uiCallback = null;
		public UISimStatusCallback UICallback
		{
			get
			{
				return _uiCallback;
			}
			set
			{
				_uiCallback = value;
			}
		}

		#endregion
        
		#region LifeSim private data

		private System.Windows.Forms.Timer _timer = null;

		#endregion
	}
}

AvalonLife Listing 1 – LifeModel.cs

using System;
using System.IO;
using System.Windows.Media;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Runtime.Serialization;

namespace AvalonLife
{
	/// <summary>
	/// GridType
	/// 
	/// This enumeration is used to specify the type of grid bounding that
	/// the model will use:
	/// 
	/// Torus - the cells on all edges of the grid wrap to the opposite edge.
	/// XCylinder - the cells on the x axis edges wrap, the y-axis is finite.
	/// YCylinder - the cells on the y axis edges wrap, the x-axis is finite.
	/// Finite - all four edges of the grid are finite.
	/// 
	/// </summary>
	enum GridType
	{
		Torus,
		XCylinder,
		YCylinder,
		Finite
	};
    
	/// <summary>
	/// I just wanted a simple rectangle class with four numbers in it, to use in
	/// resizing the model grid (I use it as return type from a function that
	/// calculates the extent of the current model).
	/// </summary>
	struct ALRectangle
	{
		public int Left;
		public int Top;
		public int Right;
		public int Bottom;
	}
    
	/// <summary>
	/// The LifeCell class represents a single cell and its live/dead state
	/// </summary>
	class LifeCell : INotifyPropertyChanged
	{
		#region LifeCell public properties


		private bool _isAlive = false;
		public bool IsAlive
		{
			get
			{
				return _isAlive;
			}
			set
			{
				_isAlive = value;
				if ( PropertyChanged != null )
					PropertyChanged( this, new PropertyChangedEventArgs("IsAlive") );
			}
		}
		#endregion
        
		#region INotifyPropertyChanged members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion 
	}
    
	/// <summary>
	/// The LifeModel class represents a grid of life cells
	/// </summary>
	[Serializable]
	class LifeModel : ISerializable, INotifyPropertyChanged
	{
		#region LifeModel public interface

		/// <summary>
		/// LifeModel()
		/// 
		/// Initializes the LifeCell array with the default bounds
		/// </summary>
		public LifeModel()
		{
			InitArrays( ALSettings.Default.GridHeight, ALSettings.Default.GridWidth );
		}

		/// <summary>
		/// LifeModel(int, int)
		/// 
		/// Initializes the LifeCell array with the passed in bounds
		/// </summary>
		/// <param name="columns">int, width of the life grid</param>
		/// <param name="rows">int, height of the life grid</param>
		public LifeModel( int rows, int columns )
		{
			InitArrays( rows, columns ); 
		}

		/// <summary>
		/// LifeModel(Stream)
		/// 
		/// Constructs a LifeModel from a stream of .cells data compatible with the
		/// Life Lexicon website.
		/// </summary>
		/// <param name="str"></param>
		public LifeModel( Stream str )
		{
			int copyRow = 0;
			int copyCol = 0;

			StreamReader reader = new StreamReader(str);
			string input = reader.ReadToEnd();
			char[] sep = { '\n' };
			string[] inputs = input.Split(sep);
			if ( inputs.Length < 2 )
				throw( new System.Exception("Bad cell data format") );

			_modelName = inputs[0].Substring(6, inputs[0].Length - 6);

			int newRows = inputs.Length - 3;
			int newCols = inputs[2].Length;
			int defRows = ALSettings.Default.GridHeight;
			int defCols = ALSettings.Default.GridWidth;

			if ( !ALSettings.Default.ShrinkGridToModel )
			{
				if (newRows < defRows)
					copyRow = (defRows - newRows) / 2;
				else
					defRows = newRows;

				if (newCols < defCols)
					copyCol = (defCols - newCols) / 2;
				else
					defCols = newCols;
			}
			else
			{
				defRows = newRows;
				defCols = newCols;
			}

		_evaluated = false;
		PeakPopulation = 0;
		Population = 0;
		_gridType = ALSettings.Default.DefaultGridType;

		InitArrays(defRows, defCols);

		BuildWorkGrid();

		for ( int row = 0; row < newRows; row++ )
		{
			for ( int col = 0; col < newCols; col++ )
			{
				bool set = false;
				if ( inputs[row + 2][col] == '\x4f' )
				{    
					set = true;
				}
				else if ( inputs[row + 2][col] != '\x2e' )
					throw( new System.Exception("Invalid character in cell data: " + inputs[row + 2][col].ToString()) );

				_cellGrid[row + copyRow, col + copyCol].IsAlive = 
				_lastGrid[row + copyRow, col + copyCol] = 
				_startingGrid[row + copyRow, col + copyCol] = set;
				}
			}
		}

		#region ISerializable methods

		/// <summary>
		/// LifeModel(SerializationInfo, StreamingContext)
		/// 
		/// Called by the BinaryFormatter to construct a LifeModel instance from a stream. Expects
		/// _lastGrid to contain the most recent grid array, and copies it into a newly constructed
		/// array of LifeCells.
		/// </summary>
		/// <param name="info"></param>
		/// <param name="ctxt"></param>
		public LifeModel( SerializationInfo info, StreamingContext ctxt )
		{
			_rows = (int)info.GetValue( "_rows", typeof(int) );
			_columns = (int)info.GetValue( "_columns", typeof(int) );
			_startingGrid = (bool[,])info.GetValue( "_startingGrid", typeof(bool[,]) );
			_lastGrid = (bool[,])info.GetValue("_lastGrid", typeof(bool[,]) );
			_evaluated = (bool)info.GetValue( "_evaluated", typeof(bool) );
			_peakPopulation = (int)info.GetValue( "_peakPopulation", typeof(int) );
			_gridType = (GridType)info.GetValue( "_gridType", typeof(GridType) );
			_modelName = (string)info.GetValue( "_modelName", typeof(string) );
			_cellGrid = new LifeCell[_rows, _columns];

			for ( int row = 0; row < _rows; row++ )
			{
				for ( int col = 0; col < _columns; col++ )
				{    
					_cellGrid[row, col] = new LifeCell();
					_cellGrid[row, col].IsAlive = _lastGrid[row, col];
				}
			}
			BuildWorkGrid();
		}

		/// <summary>
		/// GetObjectData(SerializationInfo, StreamingContext)
		/// 
		/// Called by the BinaryFormatter to serialize the data for a LifeModel
		/// into a stream. If the model has never been evaluated (i.e. edits have been
		/// made to the grid but the sim hasn't been run, the contents of the grid array
		/// are copied into _lastGrid first, and then serialization occurs.
		/// </summary>
		/// <param name="info"></param>
		/// <param name="ctxt"></param>
		public void GetObjectData( SerializationInfo info, StreamingContext ctxt )
		{
			if ( !_evaluated )
			{
				for ( int row = 0; row < _rows; row++ )
				{
					for ( int col = 0; col < _columns; col++ )
					_lastGrid[row, col] = _cellGrid[row, col].IsAlive;
				}
			}
			info.AddValue( "_rows", _rows );
			info.AddValue( "_columns", _columns );
			info.AddValue( "_startingGrid", _startingGrid );
			info.AddValue( "_lastGrid", _lastGrid );
			info.AddValue( "_evaluated", _evaluated );
			info.AddValue( "_gridType", _gridType );
			info.AddValue( "_modelName", _modelName );
			info.AddValue( "_peakPopulation", _peakPopulation );
		}

		/// <summary>
		/// StreamCells(Stream)
		/// 
		/// Writes out the current model grid to the passed in Stream in .cells
		/// format compatible with the format on the Life Lexicon website. Note
		/// that this format doesn't store any of the additional information, such
		/// as grid type and starting state, that the .avl format saves.
		/// </summary>
		/// <param name="str"></param>
		public void StreamCells( Stream str )
		{
			str.WriteByte( 0x21 );

			byte[] bytes;
			if ( _modelName != null && _modelName.Length > 0 )
				bytes = Encoding.UTF8.GetBytes( "Name: " + _modelName );
			else
				bytes = Encoding.UTF8.GetBytes( "Name: untitled" );

			str.Write( bytes, 0, bytes.Length );
			str.WriteByte( 0x0a );

			str.WriteByte( 0x21 );
			str.WriteByte( 0x0a );

			for ( int row = 0; row < _rows; row++ )
			{
				for ( int col = 0; col < _columns; col++ )
				{
					if ( _cellGrid[row, col].IsAlive )
						str.WriteByte( 0x4f );
					else
						str.WriteByte( 0x2e );
				}
				str.WriteByte( 0x0a );
			}
		}

		/// <summary>
		/// ResizeModel(int, int)
		/// 
		/// This function performs a resizing of the grid according to the specified
		/// dimensions. It does not resize the model, i.e. the current set of live cells
		/// but will center the model in the new grid if it is larger. If this function
		/// is passed 0 for the rows and columns it will shrink the grid to the size
		/// of the current model. The function will return false if the specific requested
		/// grid size is too small to contain the current set of live cells.
		/// </summary>
		/// <param name="newRows"></param>
		/// <param name="newCols"></param>
		/// <returns></returns>
		public bool ResizeModel( int newRows, int newCols )
		{
			int copyRow = 0;
			int copyCol = 0;
			int currRows = 0;
			int currCols = 0;

			// if the dimensions haven't changed bail
			if ( newRows == _rows && newCols == _columns )
				return true;

			// if the requested dimensions are larger we can just expand and center
			// the model on the grid
			if ( newRows > _rows && newCols > _columns )
			{
				copyRow = (newRows - _rows) / 2;
				copyCol = (newCols - _columns) / 2;

				bool[,] tempLifeGrid = new bool[_rows, _columns];
				bool[,] tempStartGrid = _lastGrid;

				for ( int row = 0; row < _rows; row++ )
				{
					for ( int col = 0; col < _columns; col++ )
					{    
						tempLifeGrid[row, col] = _cellGrid[row, col].IsAlive;
						tempStartGrid[row, col] = _startingGrid[row, col];
					}
				}
				currRows = _rows;
				currCols = _columns;

				InitArrays(newRows, newCols);
				BuildWorkGrid();

				for ( int row = 0; row < currRows; row++ )
				{
					for ( int col = 0; col < currCols; col++ )
					{    
						_cellGrid[row + copyRow, col + copyCol].IsAlive = tempLifeGrid[row, col];
						_startingGrid[row + copyRow, col + copyCol] = tempStartGrid[row, col];
					}
				}
				return true;
			}

			// if we get here either the requested grid is smaller than the current
			// one, or the user has asked for the grid to be shrunk to the model. In
			// either case we first need to know the extent of the current set of live
			// cells.
			ALRectangle rect = GetModelExtent();
			currRows = (rect.Bottom - rect.Top) + 1;
			currCols = (rect.Right - rect.Left) + 1;

			if ( newRows == 0 ) newRows = currRows;
			if ( newCols == 0 ) newCols = currCols;

			if ( newRows >= currRows && newCols >= currCols )
			{
				copyRow = (newRows - currRows) / 2;
				copyCol = (newCols - currCols) / 2;

				bool[,] tempLifeGrid = new bool[_rows, _columns];
				bool[,] tempStartGrid = _lastGrid;

				for (int row = 0; row < _rows; row++)
				{
					for (int col = 0; col < _columns; col++)
					{
						tempLifeGrid[row, col] = _cellGrid[row, col].IsAlive;
						tempStartGrid[row, col] = _startingGrid[row, col];
					}
				}
				InitArrays(newRows, newCols);
				BuildWorkGrid();
				for (int row = 0; row < currRows; row++)
				{
					for (int col = 0; col < currCols; col++)
					{
						_cellGrid[row + copyRow, col + copyCol].IsAlive = tempLifeGrid[row + rect.Top, col + rect.Left];
						_startingGrid[row + copyRow, col + copyCol] = tempStartGrid[row + rect.Top, col + rect.Left];
					}
				}
				return true;
			}
			return false;
		}

		#endregion

		/// <summary>
		/// IsEmpty()
		/// 
		/// Returns false if at least one cell in the grid is alive
		/// </summary>
		/// <returns></returns>
		public bool IsEmpty()
		{
			bool empty = true;
			for ( int row = 0; row < _rows; row++ )
			{
				for ( int col = 0; col < _columns; col++ )
				{
					if ( _cellGrid[row, col].IsAlive )
					{
						empty = false;
						break;
					}
				}
			} 
			return empty;
		}

		/// <summary>
		/// Called by the LifeSim class to iterate through the array and apply the game
		/// rules once.
		/// </summary>
		public void Evaluate()
		{
			bool[,] temp = new bool[_rows, _columns];

			int population = 0;

			if ( !_evaluated )
			{
				_evaluated = true;
				for ( int row = 0; row < _rows; row++ )
				{
					for ( int col = 0; col < _columns; col++ )
					{    
						_startingGrid[row, col] = _lastGrid[row, col] = _cellGrid[row, col].IsAlive;
						if ( _startingGrid[row, col] )
							_peakPopulation++;
					}
				}
				PeakPopulation = _peakPopulation;
			} 

			for( int row = 0; row < _rows; row++ )
			{
				for ( int col = 0; col < _columns; col++ )
				{
					_lastGrid[row, col] = _cellGrid[row, col].IsAlive;
					int adj = CountAdjacent( row, col );
					if ( _cellGrid[row, col].IsAlive )
					{
						if ( adj == 2 || adj == 3 )
							temp[row, col] = true;
						else
							CellDeaths++;
					}
					else
					{
						if ( adj == 3 )
						{
							temp[row, col] = true;
							CellBirths++;
						}
					}
				}
			}
			for ( int row = 0; row < _rows; row++ )
			{
				for ( int col = 0; col < _columns; col++ )
				{
					if ( temp[row, col] == true )
					{    
						_cellGrid[row, col].IsAlive = true;
						population++;
					}
					else
						_cellGrid[row, col].IsAlive = false;   
				}
			}
			Population = population;
			if ( _peakPopulation < population )
				PeakPopulation = population;
			if ( AreEqualGrids(_cellGrid, _lastGrid) )
				_evoHalted = true;
		}

		/// <summary>
		/// ResetModel()
		/// 
		/// Resets the life model to the starting state by restoring the values in
		/// _startingGrid to the cell array.
		/// </summary>
		public void ResetModel()
		{
			_evaluated = false;
			CellBirths = 0;
			CellDeaths = 0;
			_evoHalted = false;
			Population = 0;
			PeakPopulation = 0;

			for ( int row = 0; row < _rows; row++ )
			{
				for ( int col = 0; col < _columns; col++ )
					_cellGrid[row, col].IsAlive = _startingGrid[row, col];
			}
		}
		#endregion

		#region LifeModel private methods

		/// <summary>
		/// GetModelExtent()
		/// 
		/// Returns an ALRectangle structure with the extent of the current
		/// set of life cells.
		/// </summary>
		/// <returns></returns>
		private ALRectangle GetModelExtent()
		{
			ALRectangle rect;
			rect.Left = _columns / 2;
			rect.Top = _rows / 2;
			rect.Right = rect.Left + 1;
			rect.Bottom = rect.Top + 1;

			for ( int row = 0; row < _rows; row++ )
			{
				for ( int col = 0; col < _columns; col++ )
				{
					if ( _cellGrid[row, col].IsAlive )
					{
						if (row < rect.Top ) rect.Top = row;
						if (row > rect.Bottom ) rect.Bottom = row;
						if (col < rect.Left) rect.Left = col;
						if (col > rect.Right) rect.Right = col;
					}
				}
			}
			return rect;
		}

		/// <summary>
		/// AreEqualGrids(LifeCell[,], bool[,])
		/// 
		/// Called from Evaluate() to determine if evolution has ceased or the sim
		/// has fallen into an  oscillating pattern. Compares an array of LifeCells
		/// to a backup array of booleans and returns true if they are the same
		/// </summary>
		/// <param name="lc"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		private bool AreEqualGrids( LifeCell[,] lc, bool[,] b )
		{
			bool result = true;
			for ( int row = 0; row < _rows; row++ )
			{
				for ( int col = 0; col < _columns; col++ )
				{
					if ( lc[row, col].IsAlive != b[row, col] )
					{
						result = false;
						break;
					}
				}
			}
			return result;
		}

		/// <summary>
		/// CountAdjacent(int, int)
		/// 
		/// This function counts neighbors using the work grid, which returns
		/// the correct values for edge cells depending on the grid type. The work
		/// grid is two cells larger in both dimensions, with the outer cells used
		/// for correct wrapping of neighbor checks, as set up in BuildWorkGrid().
		/// The incoming coordinates are in the _cellGrid space, so this function
		/// shifts them down and right 1 cell to get to _workGrid space.
		/// </summary>
		/// <param name="row"></param>
		/// <param name="column"></param>
		/// <returns></returns>
		private int CountAdjacent(int row, int column)
		{
			int count = 0;

			row++;
			column++;

			// upper left
			if ( _workGrid[row - 1, column - 1].IsAlive )
				count++;

			if ( _workGrid[row - 1, column].IsAlive )
				count++;

			// upper right
			if ( _workGrid[row - 1, column + 1].IsAlive )
				count++;

			// left
			if ( _workGrid[row, column - 1].IsAlive )
				count++;

			// right
			if ( _workGrid[row, column + 1].IsAlive )
				count++;

			// lower left
			if ( _workGrid[row + 1, column - 1].IsAlive )
				count++;

			// lower middle
			if ( _workGrid[row + 1, column].IsAlive )
				count++;

			// lower right
			if ( _workGrid[row + 1, column + 1].IsAlive )
				count++;

			return count;
		}

		/// <summary>
		/// BuildWorkGrid()
		/// 
		/// The work grid is two cells larger in each dimension, with the edge cells being
		/// used for controlling how the model wraps. This method builds the work grid off
		/// of the _cellGrid taking the GridType into account.
		/// </summary>
		private void BuildWorkGrid()
		{
			_workGrid = new LifeCell[_rows + 2, _columns + 2];
			for ( int row = 0; row < _rows + 2; row++ )
			{
				for ( int col = 0; col < _columns + 2; col++ )
				{
					// Handle the corner conditions. A corner cell can only be
					// alive in the case of a torus. In all other grid types it
					// will be dead by one of the other edges. In the case of a
					// torus it wraps to the opposite corner.
					if ( row == 0 && col == 0 )
					{
						switch (_gridType)
						{
							case GridType.Torus:
								_workGrid[row, col] = _cellGrid[_rows - 1, _columns - 1];
								break;
							default:    
								_workGrid[row, col] = new LifeCell();
								break;
						}
					}
					else if ( row == 0 && col == _columns + 1 )
					{
						switch (_gridType)
						{
							case GridType.Torus:
								_workGrid[row, col] = _cellGrid[_rows - 1, 0];
								break;
							default:
								_workGrid[row, col] = new LifeCell();
								break;
						}
					}
					else if (row == _rows + 1 && col == _columns + 1)
					{
						switch (_gridType)
						{
							case GridType.Torus:
								_workGrid[row, col] = _cellGrid[0, 0];
								break;
							default:
								_workGrid[row, col] = new LifeCell();
								break;
						}
					}
					else if (row == _rows + 1 && col == 0)
					{
						switch (_gridType)
						{
							case GridType.Torus:
								_workGrid[row, col] = _cellGrid[0, _columns - 1];
								break;
							default:
								_workGrid[row, col] = new LifeCell();
								break;
						}
					}
					// Handle the non-corner edges. They are dead in the
					// finite case, or the case where they lie along the top/bottom
					// in an x cylinder grid, or the left/right in a y cylinder grid.
					// Otherwise they wrap to the cell on the opposite side.
					else if (row == 0)
					{
						switch( _gridType )
						{
							case GridType.Finite:
							case GridType.XCylinder:
								_workGrid[row, col] = new LifeCell();
								break;
							case GridType.Torus:
							case GridType.YCylinder:
								_workGrid[row, col] = _cellGrid[_rows - 1, col - 1];
								break;
						}
					}
					else if ( row == _rows + 1 )
					{
						switch( _gridType )
						{
							case GridType.Finite:
							case GridType.XCylinder:
								_workGrid[row, col] = new LifeCell();
								break;
							case GridType.Torus:
							case GridType.YCylinder:
								_workGrid[row, col] = _cellGrid[0, col - 1];
								break;
						}
					}
					else if ( col == 0 )
					{
						switch (_gridType)
						{
							case GridType.Finite:
							case GridType.YCylinder:
								_workGrid[row, col] = new LifeCell();
								break;
							case GridType.Torus:
							case GridType.XCylinder:
								_workGrid[row, col] = _cellGrid[row - 1, _columns - 1];
								break;
						}
					}
					else if ( col == _columns + 1 )
					{
						switch (_gridType)
						{
							case GridType.Finite:
							case GridType.YCylinder:
								_workGrid[row, col] = new LifeCell();
								break;
							case GridType.Torus:
							case GridType.XCylinder:
								_workGrid[row, col] = _cellGrid[row - 1, 0];
								break;
						}
					}
					else
						_workGrid[row, col] = _cellGrid[row - 1, col - 1];
				}
			}          
		}

		/// <summary>
		/// InitArrays(int, int)
		/// 
		/// Called from the LifeModel constructors to create the LifeCell array and assign
		/// values to related private members
		/// </summary>
		/// <param name="columns">int, the width (columns) of the grid to be created</param>
		/// <param name="rows">int, the height (rows) of the grid to be created</param>
		private void InitArrays(int rows, int columns )
		{
			if ( columns <= 0 || rows <= 0 )
				throw( new System.ArgumentOutOfRangeException("InitArrays") );

			_columns = columns;
			_rows = rows;

			_cellGrid = new LifeCell[_rows, _columns];

			for ( int row = 0; row < _rows; row++ )
			{
				for ( int col = 0; col < _columns; col++ )
					_cellGrid[row, col] = new LifeCell();
			}

			BuildWorkGrid();

			_startingGrid = new bool[_rows, _columns];
			_lastGrid = new bool[_rows, _columns];
		}
		#endregion

		#region LifeModel Properties

		/// <summary>
		/// The number of columns across the life grid
		/// </summary>
		private int _columns = 0;
		public int Columns
		{
			get
			{
				return _columns;
			}
		}

		/// <summary>
		/// The number of rows down the life grid
		/// </summary>
		private int _rows = 0;
		public int Rows
		{
			get
			{
				return _rows;
			}
		}

		/// <summary>
		/// LifeCell
		/// 
		/// This property gives access to the array of LifeCell objects so that they can be used
		/// as data context in binding to the UI. Not sure I like this design.
		/// </summary>
		private LifeCell[,] _cellGrid = null;
		public LifeCell[,] CellGrid
		{
			get
			{
				if ( _cellGrid != null )
					return _cellGrid;    
				else
					throw( new System.InvalidOperationException("LifeCell_get") );
			}
		}

		/// <summary>
		/// Evaluated
		/// 
		/// True if the model has been evaluated at least once, meaning that the data
		/// in _startingGrid is valid
		/// </summary>
		bool _evaluated = false;
		public bool Evaluated
		{
			get
			{
				return _evaluated;
			}
		}

		/// <summary>
		/// CellBirths
		/// 
		/// Counts the number of cell births. Fires change notification.
		/// </summary>
		private int _cellBirths = 0;
		public int CellBirths
		{
			get
			{
				return _cellBirths;
			}
			protected set
			{
				_cellBirths = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("CellBirths"));
			}
		}

		/// <summary>
		/// CellDeaths
		/// 
		/// Counts the number of cell deaths. Fires change notification.
		/// </summary>
		private int _cellDeaths = 0;
		public int CellDeaths
		{
			get
			{
				return _cellDeaths;
			}
			protected set
			{
				_cellDeaths = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("CellDeaths"));
			}
		}

		/// <summary>
		/// EvolutionHalted
		/// 
		/// True if the model evolution has halted
		/// </summary>
		private bool _evoHalted = false;
		public bool EvolutionHalted
		{
			get
			{
				return _evoHalted;
			}
		}

		/// <summary>
		/// PeakPopulation
		/// 
		/// Tracks the maximum population of cells on the grid. Fires
		/// change notification.
		/// </summary>
		private int _peakPopulation = 0;
		public int PeakPopulation
		{
			get
			{
				return _peakPopulation;
			}
			protected set
			{
				_peakPopulation = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("PeakPopulation"));
			}
		}

		/// <summary>
		/// Population
		/// 
		/// Tracks the current population of the grid at the close of each tick.
		/// Fires change notification.
		/// </summary>
		private int _population = 0;
		public int Population
		{
			get
			{
				return _population;
			}
			protected set
			{
				_population = value;
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs("Population"));
			}
		}

		/// <summary>
		/// GridType
		/// 
		/// Used to set the bounding behavior of the cell grid according to the
		/// GridType enumeration. 
		/// </summary>
		private GridType _gridType = ALSettings.Default.DefaultGridType;
		public GridType LifeGridType
		{
			get
			{
				return _gridType;
			}
			set
			{
				_gridType = value;
				BuildWorkGrid();
			}
		}

		/// <summary>
		/// Contains the name (title) of the current model
		/// </summary>
		private string _modelName = null;
		public string ModelName
		{
			get
			{
				return _modelName;
			}
			set
			{
				_modelName = value;
			}
		}

		#endregion

		#region INotifyPropertyChanged members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		#region LifeModel private data

		// holds the starting grid state so we can revert on demand
		private bool[,] _startingGrid = null;

		// holds the state of the last calculate grid, used to 
		// detect a halt
		private bool[,] _lastGrid = null;

		// holds the working grid, which is two cells larger than
		// the cell grid on each dimension, with the edge cells
		// being set up to wrap correctly according to the 
		// grid type. See BuildWorkGrid()
		private LifeCell[,] _workGrid = null;

		#endregion
	}
}