/// <summary> /// Clear local variable and set selected items wh when mouse button up /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void DoMouseButtonUp(object sender, MouseButtonEventArgs e) { var elementPosition = e.GetPosition((IInputElement)sender); if ((sender is TabControl) && !HitTestUtilities.HitTest4Type <TabPanel>(sender, elementPosition)) { m_DragInfo = null; m_ClickSupressItem = null; return; } var dragInfo = m_DragInfo; // If we prevented the control's default selection handling in DragSource_PreviewMouseLeftButtonDown // by setting 'e.Handled = true' and a drag was not initiated, manually set the selection here. if (sender is ItemsControl itemsControl && dragInfo != null && m_ClickSupressItem != null && m_ClickSupressItem == dragInfo.SourceItem) { if ((Keyboard.Modifiers & ModifierKeys.Control) != 0) { itemsControl.SetItemSelected(dragInfo.SourceItem, false); } else if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0) { itemsControl.SetSelectedItem(dragInfo.SourceItem); } } m_DragInfo = null; m_ClickSupressItem = null; }
/// <summary> /// Gets the drag handler from the drag info or from the sender, if the drag info is null /// </summary> /// <param name="dragInfo">the drag info object</param> /// <param name="sender">the sender from an event, e.g. mouse down, mouse move</param> /// <returns></returns> private static IDragSource TryGetDragHandler(DragInfo dragInfo, UIElement sender) { IDragSource dragHandler = null; if (dragInfo?.VisualSource != null) { dragHandler = GetDragHandler(dragInfo.VisualSource); } if (dragHandler == null && sender != null) { dragHandler = GetDragHandler(sender); } return(dragHandler ?? DefaultDragHandler); }
/// <summary> /// Initializes a new instance of the DropInfo class. /// <param name="sender">The sender of the drag event.</param> /// <param name="e">The drag event.</param> /// <param name="dragInfo">Information about the source of the drag, if the drag came from within the framework.</param> /// <param name="eventType">The type of the underlying event (tunneled or bubbled).</param> /// </summary> public DropInfo(object sender, DragEventArgs e, [CanBeNull] DragInfo dragInfo, EventType eventType) { DragInfo = dragInfo; KeyStates = e.KeyStates; EventType = eventType; var dataFormat = dragInfo?.DataFormat; Data = dataFormat != null && e.Data.GetDataPresent(dataFormat.Name) ? e.Data.GetData(dataFormat.Name) : e.Data; VisualTarget = sender as UIElement; // if there is no drop target, find another if (!VisualTarget.IsDropTarget()) { // try to find next element var element = VisualTarget.TryGetNextAncestorDropTargetElement(); if (element != null) { VisualTarget = element; } } // try find ScrollViewer var dropTargetScrollViewer = DragDrop.GetDropTargetScrollViewer(VisualTarget); if (dropTargetScrollViewer != null) { TargetScrollViewer = dropTargetScrollViewer; } else if (VisualTarget is TabControl) { var tabPanel = VisualTarget.FindVisualChild <TabPanel>(); TargetScrollViewer = tabPanel?.FindVisualParent <ScrollViewer>(); } else { TargetScrollViewer = VisualTarget?.FindVisualChild <ScrollViewer>(); } TargetScrollingMode = VisualTarget != null?DragDrop.GetDropScrollingMode(VisualTarget) : ScrollingMode.Both; // visual target can be null, so give us a point... DropPosition = VisualTarget != null?e.GetPosition(VisualTarget) : new Point(); if (VisualTarget is TabControl) { if (!HitTestUtilities.HitTest4Type <TabPanel>(VisualTarget, DropPosition)) { return; } } if (VisualTarget is ItemsControl itemsControl) { //System.Diagnostics.Debug.WriteLine(">>> Name = {0}", itemsControl.Name); // get item under the mouse item = itemsControl.GetItemContainerAt(DropPosition); var directlyOverItem = item != null; TargetGroup = itemsControl.FindGroup(DropPosition); VisualTargetOrientation = itemsControl.GetItemsPanelOrientation(); VisualTargetFlowDirection = itemsControl.GetItemsPanelFlowDirection(); if (item == null) { // ok, no item found, so maybe we can found an item at top, left, right or bottom item = itemsControl.GetItemContainerAt(DropPosition, VisualTargetOrientation); directlyOverItem = DropPosition.DirectlyOverElement(item, itemsControl); } if (item == null && TargetGroup != null && TargetGroup.IsBottomLevel) { var itemData = TargetGroup.Items.FirstOrDefault(); if (itemData != null) { item = itemsControl.ItemContainerGenerator.ContainerFromItem(itemData) as UIElement; directlyOverItem = DropPosition.DirectlyOverElement(item, itemsControl); } } if (item != null) { itemParent = ItemsControl.ItemsControlFromItemContainer(item); VisualTargetOrientation = itemParent.GetItemsPanelOrientation(); VisualTargetFlowDirection = itemParent.GetItemsPanelFlowDirection(); InsertIndex = itemParent.ItemContainerGenerator.IndexFromContainer(item); TargetCollection = itemParent.ItemsSource ?? itemParent.Items; var tvItem = item as TreeViewItem; if (directlyOverItem || tvItem != null) { VisualTargetItem = item; TargetItem = itemParent.ItemContainerGenerator.ItemFromContainer(item); } var expandedTVItem = tvItem != null && tvItem.HasHeader && tvItem.HasItems && tvItem.IsExpanded; var itemRenderSize = expandedTVItem ? tvItem.GetHeaderSize() : item.RenderSize; if (VisualTargetOrientation == Orientation.Vertical) { // Get the current position relative to the items container var currentYPos = e.GetPosition(item).Y; // Get items container height var targetHeight = itemRenderSize.Height; var topGap = targetHeight * 0.25; var bottomGap = targetHeight * 0.75; if (currentYPos > targetHeight / 2) { // If items control is tree viewer if (expandedTVItem && (currentYPos < topGap || currentYPos > bottomGap)) { VisualTargetItem = tvItem.ItemContainerGenerator.ContainerFromIndex(0) as UIElement; TargetItem = VisualTargetItem != null?tvItem.ItemContainerGenerator.ItemFromContainer(VisualTargetItem) : null; TargetCollection = tvItem.ItemsSource ?? tvItem.Items; InsertIndex = 0; InsertPosition = RelativeInsertPosition.BeforeTargetItem; } else { InsertIndex++; InsertPosition = RelativeInsertPosition.AfterTargetItem; } } else { InsertPosition = RelativeInsertPosition.BeforeTargetItem; } if (currentYPos > topGap && currentYPos < bottomGap) { if (tvItem != null) { TargetCollection = tvItem.ItemsSource ?? tvItem.Items; InsertIndex = TargetCollection != null?TargetCollection.OfType <object>().Count() : 0; } InsertPosition |= RelativeInsertPosition.TargetItemCenter; } //System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, idx={1}, Y={2}, Item={3}", InsertPosition, InsertIndex, currentYPos, item); } else { var currentXPos = e.GetPosition(item).X; var targetWidth = itemRenderSize.Width; if (VisualTargetFlowDirection == FlowDirection.RightToLeft) { if (currentXPos > targetWidth / 2) { InsertPosition = RelativeInsertPosition.BeforeTargetItem; } else { InsertIndex++; InsertPosition = RelativeInsertPosition.AfterTargetItem; } } else if (VisualTargetFlowDirection == FlowDirection.LeftToRight) { if (currentXPos > targetWidth / 2) { InsertIndex++; InsertPosition = RelativeInsertPosition.AfterTargetItem; } else { InsertPosition = RelativeInsertPosition.BeforeTargetItem; } } if (currentXPos > targetWidth * 0.25 && currentXPos < targetWidth * 0.75) { if (tvItem != null) { TargetCollection = tvItem.ItemsSource ?? tvItem.Items; InsertIndex = TargetCollection != null?TargetCollection.OfType <object>().Count() : 0; } InsertPosition |= RelativeInsertPosition.TargetItemCenter; } //System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, idx={1}, X={2}, Item={3}", InsertPosition, InsertIndex, currentXPos, item); } } else { TargetCollection = itemsControl.ItemsSource ?? itemsControl.Items; InsertIndex = itemsControl.Items.Count; //System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, item=NULL, idx={1}", InsertPosition, InsertIndex); } } else { VisualTargetItem = VisualTarget; } }
/// <summary> /// Set base drag info when mouse left button down /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void DoMouseButtonDown(object sender, MouseButtonEventArgs e) { m_DragInfo = null; // Gets the position of the mouse relative to source control var elementPosition = e.GetPosition((IInputElement)sender); // Ignore the click if clickCount != 1 or the user has clicked on a scrollbar or the control while editing. if (e.ClickCount != 1 || (sender as UIElement).IsDragSourceIgnored() || (e.Source as UIElement).IsDragSourceIgnored() || (e.OriginalSource as UIElement).IsDragSourceIgnored() // Ignore TabControl's content presenter || (sender is TabControl) && !HitTestUtilities.HitTest4Type <TabPanel>(sender, elementPosition) // Ignore scroll bar || HitTestUtilities.HitTest4Type <RangeBase>(sender, elementPosition) // Ignore the control while editing. || HitTestUtilities.HitTest4Type <TextBoxBase>(sender, elementPosition) || HitTestUtilities.HitTest4Type <PasswordBox>(sender, elementPosition) || HitTestUtilities.HitTest4Type <ComboBox>(sender, elementPosition) // Ignore grid column header || HitTestUtilities.HitTest4GridViewColumnHeader(sender, elementPosition) // Ignore user reordering DataGrid columns and drag row header gripper to chang row header height || HitTestUtilities.HitTest4DataGridTypes(sender, elementPosition) || HitTestUtilities.IsNotPartOfSender(sender, e)) { return; } var dragInfo = new DragInfo(sender, e); if (dragInfo.VisualSource is ItemsControl control && control.CanSelectMultipleItems()) { control.Focus(); } if (dragInfo.VisualSourceItem == null) { return; } var dragHandler = TryGetDragHandler(dragInfo, sender as UIElement); if (!dragHandler.CanStartDrag(dragInfo)) { return; } // If the sender is a list box that allows multiple selections, ensure that clicking on an // already selected item does not change the selection(didn't click a shift key or control key at the same time), // otherwise dragging multiple items is made impossible. if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0 && (Keyboard.Modifiers & ModifierKeys.Control) == 0 && dragInfo.VisualSourceItem != null && sender is ItemsControl itemsControl && itemsControl.CanSelectMultipleItems()) { var selectedItems = itemsControl.GetSelectedItems().OfType <object>().ToList(); // we haven't pressed control key and shift key, set clicked current item if (selectedItems.Count > 1 && selectedItems.Contains(dragInfo.SourceItem)) { m_ClickSupressItem = dragInfo.SourceItem; e.Handled = true; } } m_DragInfo = dragInfo; }
/// <summary> /// Raise on mouse move at source ItemsControl /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void DragSourceOnMouseMove(object sender, MouseEventArgs e) { var dragInfo = m_DragInfo; // if we not release the mouse button while we moving our mouse... if (dragInfo != null && !m_DragInProgress) { // the start from the source var dragStart = dragInfo.DragStartPosition; // do nothing if mouse left/right button is released or the pointer is captured if (dragInfo.MouseButton == MouseButton.Left && e.LeftButton == MouseButtonState.Released) { m_DragInfo = null; return; } if (GetCanDragWithMouseRightButton(dragInfo.VisualSource) && dragInfo.MouseButton == MouseButton.Right && e.RightButton == MouseButtonState.Released) { m_DragInfo = null; return; } // current mouse position var position = e.GetPosition((IInputElement)sender); // prevent selection changing while drag operation dragInfo.VisualSource?.ReleaseMouseCapture(); // only if the sender is the source control and the mouse point differs from an offset if (dragInfo.VisualSource == sender && (Math.Abs(position.X - dragStart.X) > GetMinimumHorizontalDragDistance(dragInfo.VisualSource) || Math.Abs(position.Y - dragStart.Y) > GetMinimumVerticalDragDistance(dragInfo.VisualSource))) { // Ensure SourceItems is latest selected items dragInfo.RefreshSelectedItems(sender, e); var dragHandler = TryGetDragHandler(dragInfo, sender as UIElement); if (dragHandler.CanStartDrag(dragInfo)) { dragHandler.StartDrag(dragInfo); if (dragInfo.Effects != DragDropEffects.None) { var dataObject = dragInfo.DataObject; if (dataObject == null) { if (dragInfo.Data == null) { // it's bad if the Data is null, cause the DataObject constructor will raise an ArgumentNullException m_DragInfo = null; // maybe not necessary or should not set here to null return; } dataObject = new DataObject(dragInfo.DataFormat.Name, dragInfo.Data); } try { m_DragInProgress = true; var dragDropHandler = dragInfo.DragDropHandler ?? System.Windows.DragDrop.DoDragDrop; var dragDropEffects = dragDropHandler(dragInfo.VisualSource, dataObject, dragInfo.Effects); if (dragDropEffects == DragDropEffects.None) { dragHandler.DragCancelled(); } dragHandler.DragDropOperationFinished(dragDropEffects, dragInfo); } catch (Exception ex) { if (!dragHandler.TryCatchOccurredException(ex)) { throw; } } finally { m_DragInProgress = false; m_DragInfo = null; } } } } } }