// 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, object draggedItem) { _isInFirstHalf = false; _targetItemContainer = null; _targetOrientation = Orientation.Vertical; _targetInsertionIndex = -1; if (!IsDropDataAllowed(draggedItem)) { // Mismatched type e.Effects = DragDropEffects.None; return; } var targetItemsControlCount = _targetItemsControl.Items.Count; if (targetItemsControlCount == 0) { // Over an empty items control, insert at the beginning _targetInsertionIndex = 0; return; } // Find the items panel _targetOrientation = DragDropUtilities.GetOrientation( _targetItemsControl.ItemContainerGenerator.ContainerFromIndex(0), GetDefaultOrientation(_targetItemsControl)); // This is the element we are pointing to (as long as it has a background) _targetItemContainer = _targetItemsControl.ContainerFromElement((DependencyObject)e.OriginalSource) as FrameworkElement; if (_targetItemContainer != null) { var positionRelativeToItemContainer = e.GetPosition(_targetItemContainer); _isInFirstHalf = DragDropUtilities.IsInFirstHalf(_targetItemContainer, positionRelativeToItemContainer, _targetOrientation); _targetInsertionIndex = _targetItemsControl.ItemContainerGenerator.IndexFromContainer(_targetItemContainer); if (!_isInFirstHalf) { // Insert after the target if more than half way through _targetInsertionIndex++; } return; } // Fallback to the last item in the container (always show at bottom) _targetItemContainer = _targetItemsControl.ItemContainerGenerator.ContainerFromIndex(targetItemsControlCount - 1) as FrameworkElement; _targetInsertionIndex = targetItemsControlCount; }
private void DropTarget_Drop(object sender, DragEventArgs e) { e.Handled = true; var draggedItem = e.Data.GetData(_format.Name); if (draggedItem == null) { return; } var indexRemoved = -1; if ((e.Effects & DragDropEffects.Move) != 0) { indexRemoved = DragDropUtilities.RemoveItemFromItemsControl(_sourceItemsControl, draggedItem); } // This happens when we drag an item to a later position within the same ItemsControl. if (indexRemoved != -1 && _sourceItemsControl == _targetItemsControl && indexRemoved < _targetInsertionIndex) { _targetInsertionIndex--; } DragDropUtilities.InsertItemIntoItemsControl(_targetItemsControl, draggedItem, _targetInsertionIndex); RemoveDraggedAdorner(); RemoveInsertionAdorner(); }
// Drag = mouse down + move by a certain amount private void DragSource_PreviewMouseMove(object sender, MouseEventArgs e) { if (_draggedData == null) { return; } // Only drag when user moved the mouse by a reasonable amount. if (!DragDropUtilities.IsMovementBigEnough(_initialMousePosition, e.GetPosition(_sourceItemsControl))) { return; } // Definately was our event e.Handled = true; // Get the data ready to be dropped var data = new DataObject(_format.Name, _draggedData); // From now on, get the data from the DataObject _draggedData = null; // Adding events to the parent to make sure the dragged adorner comes up when mouse is not over a drop target var parent = GetDragParent(_sourceItemsControl); DependencyProperty backgroundProperty = null; bool setBackground = false; bool previousAllowDrop = false; if (parent != null) { backgroundProperty = DragDropUtilities.GetDependencyPropertyByName(parent.GetType(), "BackgroundProperty"); // If the parent does not draw a background (like a grid by default) then you will not get drag events // Unfortunately Background is defined in several classes e.g. Control, Panel not a single base class // so use reflection to get and set the current value if (backgroundProperty != null) { var background = parent.GetValue(backgroundProperty) as Brush; if (background == null) { parent.SetValue(backgroundProperty, Brushes.Transparent); setBackground = true; } previousAllowDrop = parent.AllowDrop; parent.AllowDrop = true; parent.DragEnter += Parent_DragEnter; parent.DragOver += Parent_DragOver; parent.DragLeave += Parent_DragLeave; } } var 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. RemoveDraggedAdorner(); if (parent != null) { if (backgroundProperty != null) { if (setBackground) { parent.ClearValue(backgroundProperty); setBackground = false; } parent.AllowDrop = previousAllowDrop; parent.DragEnter -= Parent_DragEnter; parent.DragOver -= Parent_DragOver; parent.DragLeave -= Parent_DragLeave; } } }