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