Silverlight 4: Drag&drop with EventToCommand

One of the MVVM Light Toolkit’s user requested that I add the possibility to pass the EventArgs of an event to the ICommand that it is bound to through the EventToCommand trigger. At first I was a bit reluctant because it seems like a transgression of the rule that says you should avoid to have too much knowledge about the UI layer in the ViewModel. For example, if you have a RelayCommand in the ViewModel that expects a MouseEventArgs, it kinds of binds you to a certain kind of UI element, which is not super clean.

That said, I also understand that in some cases it might be useful to get the EventArgs down in the ViewModel, which is why I decided to add this possibility (available from V3/alpha 3).

And in fact, as I was testing it, I just got another request from my good friend Laurent Kempé (one of the early and enthusiastic users of MVVM Light) to help use EventToCommand in the case of a drag&drop operation in Silverlight 4. That was the perfect test case for this new feature.

Note: Laurent wrote a post in French describing a drag&drop implementation.

Resources

The source code for this sample is available here. The MVVM Light Toolkit’s DLLs are also included (V3/alpha3). This code is for Visual Studio 2010 beta 2 and/or Expression Blend Preview for Silverlight 4.

You can optionally install the MVVM Light Toolkit V3/alpha3 from the Installing Manually page.

More info about the MVVM Light Toolkit is available on the Get Started page.

Creating a drag&drop enabled Silverlight application

Let’s start by creating a new MVVM Light application in Silverlight 4. In the moment the process of installing the project templates is manual, but it should be easy enough if you follow these instructions.

  • You can create a new MVVM Light application for Silverlight 4 in Visual Studio 2010 or in Expression Blend Preview 4. Simply choose File, New Project and then select the template called MVVM Light (SL4).

We need to define a drop target for the files in the application. This can be done for just any UI element, so you can choose the main Grid (if you want the whole application to be a drop target) or any other element. In our case, to keep things simple, let’s define the main Grid as the drop target.

  • Set the property AllowDrop to True, either in XAML or in Blend. Also change the other properties as shown in XAML below.
    Note: We will implement the DroppedFileContent property in a moment.
<Grid x:Name="LayoutRoot"
      AllowDrop="True"
      Background="#FF9F9F9F">
    <TextBlock FontSize="36"
               FontWeight="Bold"
               Foreground="Purple"
               Text="{Binding DroppedFileContent}"
               VerticalAlignment="Center"
               HorizontalAlignment="Center"
               TextWrapping="Wrap"
               TextTrimming="WordEllipsis" />
</Grid>

  • Open the MainViewModel and add a bindable property.
    Note: If you have the MVVM Light Toolkit installed, you can use a code snippet for this: Type mvvminpc then tab to expand the snippet, and enter the property’s name, tab, its type, tab and the name of the attribute. Then, edit the code until it looks like the one below:
/// <summary>
/// The <see cref="DroppedFileContent" /> property's name.
/// </summary>
public const string DroppedFileContentPropertyName = "DroppedFileContent";

private string _droppedFile = "Drop file here";

/// <summary>
/// Gets the DroppedFileContent property.
/// Changes to that property's value raise the PropertyChanged event. 
/// </summary>
public string DroppedFileContent
{
    get
    {
        return _droppedFile;
    }

    set
    {
        if (_droppedFile == value)
        {
            return;
        }

        _droppedFile = value;
        RaisePropertyChanged(DroppedFileContentPropertyName);
    }
}
  • Add a RelayCommand<DragEventArgs> property to the MainViewModel. Name this property HandleDropCommand. You will need to add GalaSoft.MvvmLight.Command, System and System.Windows to the “using” section.
public RelayCommand<DragEventArgs> HandleDropCommand
{
    get;
    private set;
}
  • Finally, in the constructor, initialize the HandleDropCommand. We will start by simply showing a MessageBox when a file is dropped.
