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>();
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *