Skip to content

Commit

Permalink
fix(dragdrop): Avoid double subscription (since ClearItem not always …
Browse files Browse the repository at this point in the history
…invoked) + make sure to subscribe event on CanDragItems change
  • Loading branch information
dr1rrb committed Dec 1, 2020
1 parent 6157b15 commit 9b135ac
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using Windows.ApplicationModel.DataTransfer;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Uno.Extensions;
Expand All @@ -17,9 +18,15 @@ namespace Windows.UI.Xaml.Controls
{
public partial class ListViewBase
{
private const string ReorderOwnerFormatId = DataPackage.UnoPrivateDataPrefix + "__list__view__base__source__";
private const string ReorderItemFormatId = DataPackage.UnoPrivateDataPrefix + "__list__view__base__source__item__";
private const string ReorderContainerFormatId = DataPackage.UnoPrivateDataPrefix + "__list__view__base__source__container__";
private const string DragItemsFormatId = DataPackage.UnoPrivateDataPrefix + "__list__view__base__items__";

public event DragItemsStartingEventHandler DragItemsStarting;
public event TypedEventHandler<ListViewBase, DragItemsCompletedEventArgs> DragItemsCompleted;

#region CanReorderItems (DP)
public static DependencyProperty CanReorderItemsProperty { get; } = DependencyProperty.Register(
nameof(CanReorderItems),
typeof(bool),
Expand All @@ -31,19 +38,69 @@ public bool CanReorderItems
get => (bool)GetValue(CanReorderItemsProperty);
set => SetValue(CanReorderItemsProperty, value);
}
#endregion

#region CanDragItems (DP)
public static DependencyProperty CanDragItemsProperty { get; } = DependencyProperty.Register(
nameof(CanDragItems),
typeof(bool),
typeof(ListViewBase),
new FrameworkPropertyMetadata(default(bool)));
new FrameworkPropertyMetadata(default(bool), OnCanDragItemsChanged));

public bool CanDragItems
{
get => (bool)GetValue(CanDragItemsProperty);
set => SetValue(CanDragItemsProperty, value);
}

private static void OnCanDragItemsChanged(DependencyObject snd, DependencyPropertyChangedEventArgs args)
{
if (snd is ListViewBase that && args.NewValue is bool canDragItems)
{
var items = that.MaterializedContainers.OfType<UIElement>();
if (canDragItems)
{
items.ForEach(PrepareContainerForDragDropCore);
}
else
{
items.ForEach(ClearContainerForDragDrop);
}
}
}
#endregion

private void PrepareContainerForDragDrop(UIElement itemContainer)
{
if (CanDragItems)
{
PrepareContainerForDragDropCore(itemContainer);
}
}

private static void PrepareContainerForDragDropCore(UIElement itemContainer)
{
// Known issue: the ContainerClearedForItem might not be invoked properly for all items on some platforms.
// This patch is acceptable as event handlers are static (so they won't leak).
itemContainer.DragStarting -= OnItemContainerDragStarting;
itemContainer.DropCompleted -= OnItemContainerDragCompleted;

itemContainer.CanDrag = true;
itemContainer.DragStarting += OnItemContainerDragStarting;
itemContainer.DropCompleted += OnItemContainerDragCompleted;
}

private static void ClearContainerForDragDrop(UIElement itemContainer)
{
itemContainer.DragStarting -= OnItemContainerDragStarting;
itemContainer.DropCompleted -= OnItemContainerDragCompleted;

itemContainer.DragEnter -= OnReorderUpdated;
itemContainer.DragOver -= OnReorderUpdated;
itemContainer.DragLeave -= OnReorderCompleted;
itemContainer.Drop -= OnReorderCompleted;
}

private static void OnItemContainerDragStarting(UIElement sender, DragStartingEventArgs innerArgs)
{
if (ItemsControlFromItemContainer(sender) is ListViewBase that && that.CanDragItems)
Expand All @@ -70,6 +127,12 @@ private static void OnItemContainerDragStarting(UIElement sender, DragStartingEv
args.Data.SetData(ReorderItemFormatId, draggedItem);
args.Data.SetData(ReorderContainerFormatId, sender);

// For safety only, avoids double subscription
that.DragEnter -= OnReorderUpdated;
that.DragOver -= OnReorderUpdated;
that.DragLeave -= OnReorderCompleted;
that.Drop -= OnReorderCompleted;

that.DragEnter += OnReorderUpdated;
that.DragOver += OnReorderUpdated;
that.DragLeave += OnReorderCompleted;
Expand Down
16 changes: 2 additions & 14 deletions src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
using Uno.Client;
using System.Threading.Tasks;
using System.Threading;
using Windows.ApplicationModel.DataTransfer;
using Windows.Foundation;
using Uno.UI;

Expand Down Expand Up @@ -657,29 +656,18 @@ protected override void PrepareContainerForItemOverride(DependencyObject element
}
}

private const string ReorderOwnerFormatId = DataPackage.UnoPrivateDataPrefix + "__list__view__base__source__";
private const string ReorderItemFormatId = DataPackage.UnoPrivateDataPrefix + "__list__view__base__source__item__";
private const string ReorderContainerFormatId = DataPackage.UnoPrivateDataPrefix + "__list__view__base__source__container__";
private const string DragItemsFormatId = DataPackage.UnoPrivateDataPrefix + "__list__view__base__items__";

internal override void ContainerPreparedForItem(object item, SelectorItem itemContainer, int itemIndex)
{
base.ContainerPreparedForItem(item, itemContainer, itemIndex);

if (CanDragItems)
{
itemContainer.CanDrag = true;
itemContainer.DragStarting += OnItemContainerDragStarting;
itemContainer.DropCompleted += OnItemContainerDragCompleted;
}
PrepareContainerForDragDrop(itemContainer);

ContainerContentChanging?.Invoke(this, new ContainerContentChangingEventArgs(item, itemContainer, itemIndex));
}

internal override void ContainerClearedForItem(object item, SelectorItem itemContainer)
{
itemContainer.DragStarting -= OnItemContainerDragStarting;
itemContainer.DropCompleted -= OnItemContainerDragCompleted;
ClearContainerForDragDrop(itemContainer);

base.ContainerClearedForItem(item, itemContainer);
}
Expand Down

0 comments on commit 9b135ac

Please sign in to comment.