HowTo: Drop files onto Silverlight controls
I was recently adding drop-files support to ClipFlair Studio, so I had to do some research on the related API that is available to Silverlight apps.
Silverlight supports a limited set of Drag-and-Drop interaction with the operating system (supposedly for security reasons, but most probably because of the classic cross-platform implementation pains).
For example it allows you to drop a collections of file objects from the operating system’s file explorer (or other similar application) onto a Silverlight drop target, but not the other way around.
Also, it doesn’t allow dropping other flavors of content, like text, images etc., only collections of file objects.
To allow dropping files onto a Silverlight control you set AllowDrop property to true and handle the Drop and optionally the DragEnter/DragOver/DragLeave events to provide visual feedback during the drop operation (mostly when over the drop target, unless you do Mouse Capturing).
<RichTextBox
x:Name="rtb"
…
AllowDrop="True"
Drop="rtb_Drop"
DragEnter="rtb_DragEnter"
DragOver="rtb_DragOver"
DragLeave="rtb_DragLeave"
/>
#region DragAndDrop
private void rtb_Drop(object sender, System.Windows.DragEventArgs e)
{
VisualStateManager.GoToState(this, "Normal", true);//the Drop event passes in an array of FileInfo objects for the list of files that were selected and drag-dropped onto the RichTextBox.
if (e.Data == null)
return;IDataObject f = e.Data as IDataObject;
if (f != null) //checks if the dropped objects are files
{
object data = f.GetData(DataFormats.FileDrop); //Silverlight only supports FileDrop
FileInfo[] files = data as FileInfo[]; //…GetData returns null if format is not supportede.Handled = true;
if (files != null)
//Walk through the list of FileInfo objects of the selected and drag-dropped files and parse the .txt and .docx files
//and insert their content in the RichTextBox.
foreach (FileInfo file in files)
Load(file, false);
}}
private void rtb_DragEnter(object sender, System.Windows.DragEventArgs e)
{
VisualStateManager.GoToState(this, "DragOver", true);
e.Handled = true;
}
private void rtb_DragOver(object sender, System.Windows.DragEventArgs e)
{
e.Handled = true;
//NOP
}private void rtb_DragLeave(object sender, System.Windows.DragEventArgs e)
{
VisualStateManager.GoToState(this, "Normal", true);
e.Handled = true;
}#endregion
For the visual feedback you can use VisualStateManager and respective VisualStates at say a Grid that wraps the drop target in the XAML layout.
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DragStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.3">
<VisualTransition.GeneratedEasingFunction>
<CircleEase EasingMode="EaseIn"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="DragOver">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)" Storyboard.TargetName="rtb">
<EasingColorKeyFrame KeyTime="0" Value="#FFFFF7D1"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Note that on MacOS-X Safari doesn’t pass drop events automatically to plugins and you have to catch them via Javascript and pass them to Silverlight, plus I’ve read that in MacOS-X Firefox doesn’t support this at all.
Even worse, Microsoft’s workaround article has a typo in the Javascript (should call dragDrop instead of drop) and there is a chance it doesn’t work in latest version of Safari. For more see:
http://msdn.microsoft.com/en-us/library/ee670998%28v=vs.95%29.aspx
http://www.telerik.com/forums/dropping-files-using-safari-on-mac (this says one can use HTML5 events and the Silverlight HTML/Javascript Bridge to notify Silverlight)
http://www.thebuzzmedia.com/html5-drag-and-drop-and-file-api-tutorial/