TreeListView DragDrop Visuals and Tooltip

5 posts, 0 answers
  1. Ryan
    Ryan avatar
    4 posts
    Member since:
    Nov 2015

    Posted 10 Aug Link to this post

    Hello,

    While comparing TreeView and TreeListView controls, I've found that the out-of-the-box visuals for DragDrop is much more complete for the TreeView than it is for the TreeListView. I've attached two images, the first showing the TreeView's preview, and the second is showing the TreeListView.

    I could not find any examples of the TreeListView that would give a preview like "Drop before x..", can this be achieved for the TreeListView? 

    Thanks, 
    Ryan
  2. Yoan
    Admin
    Yoan avatar
    1066 posts

    Posted 15 Aug Link to this post

    Hi Ryan,

    It seems that the attachments are missing. However, custom dropIndication details can be achieved with RadTreeListView control. You can check this online demo which demonstrates how this is achieved for the TreeView (please check the TreeViewDragDropBehavior class). The same approach is applicable with little modifications for the TreeListView.

    Regards,
    Yoan
    Telerik by Progress
    Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
  3. UI for WPF is Visual Studio 2017 Ready
  4. Ryan
    Ryan avatar
    4 posts
    Member since:
    Nov 2015

    Posted 15 Aug Link to this post

    Thanks for the references. Too bad there is not anything specific for the TreeListView. Is the drag and drop behavior for the TreeListView more closely related to the GridView, or the TreeView? 

    Using the behavior below, what pieces are missing for displaying the "drop before", "drop inside" tooltips? 

    Thanks,
    Ryan

    public class TreeViewDragDropBehavior
        {
            private object originalSource = null;
            private IList sourceCollection = null;
            private IList destinationCollection = null;
     
            private RadTreeListView _associatedObject;
             
            public IList SourceCollection
            {
                get
                {
                    return this.sourceCollection;
                }
                set
                {
                    this.sourceCollection = value;
                }
            }
     
            /// <summary>
            /// AssociatedObject Property
            /// </summary>
            public RadTreeListView AssociatedObject
            {
                get
                {
                    return _associatedObject;
                }
                set
                {
                    _associatedObject = value;
                }
            }
     
            private static Dictionary<RadTreeListView, TreeViewDragDropBehavior> instances;
     
            static TreeViewDragDropBehavior()
            {
                instances = new Dictionary<RadTreeListView, TreeViewDragDropBehavior>();
            }
     
            public static bool GetIsEnabled(DependencyObject obj)
            {
                return (bool)obj.GetValue(IsEnabledProperty);
            }
     
            public static void SetIsEnabled(DependencyObject obj, bool value)
            {
                TreeViewDragDropBehavior behavior = GetAttachedBehavior(obj as RadTreeListView);
     
                behavior.AssociatedObject = obj as RadTreeListView;
     
                if (value)
                {
                    behavior.Initialize();
                }
                else
                {
                    behavior.CleanUp();
                }
                obj.SetValue(IsEnabledProperty, value);
            }
     
            // Using a DependencyProperty as the backing store for IsEnabled.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty IsEnabledProperty =
                DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(TreeViewDragDropBehavior),
                    new PropertyMetadata(new PropertyChangedCallback(OnIsEnabledPropertyChanged)));
     
            public static void OnIsEnabledPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
            {
                #if !SILVERLIGHT
                SetIsEnabled(dependencyObject, (bool)e.NewValue);
                #endif
            }
     
            public static TreeViewDragDropBehavior GetAttachedBehavior(RadTreeListView gridview)
            {
                if (!instances.ContainsKey(gridview))
                {
                    instances[gridview] = new TreeViewDragDropBehavior();
                    instances[gridview].AssociatedObject = gridview;
                }
     
                return instances[gridview];
            }
     
            protected virtual void Initialize()
            {
                DragDropManager.AddDragInitializeHandler(this.AssociatedObject, OnDragInitialize, true);
                DragDropManager.AddDropHandler(this.AssociatedObject, OnDrop, true);
                DragDropManager.AddDragDropCompletedHandler(this.AssociatedObject, OnDragDropCompleted, true);
                DragDropManager.AddDragOverHandler(this.AssociatedObject, OnDragOver, true);
     
                this.AssociatedObject.DataLoaded += RadTreeListView1_DataLoaded;
            }
     
            protected virtual void CleanUp()
            {
                DragDropManager.RemoveDragInitializeHandler(this.AssociatedObject, OnDragInitialize);
                DragDropManager.RemoveDropHandler(this.AssociatedObject, OnDrop);
                DragDropManager.RemoveDragDropCompletedHandler(this.AssociatedObject, OnDragDropCompleted);
                DragDropManager.RemoveDragOverHandler(this.AssociatedObject, OnDragOver);
     
                this.AssociatedObject.DataLoaded -= RadTreeListView1_DataLoaded;
            }
     
            private void OnDragInitialize(object sender, DragInitializeEventArgs e)
            {
                var sourceRow = (e.OriginalSource as TreeListViewRow) ?? (e.OriginalSource as FrameworkElement).ParentOfType<TreeListViewRow>();
     
                if (sourceRow != null)
                {
                    var dataObject = DragDropPayloadManager.GeneratePayload(null);
     
                    var draggedItem = sourceRow.Item;
     
                    DragDropPayloadManager.SetData(dataObject, "DragData", new Collection<object>() { draggedItem });
                    e.Data = dataObject;
     
                    var screenshotVisualProvider = new ScreenshotDragVisualProvider();
                    e.DragVisual = screenshotVisualProvider.CreateDragVisual(new DragVisualProviderState(e.RelativeStartPoint, new List<object>() { draggedItem }, new List<DependencyObject>() { sourceRow }, sender as FrameworkElement));
                    e.DragVisualOffset = new Point(0, 0);
                    this.originalSource = sourceRow.Item;
                    this.sourceCollection = sourceRow.ParentRow != null ? (IList)sourceRow.ParentRow.Items.SourceCollection : (IList)sourceRow.GridViewDataControl.ItemsSource;
                }
            }
     
            private void OnDragDropCompleted(object sender, DragDropCompletedEventArgs e)
            {
            }
     
            private void OnDrop(object sender, Telerik.Windows.DragDrop.DragEventArgs e)
            {
                if (e.Data != null && e.AllowedEffects != DragDropEffects.None)
                {
                    Collection<Object> payload = DragDropPayloadManager.GetDataFromObject(e.Data, "DragData") as Collection<Object>;
                    if (payload != null)
                    {
                        Folder droppedItem = (Folder)payload[0];
                        var destinationRow = e.OriginalSource as TreeListViewRow ?? (e.OriginalSource as FrameworkElement).ParentOfType<TreeListViewRow>();
                        if (destinationRow != null)
                        {
                            Folder targetItem = destinationRow.DataContext as Folder;
                            TreeListViewDropPosition relativeDropPosition = (TreeListViewDropPosition)destinationRow.GetValue(RadTreeListView.DropPositionProperty);
                            this.destinationCollection = relativeDropPosition == TreeListViewDropPosition.Inside ? (IList)destinationRow.Items.SourceCollection :
                                                                                                                                                                 destinationRow.ParentRow != null ? (IList)destinationRow.ParentRow.Items.SourceCollection : (IList)destinationRow.GridViewDataControl.ItemsSource;
                            MoveItem(droppedItem, targetItem, relativeDropPosition);
                        }
                        else
                        {
                            this.destinationCollection = (IList)(sender as RadTreeListView).ItemsSource;
                            MoveItemToRoot(droppedItem);
                        }
                    }
                }
            }
     
            private void OnDragOver(object sender, Telerik.Windows.DragDrop.DragEventArgs e)
            {
                var dropTarget = e.OriginalSource as TreeListViewRow ?? (e.OriginalSource as FrameworkElement).ParentOfType<TreeListViewRow>();
                if (this.IsChildOf(dropTarget, this.originalSource))
                {
                    e.Effects = DragDropEffects.None;
                }
     
                e.Handled = true;
            }
     
            private bool IsChildOf(TreeListViewRow dropTarget, object originalSource)
            {
                var currentElement = dropTarget;
                while (currentElement != null)
                {
                    if (currentElement.Item == originalSource)
                    {
                        return true;
                    }
     
                    currentElement = currentElement.ParentRow;
                }
     
                return false;
            }
     
            void RadTreeListView1_DataLoaded(object sender, EventArgs e)
            {
                this.AssociatedObject.DataLoaded -= new EventHandler<EventArgs>(RadTreeListView1_DataLoaded);
                this.AssociatedObject.ExpandAllHierarchyItems();
            }
     
            private void MoveItemToRoot(Folder droppedItem)
            {
                var parentCollection = sourceCollection;
                parentCollection.Remove(droppedItem);
                droppedItem.ParentFolder = null;
                (destinationCollection).Add(droppedItem);
     
                this.AssociatedObject.ExpandAllHierarchyItems();
            }
     
            private void MoveItem(Folder droppedItem, Folder targetItem, TreeListViewDropPosition relativeDropPosition)
            {
                if (droppedItem == targetItem)
                    return;
     
                var parentCollection = this.sourceCollection;
     
                parentCollection.Remove(droppedItem);
     
                if (relativeDropPosition == TreeListViewDropPosition.Inside)
                {
                    destinationCollection.Add(droppedItem);
                }
                else if (relativeDropPosition == TreeListViewDropPosition.Before)
                {
                    destinationCollection.Insert(destinationCollection.IndexOf(targetItem), droppedItem);
                }
                else if (relativeDropPosition == TreeListViewDropPosition.After)
                {
                    destinationCollection.Insert(destinationCollection.IndexOf(targetItem) + 1, droppedItem);
                }
     
                this.AssociatedObject.ExpandAllHierarchyItems();
            }
        }
  5. Ryan
    Ryan avatar
    4 posts
    Member since:
    Nov 2015

    Posted 17 Aug Link to this post

    So I figured out something that somewhat emulates the TreeView's DragDrop behavior. I'm using a DragDrop template, which has a ContentPresenter that takes in the screenshot object from the ScreenshotDragVisualProvider().CreateDragVisual(). So I have a preview screenshot of all the nodes being moved, and a preview screenshot of the target node, and I just use the custom template to display the drop position w/the ContentPresenters which show the screenshot visuals. 

    Thanks for the help,
    Ryan
  6. Stefan X1
    Admin
    Stefan X1 avatar
    514 posts

    Posted 18 Aug Link to this post

    Hello Ryan,

    We already discussed this in the support thread you opened regarding this topic. I will, however, paste my answer here as well, so that it is shared with the community.

    In order to customize the appearance of the dragged item, you need to set the DragVisual property of the event arguments of the DragInitialize event handler. It provides the Content and ContentTemplate properties, which you can use to achieve the desired appearance.

    Please take a look at the Drag and Drop within RadGridView help topic and the relevant SDK Example, as the approach demonstrated there is applicable to RadTreeListView as well.


    All the best,
    Stefan X1
    Telerik by Progress
    Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
Back to Top
UI for WPF is Visual Studio 2017 Ready