private void DropTarget_PreviewDrop(object sender, DragEventArgs e) { var draggedItems = e.Data.GetData(this.format.Name) as List <object>; var indiciesRemoved = new List <int>(); if (draggedItems != null) { if ((e.Effects & DragDropEffects.Move) != 0) { indiciesRemoved = DragDropUtilities.RemoveItemsFromItemsControl(this.sourceItemsControl, draggedItems); } // If we're dragging to the same list, adjust the insertion point to account for removed items. if (this.sourceItemsControl == this.targetItemsControl) { int itemCountBeforeInsertionPoint = indiciesRemoved.Where(t => t < this.insertionIndex).Count(); this.insertionIndex -= itemCountBeforeInsertionPoint; } DragDropUtilities.InsertItemsInItemsControl(this.targetItemsControl, draggedItems, this.insertionIndex); this.RemoveDraggedAdorner(); this.RemoveInsertionAdorner(); } e.Handled = true; }
// Drag = mouse down + move by a certain amount private void DragSource_PreviewMouseMove(object sender, MouseEventArgs e) { if (this.draggedData != null) { // Only drag when user moved the mouse by a reasonable amount. if (DragDropUtilities.IsMovementBigEnough(this.initialMousePosition, e.GetPosition(this.topWindow))) { var draggedItems = this.draggedData as List <object>; var listView = this.sourceItemsControl as ListView; if (listView != null) { // Once the drag has started, we must re-select the originally dragged items. The initial click may have de-selected them. listView.SelectedItems.Clear(); foreach (object item in draggedItems) { listView.SelectedItems.Add(item); } } DataObject data; try { data = new DataObject(this.format.Name, this.draggedData); } catch (COMException exception) { // Not sure what's going on here, can't reproduce. Hopefully this will allow the next drag operation to succeed. Ioc.Get <IAppLogger>().LogError("Error during drag operation:" + Environment.NewLine + Environment.NewLine + exception); this.draggedData = null; return; } // Adding events to the window to make sure dragged adorner comes up when mouse is not over a drop target. bool previousAllowDrop = this.topWindow.AllowDrop; this.topWindow.AllowDrop = true; this.topWindow.DragEnter += this.TopWindow_DragEnter; this.topWindow.DragOver += this.TopWindow_DragOver; this.topWindow.DragLeave += this.TopWindow_DragLeave; DragDropEffects effects = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move); // Without this call, there would be a bug in the following scenario: Click on a data item, and drag // the mouse very fast outside of the window. When doing this really fast, for some reason I don't get // the Window leave event, and the dragged adorner is left behind. // With this call, the dragged adorner will disappear when we release the mouse outside of the window, // which is when the DoDragDrop synchronous method returns. this.RemoveDraggedAdorner(); this.topWindow.AllowDrop = previousAllowDrop; this.topWindow.DragEnter -= this.TopWindow_DragEnter; this.topWindow.DragOver -= this.TopWindow_DragOver; this.topWindow.DragLeave -= this.TopWindow_DragLeave; this.draggedData = null; } } }
// DragSource private void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { this.sourceItemsControl = (ItemsControl)sender; var visual = e.OriginalSource as Visual; this.topWindow = (Window)DragDropUtilities.FindAncestor(typeof(Window), this.sourceItemsControl); this.initialMousePosition = e.GetPosition(this.topWindow); this.sourceItemContainer = DragDropUtilities.GetItemContainer(this.sourceItemsControl, visual); if (this.sourceItemContainer != null) { object clickedItem = this.sourceItemContainer.DataContext; var itemsToDrag = new List <object>(); var listView = this.sourceItemsControl as ListView; if (listView != null && listView.SelectedItems.Contains(clickedItem)) { // If we clicked on a selected item, do a multi-drag // The selected items aren't always in order so we need to sort by index first. var itemIndices = (from object selectedItem in listView.SelectedItems select this.sourceItemsControl.Items.IndexOf(selectedItem)).ToList(); itemIndices.Sort(); itemsToDrag.AddRange(itemIndices.Select(index => this.sourceItemsControl.Items[index])); } else { // If we clicked on an unselected item, do a single drag itemsToDrag.Add(clickedItem); } // Cull items that can't be dragged for (int i = itemsToDrag.Count - 1; i >= 0; i--) { var dragData = itemsToDrag[i] as IDragItem; if (dragData != null && !dragData.CanDrag) { itemsToDrag.RemoveAt(i); } } if (itemsToDrag.Count > 0) { this.draggedData = itemsToDrag; } } }
// If the types of the dragged data and ItemsControl's source are compatible, // there are 3 situations to have into account when deciding the drop target: // 1. mouse is over an items container // 2. mouse is over the empty part of an ItemsControl, but ItemsControl is not empty // 3. mouse is over an empty ItemsControl. // The goal of this method is to decide on the values of the following properties: // targetItemContainer, insertionIndex and isInFirstHalf. private void DecideDropTarget(DragEventArgs e) { int targetItemsControlCount = this.targetItemsControl.Items.Count; var draggedItems = e.Data.GetData(this.format.Name) as List <object>; this.targetItemContainer = null; if (draggedItems != null && this.IsDropDataTypeAllowed(draggedItems[0])) { if (targetItemsControlCount > 0) { this.hasVerticalOrientation = DragDropUtilities.HasVerticalOrientation(this.targetItemsControl.ItemContainerGenerator.ContainerFromIndex(0) as FrameworkElement); // Hack, only works with vertical orientation lists. Original code assumed dragging to end of list if no // item is hovered over; this is incorrect. for (int i = 0; i < targetItemsControlCount; i++) { FrameworkElement currentItemContainer = this.targetItemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement; if (currentItemContainer != null) { Point relativeDistanceFromItem = e.GetPosition(currentItemContainer); if (relativeDistanceFromItem.Y < 0) { this.targetItemContainer = currentItemContainer; break; } if (relativeDistanceFromItem.Y < currentItemContainer.ActualHeight) { this.targetItemContainer = currentItemContainer; break; } } } if (this.targetItemContainer != null) { Point positionRelativeToItemContainer = e.GetPosition(this.targetItemContainer); this.isInFirstHalf = DragDropUtilities.IsInFirstHalf(this.targetItemContainer, positionRelativeToItemContainer, this.hasVerticalOrientation); this.insertionIndex = this.targetItemsControl.ItemContainerGenerator.IndexFromContainer(this.targetItemContainer); if (!this.isInFirstHalf) { this.insertionIndex++; } } else { this.targetItemContainer = this.targetItemsControl.ItemContainerGenerator.ContainerFromIndex(targetItemsControlCount - 1) as FrameworkElement; this.isInFirstHalf = false; this.insertionIndex = targetItemsControlCount; } if (this.insertionIndex == 0 && !GetAllowDropAtTop(this.sourceItemsControl)) { this.targetItemContainer = null; this.insertionIndex = -1; e.Effects = DragDropEffects.None; } } else { this.insertionIndex = 0; } } else { this.insertionIndex = -1; e.Effects = DragDropEffects.None; } }