Mercurial > silverbladetech
diff Chronosv2/source/DragAndDrop/DragHelper.cs @ 10:443821e55f06
Initial cleaned up add from Codeplex files
author | stevenh7776 stevenhollidge@hotmail.com |
---|---|
date | Tue, 21 Feb 2012 17:25:44 +0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Chronosv2/source/DragAndDrop/DragHelper.cs Tue Feb 21 17:25:44 2012 +0700 @@ -0,0 +1,465 @@ +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Shapes; +using Chronos.Interop; + +namespace Chronos.Presentation.DragAndDrop +{ + public sealed class DragHelper + { + #region · Static Methods · + + private static UIElement GetDragElementOnHitTest(UIElement src, MouseEventArgs args) + { + HitTestResult hr = VisualTreeHelper.HitTest(src, args.GetPosition((IInputElement)src)); + return hr.VisualHit as UIElement; + } + + #endregion + + #region · Fields · + + private UIElement dragSource; + private UIElement dragScope; + private IDataDropObjectProvider callback; + private Point startPoint; + private DragAdorner adorner; + private AdornerLayer layer; + private Window dragdropWindow; + private DragDropEffects allowedEffects; + private bool mouseLeftScope; + private bool isDragging; + private double opacity; + + #endregion + + #region · Properties · + + public double Opacity + { + get { return this.opacity; } + } + + public DragDropEffects AllowedEffects + { + get { return this.allowedEffects; } + set { this.allowedEffects = value; } + } + + #endregion + + #region · Private Properties · + + private UIElement DragSource + { + get { return this.dragSource; } + set + { + this.dragSource = value; + this.WireEvents(value); + } + } + + private UIElement DragScope + { + get { return this.dragScope; } + set { this.dragScope = value; } + } + + private IDataDropObjectProvider Callback + { + get { return this.callback; } + set { this.callback = value; } + } + + private bool IsDragging + { + get { return this.isDragging; } + set { this.isDragging = value; } + } + + private bool AllowsLink + { + get { return ((this.AllowedEffects & DragDropEffects.Link) != 0); } + } + + private bool AllowsMove + { + get { return ((this.AllowedEffects & DragDropEffects.Move) != 0); } + } + + private bool AllowsCopy + { + get { return ((this.AllowedEffects & DragDropEffects.Copy) != 0); } + } + + #endregion + + #region · Constructors · + + public DragHelper(UIElement dragSource, IDataDropObjectProvider callback, UIElement dragScope) + { + this.allowedEffects = DragDropEffects.Copy | DragDropEffects.Move; + this.opacity = 0.7; + this.DragSource = dragSource; + this.Callback = callback; + this.DragScope = dragScope; + } + + #endregion + + #region · DragSource Event Handlers · + + private void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + this.startPoint = e.GetPosition(DragScope); + } + +#if DEBUG + void DragSource_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + System.Diagnostics.Debug.Assert(IsDragging == false); + } +#endif + + private void DragSource_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed && !this.IsDragging) + { + Point position = e.GetPosition((IInputElement)DragScope); + + if (Math.Abs(position.X - startPoint.X) > SystemParameters.MinimumHorizontalDragDistance || + Math.Abs(position.Y - startPoint.Y) > SystemParameters.MinimumVerticalDragDistance) + { + this.StartDrag(e); + } + } + } + + #endregion + + #region · DragScope Event Handlers · + + private void DragScope_DragLeave(object sender, DragEventArgs args) + { + if (args.OriginalSource == this.DragScope) + { + this.mouseLeftScope = true; + } + } + + private void DragScope_DragOver(object sender, DragEventArgs args) + { + if (this.adorner != null) + { + this.adorner.LeftOffset = args.GetPosition(this.DragScope).X; /* - _startPoint.X */ + this.adorner.TopOffset = args.GetPosition(this.DragScope).Y; /* - _startPoint.Y */ + } + } + + #endregion + + #region · Event Wiring · + + private void WireEvents(UIElement uie) + { + Debug.Assert(uie != null); + + if (uie != null) + { + uie.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown); + uie.PreviewMouseMove += new MouseEventHandler(DragSource_PreviewMouseMove); + +#if DEBUG + uie.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonUp); +#endif + } + } + + #endregion + + #region · Private Methods · + + private void StartDrag(MouseEventArgs args) + { + IDataObject data = null; + UIElement dragelement = null; + + // ADD THE DATA + if (this.Callback != null) + { + DragDataWrapper dw = new DragDataWrapper(); + + data = new DataObject(typeof(DragDataWrapper).ToString(), dw); + + if ((this.Callback.SupportedActions & DragDropProviderActions.MultiFormatData) != 0) + { + this.Callback.AppendData(ref data, args); + } + + if ((this.Callback.SupportedActions & DragDropProviderActions.Data) != 0) + { + dw.Data = this.Callback.GetData(); + } + + if ((this.Callback.SupportedActions & DragDropProviderActions.Visual) != 0) + { + dragelement = this.Callback.GetVisual(args); + } + else + { + dragelement = args.OriginalSource as UIElement; + } + + dw.Source = this.DragSource; + dw.Shim = this.Callback; + + Debug.Assert(this.DragScope == null, "The DragDataWrapper is meant for in-proc... Sorry for asserting, just wanted to confirm.. comment out assertion if needed"); + } + else + { + dragelement = args.OriginalSource as UIElement; + data = new DataObject(typeof(UIElement).ToString(), dragelement); + } + + if (dragelement == null || data == null || dragelement == this.DragSource) + { + return; + } + + DragEventHandler dragOver = null; + DragEventHandler dragLeave = null; + QueryContinueDragEventHandler queryContinue = null; + GiveFeedbackEventHandler giveFeedback = null; + DragDropEffects effects = this.GetDragDropEffects(); + DragDropEffects resultEffects; + + // Inprocess Drag ... + if (this.DragScope != null) + { + bool previousAllowDrop = this.DragScope.AllowDrop; + + this.adorner = new DragAdorner(this.DragScope, (UIElement)dragelement, true, this.Opacity); + this.layer = AdornerLayer.GetAdornerLayer(this.DragScope as Visual); + + this.layer.Add(this.adorner); + + if (this.DragScope != this.DragSource) + { + this.DragScope.AllowDrop = true; + + DragDrop.AddPreviewDragOverHandler((DependencyObject)this.DragScope, dragOver = new DragEventHandler(DragScope_DragOver)); + DragDrop.AddPreviewDragLeaveHandler(this.DragScope, dragLeave = new DragEventHandler(DragScope_DragLeave)); + DragDrop.AddPreviewQueryContinueDragHandler(this.DragSource, queryContinue = new QueryContinueDragEventHandler(OnQueryContinueDrag)); + } + + try + { + this.IsDragging = true; + this.mouseLeftScope = false; + resultEffects = DragDrop.DoDragDrop(this.DragSource, data, effects); + + this.DragFinished(resultEffects); + } + catch + { + Debug.Assert(false); + } + + if (this.DragScope != this.DragSource) + { + this.DragScope.AllowDrop = previousAllowDrop; + + DragDrop.RemovePreviewDragOverHandler(this.DragScope, dragOver); + DragDrop.RemovePreviewDragLeaveHandler(this.DragScope, dragLeave); + DragDrop.RemovePreviewQueryContinueDragHandler(this.DragSource, queryContinue); + } + } + else + { + DragDrop.AddPreviewQueryContinueDragHandler(this.DragSource, queryContinue = new QueryContinueDragEventHandler(OnQueryContinueDrag)); + DragDrop.AddGiveFeedbackHandler(this.DragSource, giveFeedback = new GiveFeedbackEventHandler(OnGiveFeedback)); + + this.IsDragging = true; + + if ((this.Callback.SupportedActions & DragDropProviderActions.Visual) != 0) + { + this.CreateDragDropWindow(dragelement); + this.dragdropWindow.Show(); + } + + try + { + resultEffects = DragDrop.DoDragDrop(this.DragSource, data, effects); + } + finally + { + } + + if ((this.Callback.SupportedActions & DragDropProviderActions.Visual) != 0) + { + this.DestroyDragDropWindow(); + } + + DragDrop.RemovePreviewQueryContinueDragHandler(this.DragSource, OnQueryContinueDrag); + DragDrop.AddGiveFeedbackHandler(this.DragSource, OnGiveFeedback); + + this.IsDragging = false; + this.DragFinished(resultEffects); + } + } + + private DragDropEffects GetDragDropEffects() + { + DragDropEffects effects = DragDropEffects.None; + + bool ctrl = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl); + bool shift = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift); + + if (ctrl && shift && this.AllowsLink) + { + effects |= DragDropEffects.Link; + } + else if (ctrl && this.AllowsCopy) + { + effects |= DragDropEffects.Copy; + } + else if (this.AllowsMove) + { + effects |= DragDropEffects.Move; + } + + return effects; + } + + private void OnGiveFeedback(object sender, GiveFeedbackEventArgs args) + { + args.UseDefaultCursors = ((this.Callback.SupportedActions & DragDropProviderActions.Visual) == 0); + args.Handled = true; + } + + private void OnQueryContinueDrag(object sender, QueryContinueDragEventArgs e) + { +#if DEBUG + if (this.DragScope != null) + { + Point pd = Mouse.GetPosition(this.DragScope); + } +#endif + if (this.DragScope == null) + { + this.UpdateWindowLocation(); + } + else + { + Point p = Mouse.GetPosition(this.DragScope); + + if (this.adorner != null) + { + this.adorner.LeftOffset = p.X /* - _startPoint.X */ ; + this.adorner.TopOffset = p.Y /* - _startPoint.Y */ ; + } + + if (this.mouseLeftScope) + { + e.Action = DragAction.Cancel; + e.Handled = true; + } + } + } + + private void DestroyDragDropWindow() + { + if (this.dragdropWindow != null) + { + this.dragdropWindow.Close(); + this.dragdropWindow = null; + } + } + + private void CreateDragDropWindow(Visual dragElement) + { + Debug.Assert(this.dragdropWindow == null); + + this.dragdropWindow = new Window(); + + this.dragdropWindow.WindowStyle = WindowStyle.None; + this.dragdropWindow.AllowsTransparency = true; + this.dragdropWindow.AllowDrop = false; + this.dragdropWindow.Background = null; + this.dragdropWindow.IsHitTestVisible = false; + this.dragdropWindow.SizeToContent = SizeToContent.WidthAndHeight; + this.dragdropWindow.Topmost = true; + this.dragdropWindow.ShowInTaskbar = false; + + this.dragdropWindow.SourceInitialized += new EventHandler( + delegate(object sender, EventArgs args) + { + //TODO assert that we can do this.. + PresentationSource windowSource = PresentationSource.FromVisual(this.dragdropWindow); + IntPtr handle = ((HwndSource)windowSource).Handle; + + Int32 styles = Win32Interop.GetWindowLong(handle, Win32Interop.GWL_EXSTYLE); + + Win32Interop.SetWindowLong(handle, Win32Interop.GWL_EXSTYLE, styles | Win32Interop.WS_EX_LAYERED | Win32Interop.WS_EX_TRANSPARENT); + }); + + Rectangle r = new Rectangle(); + + r.Width = ((FrameworkElement)dragElement).ActualWidth; + r.Height = ((FrameworkElement)dragElement).ActualHeight; + r.Fill = new VisualBrush(dragElement); + + this.dragdropWindow.Content = r; + + // we want QueryContinueDrag notification so we can update the window position + //DragDrop.AddPreviewQueryContinueDragHandler(source, QueryContinueDrag); + + // put the window in the right place to start + this.UpdateWindowLocation(); + } + + private void UpdateWindowLocation() + { + if (this.dragdropWindow != null) + { + Win32Interop.POINT p; + + if (!Win32Interop.GetCursorPos(out p)) + { + return; + } + + this.dragdropWindow.Left = (double)p.X; + this.dragdropWindow.Top = (double)p.Y; + } + } + + private void DragFinished(DragDropEffects ret) + { + Mouse.Capture(null); + + if (this.IsDragging) + { + if (this.DragScope != null) + { + //AdornerLayer.GetAdornerLayer(this.DragScope).Remove(this.adorner); + //this.adorner = null; + } + else + { + this.DestroyDragDropWindow(); + } + } + + this.IsDragging = false; + } + + #endregion + } +}