Exemplo n.º 1
0
        /// <summary>
        /// Notifies that a drop event occurred over a target item and requests that appropriate updates are made to the supplied <c>DragEventArgs</c>.
        /// </summary>
        /// <param name="e">The <see cref="DragEventArgs"/> whose effects should be updated.</param>
        /// <param name="targetControl">The target control, over which the event occurred</param>
        /// <param name="targetItem">The target item, which could be <see langword="null"/> if dragging below the last tree item.</param>
        /// <param name="dropArea">A <see cref="TreeItemDropArea"/> indicating the drop area over the target item.</param>
        /// <remarks>
        /// The default implementation of this method sets the <c>e.Effects</c> to <c>DragDropEffects.None</c> and takes no further action.
        /// Override this method if you wish to support dropping and add logic to properly handle the dragged data.
        /// </remarks>
        public override void OnDrop(DragEventArgs e, TreeListBox targetControl, object targetItem, TreeItemDropArea dropArea)
        {
            var originalEffects = e.Effects;

            e.Effects = DragDropEffects.None;

            // If the drag is over an item and there is item data present...
            var targetModel = targetItem as ToolboxTreeNodeModel;

            if ((targetModel != null) && (dropArea != TreeItemDropArea.None) && (e.Data.GetDataPresent(TreeListBox.ItemDataFormat)))
            {
                // Resolve the real target item (in case the drop area is above or below the target item)
                var targetDropIndex = targetModel.Children.Count;
                switch (dropArea)
                {
                case TreeItemDropArea.Before:
                case TreeItemDropArea.After:
                    // When dropping before or after, a new node will be inserted as a child to the parent of the target
                    var nav = targetControl.GetItemNavigator(targetItem);
                    if (nav != null)
                    {
                        // Cache the original target
                        var targetChildModel = targetModel;

                        // Quit if unable to move to navigate to the parent
                        if (!nav.GoToParent())
                        {
                            return;
                        }

                        // Update the target item to be the parent of the original target
                        targetItem  = nav.CurrentItem;
                        targetModel = targetItem as ToolboxTreeNodeModel;

                        // Quit if the new target is not the expected model
                        if (targetModel == null)
                        {
                            return;
                        }

                        // Resolve index of the new node based on whether it should be before
                        // or after the original target.
                        var index = targetModel.Children.IndexOf(targetChildModel);
                        if (index != -1)
                        {
                            targetDropIndex = index + (dropArea == TreeItemDropArea.After ? 1 : 0);
                        }
                    }
                    break;
                }

                // Resolve the category for the drop target
                if (!TryGetCategory(targetControl, targetModel, out var targetCategoryModel))
                {
                    MessageBox.Show("Unable to determine the drop category.", "Drag and Drop", MessageBoxButton.OK);
                    return;
                }

                // Get the dragged controls (only control nodes are currently supported)
                List <ControlTreeNodeModel> sourceControlModels = GetDraggedModels(e.Data, targetControl)
                                                                  .OfType <ControlTreeNodeModel>()
                                                                  .ToList();
                if (sourceControlModels.Count > 0)
                {
                    // Check each item and validate that various drop operations are allowed before actually executing the drop
                    foreach (var sourceModel in sourceControlModels)
                    {
                        if (sourceModel == targetModel)
                        {
                            MessageBox.Show("Cannot drop an item on itself.", "Drag and Drop", MessageBoxButton.OK);
                            return;
                        }
                        else
                        {
                            var nav = targetControl.GetItemNavigator(sourceModel);
                            if (nav == null)
                            {
                                MessageBox.Show("Cannot drop from a different control.", "Drag and Drop", MessageBoxButton.OK);
                                return;
                            }
                            else
                            {
                                if (nav.GoToCommonAncestor(targetItem))
                                {
                                    if (nav.CurrentItem == sourceModel)
                                    {
                                        MessageBox.Show("Cannot drop onto a descendant item.", "Drag and Drop", MessageBoxButton.OK);
                                        return;
                                    }
                                }
                            }
                        }
                    }

                    // Only support a single effect (you could add support for other effects like Copy if the Ctrl key is down here)
                    if ((originalEffects & DragDropEffects.Move) == DragDropEffects.Move)
                    {
                        e.Effects = DragDropEffects.Move;
                        e.Handled = true;
                    }
                    else if ((originalEffects & DragDropEffects.Copy) == DragDropEffects.Copy)
                    {
                        e.Effects = DragDropEffects.Copy;
                        e.Handled = true;
                    }

                    if (IsFavoritesCategory(targetCategoryModel))
                    {
                        // Controls dragged into favorites category will not actually be moved. They will be added as favorites.
                        foreach (var sourceControlModel in sourceControlModels)
                        {
                            sourceControlModel.IsFavorite = true;
                        }
                    }
                    else
                    {
                        // Complete operation
                        bool isMove = e.Effects == DragDropEffects.Move;
                        foreach (var sourceControlModel in sourceControlModels)
                        {
                            // Resolve the source category of the dragged control
                            if (!TryGetCategory(targetControl, sourceControlModel, out var sourceCategoryModel))
                            {
                                break;
                            }

                            if (isMove)
                            {
                                // Remove the control from the original category
                                var index = sourceCategoryModel.Children.IndexOf(sourceControlModel);
                                if (index != -1)
                                {
                                    if ((sourceCategoryModel == targetCategoryModel) && (index < targetDropIndex))
                                    {
                                        targetDropIndex--;
                                    }

                                    sourceCategoryModel.Children.RemoveAt(index);
                                }
                                else
                                {
                                    // Quit processing if any source cannot be located
                                    break;
                                }

                                // Add the control to the new category (may be the same as the source if it was repositioned)
                                int resolvedTargetDropIndex = Math.Max(0, Math.Min(targetCategoryModel.Children.Count, targetDropIndex++));
                                targetCategoryModel.Children.Insert(resolvedTargetDropIndex, sourceControlModel);
                                targetCategoryModel.IsExpanded = true;

                                // Focus the last item that was moved
                                if (sourceControlModels[sourceControlModels.Count - 1] == sourceControlModel)
                                {
                                    // Must dispatch the focus changes to allow the view to update based on changes in the models
                                    Dispatcher.CurrentDispatcher.BeginInvoke((Action)(() => {
                                        targetControl.FocusItem(sourceControlModel);
                                    }), DispatcherPriority.Normal);
                                }
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Notifies that a drag event occurred over a target item, requests that appropriate updates are made to the supplied <c>DragEventArgs</c>,
        /// and requests that the allowed drop area is returned for visual target feedback.
        /// </summary>
        /// <param name="e">The <see cref="DragEventArgs"/> whose effects should be updated.</param>
        /// <param name="targetControl">The target control, over which the event occurred</param>
        /// <param name="targetItem">The target item, which could be <see langword="null"/> if dragging below the last tree item.</param>
        /// <param name="dropArea">A <see cref="TreeItemDropArea"/> indicating the drop area over the target item.</param>
        /// <returns>
        /// A <see cref="TreeItemDropArea"/> indicating the allowed drop area, which will be used for visual feedback to the end user.
        /// Return <c>TreeItemDropArea.None</c> for no visual feedback.
        /// </returns>
        /// <remarks>
        /// The default implementation of this method sets the <c>e.Effects</c> to <c>DragDropEffects.None</c> and returns that no drop area is allowed.
        /// Override this method if you wish to support dropping and add logic to properly handle the dragged data.
        /// </remarks>
        public override TreeItemDropArea OnDragOver(DragEventArgs e, TreeListBox targetControl, object targetItem, TreeItemDropArea dropArea)
        {
            // If the drag is over an item and there is item data present...
            if ((targetItem != null) && (dropArea != TreeItemDropArea.None) && (e.Data.GetDataPresent(TreeListBox.ItemDataFormat)))
            {
                var fullPaths = e.Data.GetData(TreeListBox.ItemDataFormat) as string;
                if (!string.IsNullOrEmpty(fullPaths))
                {
                    // Locate the first item based on full path
                    object firstItem = null;
                    foreach (var fullPath in fullPaths.Split(new char[] { '\r', '\n' }))
                    {
                        if (!string.IsNullOrEmpty(fullPath))
                        {
                            var item = targetControl.GetItemByFullPath(fullPath);
                            if (item != null)
                            {
                                firstItem = item;
                                break;
                            }
                        }
                    }

                    if (firstItem != null)
                    {
                        // Ensure that the first item is already in the target control (nav will be null if not)... if allowing drag/drop onto external
                        //   controls, you cannot use the item navigator and must rely on your own item hierarchy logic
                        var firstItemNav = targetControl.GetItemNavigator(firstItem);
                        if (firstItemNav != null)
                        {
                            // Only support a single effect (you could add support for other effects like Copy if the Ctrl key is down here)
                            if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move)
                            {
                                e.Effects = DragDropEffects.Move;
                                e.Handled = true;
                            }

                            switch (e.Effects)
                            {
                            case DragDropEffects.Move:
                                // Coerce the resulting drop-area so that if dragging 'after' an item that has a next sibling, the drop area
                                //   becomes 'on' the item instead... can still get between the items by dragging 'before' the next sibling in this scenario
                                if (dropArea == TreeItemDropArea.After)
                                {
                                    var targetItemNav = targetControl.GetItemNavigator(targetItem);
                                    if ((targetItemNav != null) && (targetItemNav.GoToNextSibling()))
                                    {
                                        dropArea = TreeItemDropArea.On;
                                    }
                                }

                                return(dropArea);
                            }
                        }
                    }
                }
            }

            e.Effects = DragDropEffects.None;
            return(TreeItemDropArea.None);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Notifies that a drag event occurred over a target item, requests that appropriate updates are made to the supplied <c>DragEventArgs</c>,
        /// and requests that the allowed drop area is returned for visual target feedback.
        /// </summary>
        /// <param name="e">The <see cref="DragEventArgs"/> whose effects should be updated.</param>
        /// <param name="targetControl">The target control, over which the event occurred</param>
        /// <param name="targetItem">The target item, which could be <see langword="null"/> if dragging below the last tree item.</param>
        /// <param name="dropArea">A <see cref="TreeItemDropArea"/> indicating the drop area over the target item.</param>
        /// <returns>
        /// A <see cref="TreeItemDropArea"/> indicating the allowed drop area, which will be used for visual feedback to the end user.
        /// Return <c>TreeItemDropArea.None</c> for no visual feedback.
        /// </returns>
        /// <remarks>
        /// The default implementation of this method sets the <c>e.Effects</c> to <c>DragDropEffects.None</c> and returns that no drop area is allowed.
        /// Override this method if you wish to support dropping and add logic to properly handle the dragged data.
        /// </remarks>
        public override TreeItemDropArea OnDragOver(DragEventArgs e, TreeListBox targetControl, object targetItem, TreeItemDropArea dropArea)
        {
            // If the drag is over an item, item data present, and the drop target is recognized...
            if ((dropArea != TreeItemDropArea.None) &&
                (e.Data.GetDataPresent(TreeListBox.ItemDataFormat)) &&
                (TryGetModelsFromItem(targetControl, targetItem, out var targetCategoryModel, out _)))
            {
                // Locate the dragged model. If multiple objects were selected, only the first object is used for reference.
                // The dragged item must be defined in the target control for the drag operation to be allowed.
                var sourceItem = GetDraggedModels(e.Data, targetControl).FirstOrDefault();
                if ((sourceItem != null) && (TryGetModelsFromItem(targetControl, sourceItem, out var sourceCategoryModel, out _)))
                {
                    if (IsFavoritesCategory(sourceCategoryModel))
                    {
                        // Dragging from the "Favorites" category is not supported within the Toolbox control. Favorites can only
                        // be dragged outside of the control (i.e. to the designer surface).
                        e.Effects = DragDropEffects.None;
                        e.Handled = true;
                    }
                    else if (IsFavoritesCategory(targetCategoryModel))
                    {
                        // Dropping on the "Favorites" category will only change if the control is a favorite and will not
                        // alter the location of the source control in the toolbox, so use the 'Copy' effect.
                        if ((e.AllowedEffects & DragDropEffects.Copy) == DragDropEffects.Copy)
                        {
                            e.Effects = DragDropEffects.Copy;
                            e.Handled = true;
                        }
                    }
                    else
                    {
                        // Controls cannot exist in more than one category, so only move effect is allowed
                        if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move)
                        {
                            e.Effects = DragDropEffects.Move;
                            e.Handled = true;
                        }
                    }

                    if ((e.Handled) && (e.Effects != DragDropEffects.None))
                    {
                        // TreeItems can be dropped 'Before', 'On', or 'After' a target node, but drop targets may not support
                        // all of these scenarios. Coerce the drop area, as needed, based on the target node.

                        if (targetItem is CategoryTreeNodeModel)
                        {
                            // Since controls can only be children to a category, not siblings, coerce all drops to 'On'
                            dropArea = TreeItemDropArea.On;
                        }
                        else if (targetItem is EmptyPlaceholderTreeNodeModel)
                        {
                            // When the control is dropped, the empty placeholder will be remove and the dropped control
                            // will be added as first child to the category, so coerce all drops to 'Before' the placeholder.
                            dropArea = TreeItemDropArea.Before;
                        }
                        else if ((targetItem is ControlTreeNodeModel) && (dropArea == TreeItemDropArea.On))
                        {
                            // A control cannot have children, so coerce dropping 'On' a control to 'After' (i.e. next sibling)
                            dropArea = TreeItemDropArea.After;
                        }

                        return(dropArea);
                    }
                }
            }

            e.Effects = DragDropEffects.None;
            return(TreeItemDropArea.None);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Notifies that a drop event occurred over a target item and requests that appropriate updates are made to the supplied <c>DragEventArgs</c>.
        /// </summary>
        /// <param name="e">The <see cref="DragEventArgs"/> whose effects should be updated.</param>
        /// <param name="targetControl">The target control, over which the event occurred</param>
        /// <param name="targetItem">The target item, which could be <see langword="null"/> if dragging below the last tree item.</param>
        /// <param name="dropArea">A <see cref="TreeItemDropArea"/> indicating the drop area over the target item.</param>
        /// <remarks>
        /// The default implementation of this method sets the <c>e.Effects</c> to <c>DragDropEffects.None</c> and takes no further action.
        /// Override this method if you wish to support dropping and add logic to properly handle the dragged data.
        /// </remarks>
        public override void OnDrop(DragEventArgs e, TreeListBox targetControl, object targetItem, TreeItemDropArea dropArea)
        {
            var originalEffects = e.Effects;

            e.Effects = DragDropEffects.None;

            // If the drag is over an item and there is item data present...
            var targetModel = targetItem as TreeNodeModel;

            if ((targetModel != null) && (dropArea != TreeItemDropArea.None) && (e.Data.GetDataPresent(TreeListBox.ItemDataFormat)))
            {
                // Resolve the real target item (in case the drop area is above or below the target item)
                var targetDropIndex = targetModel.Children.Count;
                switch (dropArea)
                {
                case TreeItemDropArea.Before:
                case TreeItemDropArea.After:
                    var nav = targetControl.GetItemNavigator(targetItem);
                    if (nav != null)
                    {
                        var targetChildModel = targetModel;
                        var targetChildItem  = targetItem;

                        if (!nav.GoToParent())
                        {
                            return;
                        }
                        targetItem  = nav.CurrentItem;
                        targetModel = targetItem as TreeNodeModel;
                        if (targetModel == null)
                        {
                            return;
                        }

                        var index = targetModel.Children.IndexOf(targetChildModel);
                        if (index != -1)
                        {
                            targetDropIndex = index + (dropArea == TreeItemDropArea.After ? 1 : 0);
                        }
                    }
                    break;
                }

                // Get the items
                var fullPaths = e.Data.GetData(TreeListBox.ItemDataFormat) as string;
                if (!string.IsNullOrEmpty(fullPaths))
                {
                    // Locate items based on full path
                    var items = new List <object>();
                    foreach (var fullPath in fullPaths.Split(new char[] { '\r', '\n' }))
                    {
                        if (!string.IsNullOrEmpty(fullPath))
                        {
                            var item = targetControl.GetItemByFullPath(fullPath);
                            if (item != null)
                            {
                                items.Add(item);
                            }
                        }
                    }

                    if (items.Count > 0)
                    {
                        // Check each item and validate that various drop operations are allowed before actually executing the drop
                        foreach (var item in items)
                        {
                            if (item == targetItem)
                            {
                                MessageBox.Show("Cannot drop an item on itself.", "Drag and Drop", MessageBoxButton.OK);
                                return;
                            }
                            else
                            {
                                var nav = targetControl.GetItemNavigator(item);
                                if (nav == null)
                                {
                                    MessageBox.Show("Cannot drop from a different control.", "Drag and Drop", MessageBoxButton.OK);
                                    return;
                                }
                                else
                                {
                                    if (nav.GoToCommonAncestor(targetItem))
                                    {
                                        if (nav.CurrentItem == item)
                                        {
                                            MessageBox.Show("Cannot drop onto a descendant item.", "Drag and Drop", MessageBoxButton.OK);
                                            return;
                                        }
                                    }
                                }
                            }
                        }

                        // Only support a single effect (you could add support for other effects like Copy if the Ctrl key is down here)
                        if ((originalEffects & DragDropEffects.Move) == DragDropEffects.Move)
                        {
                            e.Effects = DragDropEffects.Move;
                            e.Handled = true;
                        }

                        // Move items
                        foreach (var item in items)
                        {
                            var nav = targetControl.GetItemNavigator(item);
                            if (nav.GoToParent())
                            {
                                var itemModel   = item as TreeNodeModel;
                                var parentModel = nav.CurrentItem as TreeNodeModel;
                                if ((itemModel != null) && (parentModel != null))
                                {
                                    var index = parentModel.Children.IndexOf(itemModel);
                                    if (index != -1)
                                    {
                                        if ((parentModel == targetModel) && (index < targetDropIndex))
                                        {
                                            targetDropIndex--;
                                        }

                                        parentModel.Children.RemoveAt(index);
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                                else
                                {
                                    break;
                                }

                                targetModel.Children.Insert(Math.Max(0, Math.Min(targetModel.Children.Count, targetDropIndex++)), itemModel);
                                targetModel.IsExpanded = true;

                                // Focus the last item
                                if (items[items.Count - 1] == item)
                                {
                                    targetControl.FocusItem(itemModel);
                                }
                            }
                        }
                    }
                }
            }
        }