public MainViewModel()
{
    HandleDropCommand = new RelayCommand<DragEventArgs>(e =>
    {
        System.Windows.MessageBox.Show("Drop");
    });
}
  • Add an EventToCommand trigger to the main Grid. This element is part of the GalaSoft.MvvmLight.Extras DLL which is referenced by default in the MVVM Light project template, so if you start like we described here, you don’t need to do anything to use EventToCommand. If you start from a non MVVM Light application, you need to add a reference to GalaSoft.MvvmLight.dll, GalaSoft.MvvmLight.Extras.dll and System.Windows.Interactivity.dll
    Make sure you get the V3/alpha3 version.
    Make sure you reference the binaries from C:\Program Files\Laurent Bugnion (GalaSoft)\Mvvm Light Toolkit\VS10\Binaries\Silverlight).
    • Build the application to make sure that you have the latest stand loaded in Blend.
    • In Blend, open the Assets tab, select the EventToCommand from the Behaviors category and drag/drop it on the LayoutRoot grid.
    • Then set the EventName property to “Drop”.
    • Bind the Command property to the HandleDropCommand in the MainViewModel which is the Explicit Data Context of the main Grid.
      Note: To open the databinding editor, press the advanced properties widget (the small black square) next to the Command property, and select Data Binding from the context menu.

 

    • Finally, check the PassEventArgsToCommand checkbox.
      Note: The combination of having an argument of type EventArgs in the RelayCommand, and the PassEventArgsToCommand property set to true will cause the event’s EventArgs to be passed directly to the RelayCommand’s parameter.

 

  • If you do not have Blend, you can also add the EventToCommand in Visual Studio in XAML:
    • In the MainPage.xaml file, add two namespaces to the UserCommand tag:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras"

 

    • Then, add the EventToCommand trigger within the LayoutRoot grid with the following XAML code:
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Drop">
        <cmd:EventToCommand Command="{Binding HandleDropCommand, Mode=OneWay}"
                            PassEventArgsToCommand="True" />
    </i:EventTrigger>
</i:Interaction.Triggers>

 

  • Test the application: In Blend, press F5, or in Visual Studio press Ctrl-F5. Then, drag a file, and drop it on the main Grid. Notice how the mouse cursor indicates that the Grid is a valid drop target.

 

  • Drop the file. You should see the MessageBox proving that the RelayCommand has been called successfully.

Getting and handling the files

Now that we have the RelayCommand ready, let’s access the DragEventArgs and get the file content from there. In this sample example, we will accept TXT files, and display the first few words from the file content.

  • Replace the call to MessageBox within the HandleDropCommand implementation with the following code:

if (e.Data == null)
{
    return;
}

var files = e.Data.GetData(DataFormats.FileDrop)
    as FileInfo[];

// This works with multiple files, but in that
// simple case, let's just handle the 1st one
if (files == null
    || files.Length == 0)
{
    DroppedFileContent = "No files";
    return;
}

var file = files[0];

if (!file.Extension.ToLower().Equals(".txt"))
{
    DroppedFileContent = "Not a TXT file";
    return;
}

using (var stream = file.OpenRead())
{
    using (var reader = new StreamReader(stream))
    {
        // Read the first line
        var line = reader.ReadLine();
        DroppedFileContent = line;
    }
}                

Few notes about this code:

  • You can get a reference to the files dropped on the target with the DragEventArgs.Data.GetData method.
  • Note however that since we are in Silverlight within the security sandbox, you will get a security exception if you try to access the file’s FullName. You may get the content of the file (because it is dropped by the user) but not additional information about where these files are located.
  • In this example, we get only the first file, but it is easy to see how we could access all the files dropped.
  • We do handle only TXT files in this example.
  • Reading the file’s content is done in an usual way, with a Stream and a StreamReader. Note the presence of the “using” statements, to make it easier (no need to explicitly close and dispose the stream and the reader). For binary files, we would use a BinaryReader, etc…

An issue with Visual Studio 2010

In the course of preparing this article, I noticed an issue in Visual Studio 2010 when I attempt to debug the HandleDropCommand implementation. Even with a breakpoint placed within the code, the debugger does not break. I am not sure right now what is the reason, and the code does get executed, but this complicates debugging of course. I am investigating and talking to Microsoft about that, but right now I am not sure where it comes from.

Conclusion

In the MVVM pattern, we generally try to avoid putting too much code in the code behind because it complicates the developer-designer workflow. Though I am not a puritan about that :) I think that EventToCommand and the new PassEventArgsToCommand property are reasonable steps to make UI interaction such as drag&drop even easier to code.

Hope you like it :)

 

Print | posted on Thursday, December 17, 2009 4:49 AM

Feedback

# re: Silverlight 4: Drag&drop with EventToCommand

left by Laurent at 12/19/2009 6:57 AM Gravatar
Hey Fallon,

About your first question: The Messenger is designed to be completely independant from any constraint, so there is, in my opinion, no reason that it shouldn't work on the server. Make sure that you use the correct version (the one that says "WPF" and not "Silverlight", because the server side needs the full .NET.

