using System;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace FlowWorkEditDemo
{
///
/// Helper class used to start drag&drop operation and display a dragging object
///
public class ItemsControlDragHelper : IDisposable
{
#region DragDropAdorner
///
/// Renders a visual which can follow the mouse cursor,
/// such as during a drag-and-drop operation.
///
// Copyright (C) Josh Smith - January 2007
private class DragDropAdorner : Adorner
{
#region Data
private Rectangle child = null;
private double offsetLeft = 0;
private double offsetTop = 0;
#endregion // Data
#region Constructor
///
/// Initializes a new instance of DragVisualAdorner.
///
/// The element being adorned.
/// The size of the adorner.
/// A brush to with which to paint the adorner.
public DragDropAdorner(UIElement adornedElement, Size size, Brush brush)
: base(adornedElement)
{
Rectangle rect = new Rectangle();
rect.Fill = brush;
rect.Width = size.Width;
rect.Height = size.Height;
rect.IsHitTestVisible = false;
this.child = rect;
}
#endregion // Constructor
#region Public Interface
#region GetDesiredTransform
///
/// Override.
///
///
///
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
{
GeneralTransformGroup result = new GeneralTransformGroup();
result.Children.Add(base.GetDesiredTransform(transform));
result.Children.Add(new TranslateTransform(this.offsetLeft, this.offsetTop));
return result;
}
#endregion // GetDesiredTransform
#region OffsetLeft
///
/// Gets/sets the horizontal offset of the adorner.
///
public double OffsetLeft
{
get { return this.offsetLeft; }
set
{
this.offsetLeft = value;
UpdateLocation();
}
}
#endregion // OffsetLeft
#region SetOffsets
///
/// Updates the location of the adorner in one atomic operation.
///
///
///
public void SetOffsets(double left, double top)
{
this.offsetLeft = left;
this.offsetTop = top;
this.UpdateLocation();
}
#endregion // SetOffsets
#region OffsetTop
///
/// Gets/sets the vertical offset of the adorner.
///
public double OffsetTop
{
get { return this.offsetTop; }
set
{
this.offsetTop = value;
UpdateLocation();
}
}
#endregion // OffsetTop
#endregion // Public Interface
#region Protected Overrides
///
/// Override.
///
///
///
protected override Size MeasureOverride(Size constraint)
{
this.child.Measure(constraint);
return this.child.DesiredSize;
}
///
/// Override.
///
///
///
protected override Size ArrangeOverride(Size finalSize)
{
this.child.Arrange(new Rect(finalSize));
return finalSize;
}
///
/// Override.
///
///
///
protected override Visual GetVisualChild(int index)
{
return this.child;
}
///
/// Override. Always returns 1.
///
protected override int VisualChildrenCount
{
get { return 1; }
}
#endregion // Protected Overrides
#region Private Helpers
private void UpdateLocation()
{
AdornerLayer adornerLayer = this.Parent as AdornerLayer;
if (adornerLayer != null)
adornerLayer.Update(this.AdornedElement);
}
#endregion // Private Helpers
}
#endregion
private Point? _mouseDown;
private Point _offset;
private ItemsControl _source;
private UIElement _dragScope;
private DragDropAdorner _adorner;
private DragEventHandler _dragOver, _dragEnter, _dragLeave;
public ItemsControlDragHelper(ItemsControl source, UIElement dragScope)
{
if (source == null)
throw new ArgumentNullException("source");
_dragEnter = new DragEventHandler(DragScope_DragEnter);
_dragOver = new DragEventHandler(DragScope_DragOver);
_dragLeave = new DragEventHandler(DragScope_DragLeave);
_source = source;
_dragScope = dragScope;
_source.PreviewMouseLeftButtonDown += SourceMouseLeftButtonDown;
_source.PreviewMouseMove += SourceMouseMove;
}
private void SourceMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var point = e.GetPosition(_source);
if (!IsMouseOverScrollbar(point))
_mouseDown = point;
}
private void SourceMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (_mouseDown.HasValue && HasMoved(e.GetPosition(_source)))
{
var adornerSource = e.OriginalSource as UIElement;
adornerSource = _source.ContainerFromElement(adornerSource) as UIElement;
var input = adornerSource as IInputElement;
if (input != null)
_offset = e.GetPosition(input);
else
_offset = new Point(0, 0);
object subj = e.OriginalSource;
var fe = adornerSource as FrameworkElement;
if (fe != null && fe.Tag != null)
subj = fe.Tag;
DoDragDrop(subj, adornerSource);
e.Handled = true;
}
}
else
_mouseDown = null;
}
private bool HasMoved(Point point)
{
return Math.Abs(point.X - _mouseDown.Value.X) > SystemParameters.MinimumHorizontalDragDistance / 2
|| Math.Abs(point.Y - _mouseDown.Value.Y) > SystemParameters.MinimumVerticalDragDistance / 2;
}
private bool IsMouseOverScrollbar(Point ptMouse)
{
HitTestResult res = VisualTreeHelper.HitTest(_source, ptMouse);
if (res == null)
return false;
DependencyObject depObj = res.VisualHit;
while (depObj != null)
{
if (depObj is System.Windows.Controls.Primitives.ScrollBar)
return true;
// VisualTreeHelper works with objects of type Visual or Visual3D.
// If the current object is not derived from Visual or Visual3D,
// then use the LogicalTreeHelper to find the parent element.
if (depObj is Visual || depObj is System.Windows.Media.Media3D.Visual3D)
depObj = VisualTreeHelper.GetParent(depObj);
else
depObj = LogicalTreeHelper.GetParent(depObj);
}
return false;
}
private void DoDragDrop(object dragItem, UIElement adornerSource)
{
if (adornerSource != null)
{
var rect = VisualTreeHelper.GetDescendantBounds(adornerSource);
var size = new Size((double)Math.Ceiling(rect.Width), (double)Math.Ceiling(rect.Height));
var brush = new VisualBrush(adornerSource);
_adorner = new DragDropAdorner(_dragScope, size, brush);
_adorner.Opacity = 0.7;
_adorner.Visibility = Visibility.Hidden;
}
DragDrop.AddPreviewDragEnterHandler(_dragScope, _dragEnter);
DragDrop.AddPreviewDragOverHandler(_dragScope, _dragOver);
DragDrop.AddPreviewDragLeaveHandler(_dragScope, _dragLeave);
var resultEffects = DragDrop.DoDragDrop(_source, dragItem, DragDropEffects.All);
DragFinished(resultEffects);
DragDrop.RemovePreviewDragEnterHandler(_dragScope, _dragEnter);
DragDrop.RemovePreviewDragOverHandler(_dragScope, _dragOver);
DragDrop.RemovePreviewDragLeaveHandler(_dragScope, _dragLeave);
}
private void DragScope_DragEnter(object sender, DragEventArgs args)
{
if (_adorner != null)
AdornerLayer.GetAdornerLayer(_source).Add(_adorner);
}
private void DragScope_DragOver(object sender, DragEventArgs args)
{
if (_adorner != null)
{
var pos = args.GetPosition(_dragScope);
_adorner.SetOffsets(pos.X - _offset.X, pos.Y - _offset.Y);
_adorner.Visibility = Visibility.Visible;
}
}
private void DragScope_DragLeave(object sender, DragEventArgs args)
{
if (_adorner != null)
AdornerLayer.GetAdornerLayer(_source).Remove(_adorner);
}
protected void DragFinished(DragDropEffects ret)
{
if (_adorner != null)
AdornerLayer.GetAdornerLayer(_source).Remove(_adorner);
_adorner = null;
}
#region IDisposable Members
public void Dispose()
{
_source.MouseLeftButtonDown -= SourceMouseLeftButtonDown;
_source.MouseMove -= SourceMouseMove;
}
#endregion
}
}