Example #1
0
        /// <summary>
        /// When overridden in a derived class, participates in rendering operations that are directed by the layout system.
        /// The rendering instructions for this element are not used directly when this method is invoked, and are instead preserved for
        /// later asynchronous use by layout and drawing.
        /// </summary>
        /// <param name="drawingContext">The drawing instructions for a specific element. This context is provided to the layout system.</param>
        protected override void OnRender(DrawingContext drawingContext)
        {
            var dropInfo     = this.DropInfo;
            var itemsControl = dropInfo.VisualTarget as ItemsControl;

            if (itemsControl != null)
            {
                // Get the position of the item at the insertion index. If the insertion point is
                // to be after the last item, then get the position of the last item and add an
                // offset later to draw it at the end of the list.
                ItemsControl itemParent;

                var visualTargetItem = dropInfo.VisualTargetItem;
                if (visualTargetItem != null)
                {
                    itemParent = ItemsControl.ItemsControlFromItemContainer(visualTargetItem);
                }
                else
                {
                    itemParent = itemsControl;
                }

                // this could be happen with a thread scenario where items are removed very quickly
                if (itemParent == null)
                {
                    return;
                }

                var itemsCount = itemParent.Items.Count;
                var index      = Math.Min(dropInfo.InsertIndex, itemsCount - 1);

                var lastItemInGroup = false;
                var targetGroup     = dropInfo.TargetGroup;
                if (targetGroup != null && targetGroup.IsBottomLevel && dropInfo.InsertPosition.HasFlag(RelativeInsertPosition.AfterTargetItem))
                {
                    var indexOf = targetGroup.Items.IndexOf(dropInfo.TargetItem);
                    lastItemInGroup = indexOf == targetGroup.ItemCount - 1;
                    if (lastItemInGroup && dropInfo.InsertIndex != itemsCount)
                    {
                        index--;
                    }
                }

                var itemContainer = (UIElement)itemParent.ItemContainerGenerator.ContainerFromIndex(index);

                var showAlwaysDropTargetAdorner = itemContainer == null && DragDrop.GetShowAlwaysDropTargetAdorner(itemParent);
                if (showAlwaysDropTargetAdorner)
                {
                    itemContainer = itemParent;
                }

                if (itemContainer != null)
                {
                    var   itemRect = new Rect(itemContainer.TranslatePoint(new Point(), this.AdornedElement), itemContainer.RenderSize);
                    Point point1,
                          point2;
                    double rotation = 0;

                    // I really don't know why I did this
                    //
                    // var viewportWidth = double.MaxValue;
                    // var viewportHeight = double.MaxValue;
                    // if (DropInfo.TargetScrollViewer != null)
                    // {
                    //     if (DropInfo.TargetScrollViewer.ScrollableWidth != 0)
                    //     {
                    //         viewportWidth = DropInfo.TargetScrollViewer.ViewportWidth;
                    //     }
                    //
                    //     if (DropInfo.TargetScrollViewer.ScrollableHeight != 0)
                    //     {
                    //         viewportHeight = DropInfo.TargetScrollViewer.ViewportHeight;
                    //     }
                    // }

                    if (dropInfo.VisualTargetOrientation == Orientation.Vertical)
                    {
                        if ((dropInfo.InsertIndex == itemsCount) || lastItemInGroup)
                        {
                            if (itemsCount > 0)
                            {
                                itemRect.Y += itemContainer.RenderSize.Height;
                            }
                            else
                            {
                                if ((itemsControl as ListView)?.View is GridView)
                                {
                                    var header = itemsControl.GetVisualDescendent <GridViewHeaderRowPresenter>();
                                    if (header != null)
                                    {
                                        itemRect.Y += header.RenderSize.Height;
                                    }
                                }
                                else if (itemsControl is DataGrid)
                                {
                                    var header = itemsControl.GetVisualDescendent <DataGridColumnHeadersPresenter>();
                                    if (header != null)
                                    {
                                        itemRect.Y += header.RenderSize.Height;
                                    }
                                }

                                itemRect.Y += this.Pen.Thickness;
                            }
                        }

                        var itemRectRight = itemRect.Right; //Math.Min(itemRect.Right, viewportWidth);
                        var itemRectLeft  = itemRect.X < 0 ? 0 : itemRect.X;
                        point1 = new Point(itemRectLeft, itemRect.Y);
                        point2 = new Point(itemRectRight, itemRect.Y);
                    }
                    else
                    {
                        if (dropInfo.VisualTargetFlowDirection == FlowDirection.LeftToRight && dropInfo.InsertIndex == itemsCount)
                        {
                            if (itemsCount > 0)
                            {
                                itemRect.X += itemContainer.RenderSize.Width;
                            }
                            else
                            {
                                itemRect.X += this.Pen.Thickness;
                            }
                        }
                        else if (dropInfo.VisualTargetFlowDirection == FlowDirection.RightToLeft && dropInfo.InsertIndex != itemsCount)
                        {
                            if (itemsCount > 0)
                            {
                                itemRect.X += itemContainer.RenderSize.Width;
                            }
                            else
                            {
                                itemRect.X += this.Pen.Thickness;
                            }
                        }

                        var itemRectTop    = itemRect.Y < 0 ? 0 : itemRect.Y;
                        var itemRectBottom = itemRect.Bottom; //Math.Min(itemRect.Bottom, viewportHeight);

                        point1   = new Point(itemRect.X, itemRectTop);
                        point2   = new Point(itemRect.X, itemRectBottom);
                        rotation = 90;
                    }

                    drawingContext.DrawLine(this.Pen, point1, point2);
                    this.DrawTriangle(drawingContext, point1, rotation);
                    this.DrawTriangle(drawingContext, point2, 180 + rotation);
                }
            }
        }
        /// <inheritdoc />
        public virtual void Drop(IDropInfo dropInfo)
        {
            if (dropInfo == null || dropInfo.DragInfo == null)
            {
                return;
            }

            var insertIndex = dropInfo.UnfilteredInsertIndex;

            var itemsControl = dropInfo.VisualTarget as ItemsControl;

            if (itemsControl != null)
            {
                var editableItems = itemsControl.Items as IEditableCollectionView;
                if (editableItems != null)
                {
                    var newItemPlaceholderPosition = editableItems.NewItemPlaceholderPosition;
                    if (newItemPlaceholderPosition == NewItemPlaceholderPosition.AtBeginning && insertIndex == 0)
                    {
                        ++insertIndex;
                    }
                    else if (newItemPlaceholderPosition == NewItemPlaceholderPosition.AtEnd && insertIndex == itemsControl.Items.Count)
                    {
                        --insertIndex;
                    }
                }
            }

            var destinationList = dropInfo.TargetCollection.TryGetList();
            var data            = ExtractData(dropInfo.Data).OfType <object>().ToList();

            var copyData = ShouldCopyData(dropInfo);

            if (!copyData)
            {
                var sourceList = dropInfo.DragInfo.SourceCollection.TryGetList();
                if (sourceList != null)
                {
                    foreach (var o in data)
                    {
                        var index = sourceList.IndexOf(o);
                        if (index != -1)
                        {
                            sourceList.RemoveAt(index);
                            // so, is the source list the destination list too ?
                            if (destinationList != null && Equals(sourceList, destinationList) && index < insertIndex)
                            {
                                --insertIndex;
                            }
                        }
                    }
                }
            }

            if (destinationList != null)
            {
                var objects2Insert = new List <object>();

                // check for cloning
                var cloneData = dropInfo.Effects.HasFlag(DragDropEffects.Copy) || dropInfo.Effects.HasFlag(DragDropEffects.Link);
                foreach (var o in data)
                {
                    var obj2Insert = o;
                    if (cloneData)
                    {
                        var cloneable = o as ICloneable;
                        if (cloneable != null)
                        {
                            obj2Insert = cloneable.Clone();
                        }
                    }

                    objects2Insert.Add(obj2Insert);
                    destinationList.Insert(insertIndex++, obj2Insert);
                }

                var selectDroppedItems = itemsControl is TabControl || (itemsControl != null && DragDrop.GetSelectDroppedItems(itemsControl));
                if (selectDroppedItems)
                {
                    SelectDroppedItems(dropInfo, objects2Insert);
                }
            }
        }
        /// <summary>
        /// Initializes a new instance of the DropInfo class.
        /// </summary>
        ///
        /// <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>
        public DropInfo(object sender, DragEventArgs e, [CanBeNull] DragInfo dragInfo, EventType eventType)
        {
            this.DragInfo  = dragInfo;
            this.KeyStates = e.KeyStates;
            this.EventType = eventType;
            var dataFormat = dragInfo?.DataFormat;

            this.Data = dataFormat != null && e.Data.GetDataPresent(dataFormat.Name) ? e.Data.GetData(dataFormat.Name) : e.Data;

            this.VisualTarget = sender as UIElement;
            // if there is no drop target, find another
            if (!this.VisualTarget.IsDropTarget())
            {
                // try to find next element
                var element = this.VisualTarget.TryGetNextAncestorDropTargetElement();
                if (element != null)
                {
                    this.VisualTarget = element;
                }
            }

            // try find ScrollViewer
            var dropTargetScrollViewer = DragDrop.GetDropTargetScrollViewer(this.VisualTarget);

            if (dropTargetScrollViewer != null)
            {
                this.TargetScrollViewer = dropTargetScrollViewer;
            }
            else if (this.VisualTarget is TabControl)
            {
                var tabPanel = this.VisualTarget.GetVisualDescendent <TabPanel>();
                this.TargetScrollViewer = tabPanel?.GetVisualAncestor <ScrollViewer>();
            }
            else
            {
                this.TargetScrollViewer = this.VisualTarget?.GetVisualDescendent <ScrollViewer>();
            }

            this.TargetScrollingMode = this.VisualTarget != null?DragDrop.GetDropScrollingMode(this.VisualTarget) : ScrollingMode.Both;

            // visual target can be null, so give us a point...
            this.DropPosition = this.VisualTarget != null?e.GetPosition(this.VisualTarget) : new Point();

            if (this.VisualTarget is TabControl)
            {
                if (!HitTestUtilities.HitTest4Type <TabPanel>(this.VisualTarget, this.DropPosition))
                {
                    return;
                }
            }

            if (this.VisualTarget is ItemsControl)
            {
                var itemsControl = (ItemsControl)this.VisualTarget;
                //System.Diagnostics.Debug.WriteLine(">>> Name = {0}", itemsControl.Name);
                // get item under the mouse
                item = itemsControl.GetItemContainerAt(this.DropPosition);
                var directlyOverItem = item != null;

                this.TargetGroup               = itemsControl.FindGroup(this.DropPosition);
                this.VisualTargetOrientation   = itemsControl.GetItemsPanelOrientation();
                this.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(this.DropPosition, this.VisualTargetOrientation);
                    directlyOverItem = DropPosition.DirectlyOverElement(this.item, itemsControl);
                }

                if (item == null && this.TargetGroup != null && this.TargetGroup.IsBottomLevel)
                {
                    var itemData = this.TargetGroup.Items.FirstOrDefault();
                    if (itemData != null)
                    {
                        item             = itemsControl.ItemContainerGenerator.ContainerFromItem(itemData) as UIElement;
                        directlyOverItem = DropPosition.DirectlyOverElement(this.item, itemsControl);
                    }
                }

                if (item != null)
                {
                    itemParent = ItemsControl.ItemsControlFromItemContainer(item);
                    this.VisualTargetOrientation   = itemParent.GetItemsPanelOrientation();
                    this.VisualTargetFlowDirection = itemParent.GetItemsPanelFlowDirection();

                    this.InsertIndex      = itemParent.ItemContainerGenerator.IndexFromContainer(item);
                    this.TargetCollection = itemParent.ItemsSource ?? itemParent.Items;

                    var tvItem = item as TreeViewItem;

                    if (directlyOverItem || tvItem != null)
                    {
                        this.VisualTargetItem = item;
                        this.TargetItem       = itemParent.ItemContainerGenerator.ItemFromContainer(item);
                    }

                    var expandedTVItem = tvItem != null && tvItem.HasHeader && tvItem.HasItems && tvItem.IsExpanded;
                    var itemRenderSize = expandedTVItem ? tvItem.GetHeaderSize() : item.RenderSize;

                    if (this.VisualTargetOrientation == Orientation.Vertical)
                    {
                        var currentYPos  = e.GetPosition(item).Y;
                        var targetHeight = itemRenderSize.Height;

                        var topGap    = targetHeight * 0.25;
                        var bottomGap = targetHeight * 0.75;
                        if (currentYPos > targetHeight / 2)
                        {
                            if (expandedTVItem && (currentYPos < topGap || currentYPos > bottomGap))
                            {
                                this.VisualTargetItem = tvItem.ItemContainerGenerator.ContainerFromIndex(0) as UIElement;
                                this.TargetItem       = this.VisualTargetItem != null?tvItem.ItemContainerGenerator.ItemFromContainer(this.VisualTargetItem) : null;

                                this.TargetCollection = tvItem.ItemsSource ?? tvItem.Items;
                                this.InsertIndex      = 0;
                                this.InsertPosition   = RelativeInsertPosition.BeforeTargetItem;
                            }
                            else
                            {
                                this.InsertIndex++;
                                this.InsertPosition = RelativeInsertPosition.AfterTargetItem;
                            }
                        }
                        else
                        {
                            this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
                        }

                        if (currentYPos > topGap && currentYPos < bottomGap)
                        {
                            if (tvItem != null)
                            {
                                this.TargetCollection = tvItem.ItemsSource ?? tvItem.Items;
                                this.InsertIndex      = this.TargetCollection != null?this.TargetCollection.OfType <object>().Count() : 0;
                            }
                            this.InsertPosition |= RelativeInsertPosition.TargetItemCenter;
                        }
                        //System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, idx={1}, Y={2}, Item={3}", this.InsertPosition, this.InsertIndex, currentYPos, item);
                    }
                    else
                    {
                        var currentXPos = e.GetPosition(item).X;
                        var targetWidth = itemRenderSize.Width;

                        if (this.VisualTargetFlowDirection == FlowDirection.RightToLeft)
                        {
                            if (currentXPos > targetWidth / 2)
                            {
                                this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
                            }
                            else
                            {
                                this.InsertIndex++;
                                this.InsertPosition = RelativeInsertPosition.AfterTargetItem;
                            }
                        }
                        else if (this.VisualTargetFlowDirection == FlowDirection.LeftToRight)
                        {
                            if (currentXPos > targetWidth / 2)
                            {
                                this.InsertIndex++;
                                this.InsertPosition = RelativeInsertPosition.AfterTargetItem;
                            }
                            else
                            {
                                this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
                            }
                        }

                        if (currentXPos > targetWidth * 0.25 && currentXPos < targetWidth * 0.75)
                        {
                            if (tvItem != null)
                            {
                                this.TargetCollection = tvItem.ItemsSource ?? tvItem.Items;
                                this.InsertIndex      = this.TargetCollection != null?this.TargetCollection.OfType <object>().Count() : 0;
                            }
                            this.InsertPosition |= RelativeInsertPosition.TargetItemCenter;
                        }
                        //System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, idx={1}, X={2}, Item={3}", this.InsertPosition, this.InsertIndex, currentXPos, item);
                    }
                }
                else
                {
                    this.TargetCollection = itemsControl.ItemsSource ?? itemsControl.Items;
                    this.InsertIndex      = itemsControl.Items.Count;
                    //System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, item=NULL, idx={1}", this.InsertPosition, this.InsertIndex);
                }
            }
            else
            {
                this.VisualTargetItem = this.VisualTarget;
            }
        }
        /// <summary>
        /// Initializes a new instance of the DragInfo class.
        /// </summary>
        ///
        /// <param name="sender">
        /// The sender of the mouse event that initiated the drag.
        /// </param>
        ///
        /// <param name="e">
        /// The mouse event that initiated the drag.
        /// </param>
        public DragInfo(object sender, MouseButtonEventArgs e)
        {
            this.Effects              = DragDropEffects.None;
            this.MouseButton          = e.ChangedButton;
            this.VisualSource         = sender as UIElement;
            this.DragStartPosition    = e.GetPosition(this.VisualSource);
            this.DragDropCopyKeyState = DragDrop.GetDragDropCopyKeyState(this.VisualSource);

            var dataFormat = DragDrop.GetDataFormat(this.VisualSource);

            if (dataFormat != null)
            {
                this.DataFormat = dataFormat;
            }

            var sourceElement = e.OriginalSource as UIElement;

            // If we can't cast object as a UIElement it might be a FrameworkContentElement, if so try and use its parent.
            if (sourceElement == null && e.OriginalSource is FrameworkContentElement)
            {
                sourceElement = ((FrameworkContentElement)e.OriginalSource).Parent as UIElement;
            }

            if (sender is ItemsControl)
            {
                var itemsControl = (ItemsControl)sender;

                this.SourceGroup = itemsControl.FindGroup(this.DragStartPosition);
                this.VisualSourceFlowDirection = itemsControl.GetItemsPanelFlowDirection();

                UIElement item = null;
                if (sourceElement != null)
                {
                    item = itemsControl.GetItemContainer(sourceElement);
                }

                if (item == null)
                {
                    if (DragDrop.GetDragDirectlySelectedOnly(this.VisualSource))
                    {
                        item = itemsControl.GetItemContainerAt(e.GetPosition(itemsControl));
                    }
                    else
                    {
                        item = itemsControl.GetItemContainerAt(e.GetPosition(itemsControl), itemsControl.GetItemsPanelOrientation());
                    }
                }

                if (item != null)
                {
                    // Remember the relative position of the item being dragged
                    this.PositionInDraggedItem = e.GetPosition(item);

                    var itemParent = ItemsControl.ItemsControlFromItemContainer(item);

                    if (itemParent != null)
                    {
                        this.SourceCollection = itemParent.ItemsSource ?? itemParent.Items;
                        if (itemParent != itemsControl)
                        {
                            var tvItem = item as TreeViewItem;
                            if (tvItem != null)
                            {
                                var tv = tvItem.GetVisualAncestor <TreeView>();
                                if (tv != null && tv != itemsControl && !tv.IsDragSource())
                                {
                                    return;
                                }
                            }
                            else if (itemsControl.ItemContainerGenerator.IndexFromContainer(itemParent) < 0 && !itemParent.IsDragSource())
                            {
                                return;
                            }
                        }
                        this.SourceIndex = itemParent.ItemContainerGenerator.IndexFromContainer(item);
                        this.SourceItem  = itemParent.ItemContainerGenerator.ItemFromContainer(item);
                    }
                    else
                    {
                        this.SourceIndex = -1;
                    }

                    var selectedItems = itemsControl.GetSelectedItems().OfType <object>().Where(i => i != CollectionView.NewItemPlaceholder).ToList();
                    this.SourceItems = selectedItems;

                    // Some controls (I'm looking at you TreeView!) haven't updated their
                    // SelectedItem by this point. Check to see if there 1 or less item in
                    // the SourceItems collection, and if so, override the control's SelectedItems with the clicked item.
                    //
                    // The control has still the old selected items at the mouse down event, so we should check this and give only the real selected item to the user.
                    if (selectedItems.Count <= 1 || this.SourceItem != null && !selectedItems.Contains(this.SourceItem))
                    {
                        this.SourceItems = Enumerable.Repeat(this.SourceItem, 1);
                    }

                    this.VisualSourceItem = item;
                }
                else
                {
                    this.SourceCollection = itemsControl.ItemsSource ?? itemsControl.Items;
                }
            }
            else
            {
                this.SourceItem = (sender as FrameworkElement)?.DataContext;
                if (this.SourceItem != null)
                {
                    this.SourceItems = Enumerable.Repeat(this.SourceItem, 1);
                }
                this.VisualSourceItem      = sourceElement;
                this.PositionInDraggedItem = sourceElement != null?e.GetPosition(sourceElement) : this.DragStartPosition;
            }

            if (this.SourceItems == null)
            {
                this.SourceItems = Enumerable.Empty <object>();
            }
        }
        private static void DragSourceOnMouseMove(object sender, MouseEventArgs e)
        {
            var dragInfo = m_DragInfo;

            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 (DragDrop.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) > DragDrop.GetMinimumHorizontalDragDistance(dragInfo.VisualSource) ||
                     Math.Abs(position.Y - dragStart.Y) > DragDrop.GetMinimumVerticalDragDistance(dragInfo.VisualSource)))
                {
                    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;
                            }
                        }
                    }
                }
            }
        }