Second question: I am a big fan of MEF, and I would like to encourage you to integrate MEF and MVVM Light Toolkit, this should work just fine. I recently created a prototype in Silverlight 4 for a client and used MEF and MVVM Light, with great success. I try to keep the toolkit as light as possible, and am a bit careful not to grow it too much. I do however believe in making it as compatible as possible with other frameworks such as MEF, and I think that right now it is in good shape in that respect.

That said, if you have specific ideas, feel free to suggest where you think that attributes might help, and I will check that out ;)

Cheers,
Laurent

# re: Silverlight 4: Drag&drop with EventToCommand

left by Azadeh at 3/4/2010 6:16 PM Gravatar
Hi,

Thank you for this great framework.

But I just faced an issue so it would be appreciated if you can give me a solution:
I want to send a parameter to one of ViewModel via its Constructor (ViewModel’s Constructor) but this is the locator who is responsible to create or clear the ViewModels so I can’t do that. Of course this issue can be solved by using Messenger(somehow) , But I’m looking a more practical and professional way to overcome this issue.

Any solutions could be useful.

Thanks,

# re: Silverlight 4: Drag&drop with EventToCommand

left by Michael at 3/19/2010 12:13 PM Gravatar
Thank you for this!

# re: Silverlight 4: Drag&drop with EventToCommand

left by nrupal at 3/25/2010 4:12 AM Gravatar
Hi,
Thank you for the MVVM Light toolkit. It was a good presentation at MIX10.

A few quick questions:
1. Is it possible to send the drop point information to the viewmodel?
2. Is it possible to add children to a Canvas from viewmodel ( by data binding to the children property - this approach doesn't work as children property doesn't allow for databinding)

A work around for the above two problems is by developing a custom canvas control and custom DragEventArgs (which has the position information in it with respect to the custom canvas).

Thanks.

# re: Silverlight 4: Drag&drop with EventToCommand

left by Shadowdog at 7/15/2010 8:46 AM Gravatar
Laurent - you are the Bomb!!! Keep up the good work and keep adding these great examples!!!

THANK-You. :)

# re: Silverlight 4: Drag&drop with EventToCommand

left by mark baer at 8/30/2010 9:19 AM Gravatar
Excellent article. I was able to get it working...I am trying to do something similar, but wish to drag a ListBoxItem onto a Scrollviewer where I then dynamically add fields/etc...I cant figure out how to handle the dragged item. It is a File object in your code, but I'm not sure what to do with a listitem. I'm sure it's something simple. Can anyone help me out with this?

Thanks

# re: Silverlight 4: Drag&drop with EventToCommand

left by Eric Smith at 9/15/2010 4:54 AM Gravatar
Thanks for the framework. I'm just beginning with Silverlight, but I'm a proponent of DDD and MVC, so this is a natural fit for my SL entry.

By the way, I was able to step into my HandleDropCommand Implementation in VS 2010.

# re: PassEventArgsToCommand

left by Andrey Tsvetkoff at 11/29/2010 10:46 AM Gravatar
Hello Laurent! Thank you for your framework.

I've got a question about the theoretical side of the above-referenced property ('PassEventArgsToCommand').

We can use it to be more declarative, and it's good. But we simultaneously violate the principle of dependency inversion — our ViewModel depends on the UI-framework specific XXXEventArgs class.

From the other side, we could use codebehind class to handle the DropEvent, and convert it to a Command call, with the FileInfo[] passed as parameter. It's more DI-friendly, but also more coddy.

My idea is - maybe the golden mean - that we can create the Converter in code (which converts from the UI-specific XXXEventArgs to some business-specific object) and pass that converter declaratively to the 'EventToCommand', via smth like 'EventArgsToCommandArgsConverter' property.

This way, we minimize the coding efforts, and also avoid the VM to depend on UI Framework. Our command has a parameter of 'FileInfo[]', which it is actually interested in.

What do you think of it?

# re: Silverlight 4: Drag&drop with EventToCommand

left by Gonzalo Rossello at 4/21/2011 5:59 PM Gravatar
But how do you start a drag using mvvm pattern?

# re: Silverlight 4: Drag&drop with EventToCommand

left by Laurent Bugnion at 4/25/2011 10:31 PM Gravatar
Hi,

In my opinion, a drag operation is a matter of the view, strictly. The result of it (a "drop" for instance) may be communicated to the VM for treatment but the drag itself should be handled in the view.

Cheers,
Laurent
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: