Silverlight ListBoxDragDropTarget, why cant I drop there?

In my opinion Microsoft has slipped up when implementing the Silverlight DragDropTarget, whether intended or not.

I was having an issue last week where I had 2 list boxes bound to separate ObservableCollections of the same type; let’s say IFoo, and a concrete implementation class, Foo. When dragging items between the 2 list boxes I would be shown the red invalid icon even though AllowedSourceEffect=Link was enabled. To find out what was happening I inspected the System.Windows.Controls.Toolkit assembly with Jetbrains dotPeek to discover that the CanAddItem method, which gets continually evaluated with the mouse drag movement, calls through to a method called CanInsert in a CollectionHelper class which is as follows:

public static bool CanInsert(this IEnumerable collection, object item)
    {
      ICollectionView collectionView = collection as ICollectionView;
      if (collectionView != null)
        return CollectionHelper.CanInsert(collectionView.SourceCollection, item);
      if (CollectionHelper.IsReadOnly(collection))
        return false;
      Type type = Enumerable.FirstOrDefault(Enumerable.Where((IEnumerable) collection.GetType().GetInterfaces(), (Func) (interfaceType => interfaceType.FullName.StartsWith("System.Collections.Generic.IList`1", StringComparison.Ordinal))));
      if (type != null)
        return type.GetGenericArguments()[0] == item.GetType();
      if (collection is IList)
        return true;
      else
        return false;
    }

I would like to say that the reflection to determine the type is an awful line of code, using a string to check that a collection is being used is pretty ugly to say the least.

The problem lies with the return statement of the 3rd condition. This is where the method will usually return from, using the == operator which does a direct comparison of the types. In my case IFoo is not exactly the same type as Foo despite it implementing the interface. Problem! To resolve this I created my own implementation of ListBoxDragDropTarget which overwrites the CanInsert method and instead of using the == operator; I check whether the type IsAssignableFrom:

using System;
using System.Collections;
using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;

namespace SilverlightDragDrop
{
    public class FixedListBoxDragDropTarget : ListBoxDragDropTarget
    {
        protected override bool CanAddItem(ListBox itemsControl, object data)
        {
            if (itemsControl.ItemsSource == null)
                return true;
            bool canInsert = CanInsert(itemsControl.ItemsSource, data);
            return canInsert;
        }

        private static bool CanInsert(IEnumerable collection, object data)
        {
            ICollectionView collectionView = collection as ICollectionView;
            if (collectionView != null)
                return CanInsert(collectionView.SourceCollection, data);
            Type type = collection.GetType().GetInterfaces().Where(interfaceType => interfaceType.FullName.StartsWith("System.Collections.Generic.IList`1", StringComparison.Ordinal)).FirstOrDefault();
            if (type != null)
                return type.GetGenericArguments()[0].IsAssignableFrom(data.GetType());
            return collection is IList;
        }
    }
}

Problem solved.

Tags:

About Chris Haines

.NET Software Developer based in central London. Blogging about my day to day development stumbling blocks.

3 responses to “Silverlight ListBoxDragDropTarget, why cant I drop there?”

  1. stimul8d (@stimul8d) says :

    Excellent solution. Lovely. Yoink! ;-)

  2. jehof says :

    This problem drived me crazy until i found your blog post. Thanks for providing the solution.

  3. Albino says :

    Thanks a lot for the post. It would take me forever to track down the issue if wasn’t for your post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: