Silverlight 4 Rough Notes: Silverlight as a Drop Target

Note – these posts are put together after a short time with Silverlight 4 as a way of providing pointers to some of the new features that Silverlight 4 has to offer. I’m posting these from the PDC as Silverlight 4 is announced for the first time so please bear that in mind when working through these posts.

One of the desktop integration features that people want from business applications is the ability to drag and drop.

Silverlight 4 has new functionality that lets you use a Silverlight application as a drop target – that is, files that come from the desktop can be dragged into a Silverlight application but not the other way around.

This is really easy to enable. On the UIElement class, there’s a new property called AllowDrop which is just a bool indicating whether the element allows file drops or not.

There are also a bunch of events DragEnter, DragLeave, DragOver, Drop to support drag-and-drop.

So, with a simple UI such as this one;

<UserControl x:Class="SilverlightApplication8.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition
                Height="Auto" />
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <CheckBox
            Margin="10"
            Content="Allow File Drop"
            IsChecked="{Binding ElementName=rect,Path=AllowDrop,Mode=TwoWay}" />
        <Rectangle
            x:Name="rect"
            Fill="Red"
            Grid.Row="1"
            Margin="10" 
            DragEnter="OnDragEnterEvent"
            DragLeave="OnDragLeaveEvent"
            DragOver="OnDragOverEvent"
            Drop="OnDropEvent"
            MinHeight="96"/>
        <ListBox
            ItemsSource="{Binding Images}"
            Grid.Row="2"
            Margin="10">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Image
                        Source="{Binding .}"
                        Width="96"
                        Height="96"
                        Margin="10" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

I can write a little code-behind;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Text;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows.Media.Imaging;

namespace SilverlightApplication8
{
  public partial class MainPage : UserControl
  {
    public MainPage()
    {
      InitializeComponent();

      this.Loaded += (s, e) =>
        {
          this.Images = new ObservableCollection<BitmapImage>();
          this.DataContext = this;          
        };
    }
    public ObservableCollection<BitmapImage> Images { get; set; }

    private void OnDragEnterEvent(object sender, DragEventArgs e)
    {
      rect.Fill = new SolidColorBrush(Colors.Green);
    }
    private void OnDragLeaveEvent(object sender, DragEventArgs e)
    {
      rect.Fill = new SolidColorBrush(Colors.Red);
    }
    private void OnDragOverEvent(object sender, DragEventArgs e)
    {
      rect.Fill = new SolidColorBrush(Colors.Yellow);
    }
    private void OnDropEvent(object sender, DragEventArgs e)
    {
      // Was it a file drop? ( should be as it's the only type of drop )
      if ((e.Data != null) &&
        (e.Data.GetDataPresent(DataFormats.FileDrop)))
      {
        // Woohoo!
        FileInfo[] files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];

        if (files != null)
        {
          foreach (FileInfo fileInfo in files)
          {
            using (FileStream fs = fileInfo.OpenRead())
            {
              BitmapImage bi = new BitmapImage();
              bi.SetSource(fs);

              this.Images.Add(bi);
              fs.Close();
            }
          }
        }       
      }
      OnDragLeaveEvent(null, null);
    }
  }
}

and that easily lets me drag-and-drop files from the desktop ( either as single files or as multiple sets of files ) and drop them onto the Rectangle in my UI and then I pick up that Drop event and attempt ( without much error handling ) to load those files as Images into a ListBox.

Neat.

As an aside – I found that trying to interrogate the DragEventArgs.Data whilst the drag operation is in flight ( i.e. rather than when you actually get the Drop event ) gave me a security exception and I suspect that’s part of the design so take care on that one.