/// <summary>
        /// Handles the PreviewMouseLeftButtonDown event on the drag source.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        private void DragSourcePreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.sourceItemsControl = (TreeListBox)sender;
            var visual = e.OriginalSource as Visual;

            this.topWindow            = Window.GetWindow(this.sourceItemsControl);
            this.initialMousePosition = e.GetPosition(this.topWindow);

            this.sourceItemContainer = visual != null
                                           ? this.sourceItemsControl.ContainerFromElement(visual) as TreeListBoxItem
                                           : null;

            if (this.sourceItemContainer != null)
            {
                this.draggedData = new List <IDragSource>();
                this.draggedData.Add(this.sourceItemContainer.DataContext);

                // todo: how to drag multiple items?
                // must set e.Handled = true to avoid items being deselected
                // foreach (var si in sourceItemsControl.SelectedItems) this.draggedData.Add(si);
            }
            else
            {
                this.draggedData = null;
            }
        }
Пример #2
0
        /// <summary>
        /// Handles the PreviewMouseLeftButtonDown event on the drag source.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event args.
        /// </param>
        private void DragSourcePreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.sourceItemsControl = (TreeListBox)sender;
            var visual = e.OriginalSource as Visual;

            this.topWindow = Window.GetWindow(this.sourceItemsControl);
            this.initialMousePosition = e.GetPosition(this.topWindow);

            this.sourceItemContainer = visual != null
                                           ? this.sourceItemsControl.ContainerFromElement(visual) as TreeListBoxItem
                                           : null;
            if (this.sourceItemContainer != null)
            {
                this.draggedData = new List<IDragSource>();
                this.draggedData.Add(this.sourceItemContainer.DataContext);

                // todo: how to drag multiple items?
                // must set e.Handled = true to avoid items being deselected
                // foreach (var si in sourceItemsControl.SelectedItems) this.draggedData.Add(si);
            }
            else
            {
                this.draggedData = null;
            }
        }
        /// <summary>
        /// Decides the drop target.
        /// </summary>
        /// <param name="e">The event args.</param>
        private void DecideDropTarget(DragEventArgs e)
        {
            // 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.
            int targetItemsControlCount = this.targetItemsControl.Items.Count;
            var draggedItems            = e.Data.GetData(this.format.Name) as IList;

            if (targetItemsControlCount > 0)
            {
                this.hasVerticalOrientation = true;
                this.targetItemContainer    =
                    this.targetItemsControl.ContainerFromElement((DependencyObject)e.OriginalSource) as TreeListBoxItem;

                if (this.targetItemContainer != null)
                {
                    Point  positionRelativeToItemContainer = e.GetPosition(this.targetItemContainer);
                    double relativePosition = GetRelativePosition(
                        this.targetItemContainer, positionRelativeToItemContainer, this.hasVerticalOrientation);
                    this.isInFirstHalf = relativePosition < 0.5;

                    if (relativePosition > 0.3 && relativePosition < 0.7)
                    {
                        this.dropPosition = DropPosition.Add;
                    }
                    else
                    {
                        this.dropPosition = relativePosition < 0.5
                                                ? DropPosition.InsertBefore
                                                : DropPosition.InsertAfter;
                    }
                }
                else
                {
                    this.targetItemContainer = this.targetItemsControl.ContainerFromIndex(targetItemsControlCount - 1);
                    this.isInFirstHalf       = false;
                    this.dropPosition        = DropPosition.InsertAfter;
                }
            }
            else
            {
                this.targetItemContainer = null;
                this.dropPosition        = DropPosition.InsertBefore;
            }

            if (this.targetItemContainer != null && draggedItems != null)
            {
                var dropTarget = this.targetItemContainer.DataContext as IDropTarget;
                foreach (var draggedItem in draggedItems)
                {
                    var dragSource = draggedItem as IDragSource;
                    if ((dragSource == null || !dragSource.IsDraggable) ||
                        (dropTarget == null || !dropTarget.CanDrop(dragSource, this.dropPosition, (DragDropEffect)e.Effects)))
                    {
                        this.targetItemContainer = null;
                        e.Effects = DragDropEffects.None;
                        return;
                    }
                }
            }
        }
Пример #4
0
        /// <summary>
        /// Expands the specified item.
        /// </summary>
        /// <param name="item">
        /// The item.
        /// </param>
        internal void Expand(TreeListBoxItem item)
        {
            if (this.isPreparingContainer)
            {
                // cannot expand when preparing the container
                // add the item to a queue, and expand later
                this.expandItems.Enqueue(item);
                if (!this.isSubscribedToRenderingEvent)
                {
                    CompositionTarget.Rendering += this.CompositionTargetRendering;
                    this.isSubscribedToRenderingEvent = true;
                }

                return;
            }

            if (item.Content == null || item.Children == null)
            {
                return;
            }

            lock (this)
            {
                int i0 = this.Items.IndexOf(item.Content) + 1;
                foreach (var child in item.Children)
                {
                    if (this.Items.Contains(child))
                    {
                        Debug.WriteLine("TreeListBox bug.");
                        continue;
                    }

                    this.Items.Insert(i0++, child);
                    this.parentContainerMap[child] = item;
                }
            }
        }
Пример #5
0
        /// <summary>
        /// Decides the drop target.
        /// </summary>
        /// <param name="e">
        /// The event args.
        /// </param>
        private void DecideDropTarget(DragEventArgs e)
        {
            // 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.
            int targetItemsControlCount = this.targetItemsControl.Items.Count;
            var draggedItems = e.Data.GetData(this.format.Name) as IList;

            if (targetItemsControlCount > 0)
            {
                this.hasVerticalOrientation = true;
                this.targetItemContainer =
                    this.targetItemsControl.ContainerFromElement((DependencyObject)e.OriginalSource) as TreeListBoxItem;

                if (this.targetItemContainer != null)
                {
                    Point positionRelativeToItemContainer = e.GetPosition(this.targetItemContainer);
                    double relativePosition = GetRelativePosition(
                        this.targetItemContainer, positionRelativeToItemContainer, this.hasVerticalOrientation);
                    this.isInFirstHalf = relativePosition < 0.5;

                    if (relativePosition > 0.3 && relativePosition < 0.7)
                    {
                        this.dropPosition = DropPosition.Add;
                    }
                    else
                    {
                        this.dropPosition = relativePosition < 0.5
                                                ? DropPosition.InsertBefore
                                                : DropPosition.InsertAfter;
                    }
                }
                else
                {
                    this.targetItemContainer = this.targetItemsControl.ContainerFromIndex(targetItemsControlCount - 1);
                    this.isInFirstHalf = false;
                    this.dropPosition = DropPosition.InsertAfter;
                }
            }
            else
            {
                this.targetItemContainer = null;
                this.dropPosition = DropPosition.InsertBefore;
            }

            if (this.targetItemContainer != null && draggedItems != null)
            {
                var dropTarget = this.targetItemContainer.DataContext as IDropTarget;
                bool copy = (e.Effects & DragDropEffects.Copy) != 0;

                foreach (var draggedItem in draggedItems)
                {
                    var dragSource = draggedItem as IDragSource;
                    if ((dragSource == null || !dragSource.IsDraggable)
                        || (dropTarget == null || !dropTarget.CanDrop(dragSource, this.dropPosition, copy)))
                    {
                        this.targetItemContainer = null;
                        e.Effects = DragDropEffects.None;
                        return;
                    }
                }
            }
        }
Пример #6
0
        /// <summary>
        /// Handles child collection changes.
        /// </summary>
        /// <param name="parent">
        /// The parent.
        /// </param>
        /// <param name="e">
        /// The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.
        /// </param>
        internal void ChildCollectionChanged(TreeListBoxItem parent, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Remove:
                case NotifyCollectionChangedAction.Move:
                    foreach (var item in e.OldItems)
                    {
                        var container = this.ContainerFromItem(item);
                        if (container != null && container.IsExpanded)
                        {
                            container.IsExpanded = false;
                        }

                        this.Items.Remove(item);
                    }

                    break;

                case NotifyCollectionChangedAction.Reset:
                    var itemsToRemove = new List<TreeListBoxItem>();

                    foreach (var item in this.Items)
                    {
                        var container = this.ContainerFromItem(item);
                        if (container == null || !ReferenceEquals(container.ParentItem, parent))
                        {
                            continue;
                        }

                        itemsToRemove.Add(container);
                    }

                    foreach (var container in itemsToRemove)
                    {
                        if (container.IsExpanded)
                        {
                            container.IsExpanded = false;
                        }

                        this.Items.Remove(container.Content);
                    }

                    parent.IsExpanded = false;
                    parent.HasItems = false;
                    break;
                case NotifyCollectionChangedAction.Replace:
                    // todo: remove old items and child items
                    throw new NotImplementedException();
            }

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Move:
                case NotifyCollectionChangedAction.Add:
                    if (parent.IsExpanded)
                    {
                        int i;
                        if (e.NewStartingIndex + e.NewItems.Count < parent.Children.Count)
                        {
                            // inserted items
                            var followingChild = parent.Children[e.NewStartingIndex + e.NewItems.Count];
                            i = this.Items.IndexOf(followingChild);
                        }
                        else
                        {
                            // added items
                            var parentSibling = parent.GetNextSibling();
                            if (parentSibling == null)
                            {
                                // No sibling found, so add at the end of the list.
                                i = this.Items.Count;
                            }
                            else
                            {
                                // Found the sibling, so add the items before this item.
                                i = this.Items.IndexOf(parentSibling.Content);

                                //// int i0 = this.ItemContainerGenerator.IndexFromContainer(parentSibling);
                            }
                        }

                        if (i < 0)
                        {
                            Debug.WriteLine("TreeListBox bug.");
                        }
                        else
                        {
                            foreach (var item in e.NewItems)
                            {
                                this.parentContainerMap[item] = parent;
                                if (this.Items.Contains(item))
                                {
                                    Debug.WriteLine("TreeListBox bug.");
                                }

                                this.Items.Insert(i++, item);
                            }
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Replace:
                    for (int i = 0; i < e.NewItems.Count; i++)
                    {
                        this.Items[e.NewStartingIndex + i] = e.NewItems[i];
                    }

                    // todo: add new items and child items
                    throw new NotImplementedException();
            }

            parent.HasItems = parent.Children.Cast<object>().Any();
        }
Пример #7
0
        /// <summary>
        /// Collapses the specified item.
        /// </summary>
        /// <param name="item">
        /// The item.
        /// </param>
        internal void Collapse(TreeListBoxItem item)
        {
            if (item.Children == null)
            {
                return;
            }

            foreach (var child in item.Children)
            {
                var childContainer = this.ContainerFromItem(child);
                if (childContainer != null)
                {
                    if (childContainer.IsExpanded)
                    {
                        childContainer.IsExpanded = false;
                    }

                    if (childContainer.IsSelected)
                    {
                        childContainer.IsSelected = false;
                    }
                }

                this.Items.Remove(child);
            }
        }
Пример #8
0
        /// <summary>
        /// Inserts the specified items in the tree.
        /// </summary>
        /// <param name="parentContainer">The parent container.</param>
        /// <param name="newItems">The new items.</param>
        /// <param name="newStartingIndex">New index of the starting.</param>
        private void InsertItems(TreeListBoxItem parentContainer, IList newItems, int newStartingIndex)
        {
            // Find the index where the new items should be added
            int i;
            if (newStartingIndex + newItems.Count < parentContainer.Children.Count)
            {
                // inserted items
                var followingChild = parentContainer.Children[newStartingIndex + newItems.Count];
                i = this.Items.IndexOf(followingChild);
            }
            else
            {
                // added items
                var parentSibling = parentContainer.GetNextSibling();
                if (parentSibling == null)
                {
                    // No sibling found, so add at the end of the list.
                    i = this.Items.Count;
                }
                else
                {
                    // Found the sibling, so add the items before this item.
                    i = this.Items.IndexOf(parentSibling.Content);
                }
            }

            if (i < 0)
            {
#if !DEBUG
                throw new InvalidOperationException("Could not get parent index in TreeListBox.");
#else
                System.Diagnostics.Trace.WriteLine("Could not get parent index in TreeListBox.");
#endif
            }

            foreach (var item in newItems)
            {
                this.InsertItem(i, item, parentContainer.Content);
            }
        }
Пример #9
0
        /// <summary>
        /// Expands the specified container.
        /// </summary>
        /// <param name="container">The container to expand.</param>
        internal void Expand(TreeListBoxItem container)
        {
            if (this.isPreparingContainer)
            {
                // cannot expand when preparing the container
                // add the item to a queue, and expand later
                this.expandItemQueue.Enqueue(container);
                if (!this.isSubscribedToRenderingEvent)
                {
                    CompositionTarget.Rendering += this.CompositionTargetRendering;
                    this.isSubscribedToRenderingEvent = true;
                }

                return;
            }

            if (container.Content == null || container.Children == null)
            {
                return;
            }

            lock (this)
            {
                int i0 = this.Items.IndexOf(container.Content) + 1;
                var parent = container.Content;
                foreach (var child in container.Children)
                {
                    this.InsertItem(i0++, child, parent);
                }
            }
        }
Пример #10
0
        /// <summary>
        /// Handles child collection changes.
        /// </summary>
        /// <param name="parentContainer">The parent container.</param>
        /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs" /> instance containing the event data.</param>
        internal void ChildCollectionChanged(TreeListBoxItem parentContainer, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Remove:
                case NotifyCollectionChangedAction.Move:
                case NotifyCollectionChangedAction.Replace:
                    this.RemoveItems(e.OldItems);
                    break;

                case NotifyCollectionChangedAction.Reset:
                    this.RemoveItems(this.Items);
                    parentContainer.IsExpanded = false;
                    parentContainer.HasItems = false;
                    break;
            }

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Move:
                case NotifyCollectionChangedAction.Add:
                case NotifyCollectionChangedAction.Replace:
                    if (parentContainer.IsExpanded)
                    {
                        this.InsertItems(parentContainer, e.NewItems, e.NewStartingIndex);
                    }

                    break;
            }

            parentContainer.HasItems = parentContainer.Children.Cast<object>().Any();
        }
Пример #11
0
        /// <summary>
        /// Subscribes for changes on the specified collection.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="collection">The collection to observe.</param>
        private void SubscribeForCollectionChanges(TreeListBoxItem parent, IEnumerable collection)
        {
            if (this.ParentTreeListBox == null)
            {
                throw new InvalidOperationException("Parent not set.");
            }

            var cc = collection as INotifyCollectionChanged;
            if (cc != null)
            {
                var parentTreeListBox = this.ParentTreeListBox;
                this.collectionChangedHandler = (s, e) => parentTreeListBox.ChildCollectionChanged(parent, e);
                cc.CollectionChanged += this.collectionChangedHandler;
            }
        }
Пример #12
0
 /// <summary>
 /// Subscribes for collection changes.
 /// </summary>
 /// <param name="parent">
 /// The parent.
 /// </param>
 /// <param name="collection">
 /// The collection.
 /// </param>
 private void SubscribeForCollectionChanges(TreeListBoxItem parent, IEnumerable collection)
 {
     var cc = collection as INotifyCollectionChanged;
     if (cc != null)
     {
         this.collectionChangedHandler = (s, e) => this.ParentTreeListBox.ChildCollectionChanged(parent, e);
         cc.CollectionChanged += this.collectionChangedHandler;
     }
 }