Click here to Skip to main content
Click here to Skip to main content
Go to top

Silverlight Simple Drag And Drop / Or Browse View Model / MVVM File Upload Control

, 25 Jun 2011
Rate this:
Please Sign up or sign in to vote.
An example of a Silverlight Drag And Drop / Or Browse File Upload Control using View Model / MVVM

A Simple Silverlight View Model / MVVM File Upload Control

Live example: http://silverlight.adefwebserver.com/SimpleMVVMFileUpload
Update
: I created a blog post that creates a dramatization of this code: Silverlight View Model (MVVM) - A Play In One Act.

See an example of this code in a Visual Studio LightSwitch application at: http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/35/Saving-Files-To-File-System-With-LightSwitch-Uploading-Files.aspx

I have covered uploading files using Silverlight in a previous article: Silverlight View Model Style File Manager Drag and Drop Upload. In that article, you drop a file and it is uploaded immediately. However, while working on a Silverlight client for my popular Open Source Help Desk software program ADefHelpDesk.com, I realized that I needed to create an interface that allowed a user to select a file for upload, enter information on a form, and have the information saved, and the file uploaded with the push of a single button.

In addition, Silverlight allows a user to drag and drop a file, or to click a button and browse for a file. Also, if a user selects a file to upload and changes her/his mind, she/he needs a way to remove that file.

Therefore, my goals for an upload control were:

  • Easy to integrate into my existing View Model / MVVM projects
  • Allows a designer to use Microsoft Expression Blend to fully redesign all visuals and animations

The Upload Experience

When you run the example, you will see a file upload control that allows you to either browse for a file, or to drop a file on the space marked Drop File Here.

After you select a file, the Upload button will be enabled and you can click it to upload the file.

A button with a X will appear, and you can click that button to remove the selected file. Hovering over the file name will display a popup that shows the full name of the selected file.

When you click the Upload button, the upload control will display an animation to indicate the file is being uploaded.

After the file is uploaded, the file will display in the list box below the control.

The Upload Control

All the Silverlight code for the upload control is contained in the ADefFileUploadControl folder. The website code is mostly contained in the Webservice folder.

The Visual State Manager is used to define 3 Visual States for the control.

One for NoFileSelected, one for FileSelectedState, and one for FileUploadingState.

Behaviors are used to provide the primary functionality. The diagram above shows how the DropFilesToUploadBehavior and OpenFileDialogBoxBehavior are bound.

The code for the OpenFileDialogBoxBehavior is covered in the article Silverlight Open File Dialog Behavior (MVVM).

Here is the code for the DropFilesToUploadBehavior:

[System.ComponentModel.Description("Allows files to be dropped for upload")]
public class DropFilesToUploadBehavior : Behavior<UIElement>
{
    private double DropUIElementOpacity;

    // Properties

    #region FileDialogDialogResultProperty

    public static readonly DependencyProperty FileDialogDialogResultProperty =
        DependencyProperty.Register("FileDialogDialogResultProperty",
        typeof(FileInfo), typeof(DropFilesToUploadBehavior), null);

    public FileInfo FileDialogDialogResult
    {
        get
        {
            return (FileInfo)base.GetValue(FileDialogDialogResultProperty);
        }
        set
        {
            base.SetValue(FileDialogDialogResultProperty, value);
        }
    }
    #endregion

    #region ParentControlProperty

    public static readonly DependencyProperty ParentControlProperty =
        DependencyProperty.Register("ParentControlProperty",
        typeof(object), typeof(DropFilesToUploadBehavior), null);

    public object ParentControl
    {
        get
        {
            return (object)base.GetValue(ParentControlProperty);
        }
        set
        {
            base.SetValue(ParentControlProperty, value);
        }
    }
    #endregion

    #region SelectedFileNameProperty

    public static readonly DependencyProperty SelectedFileNameProperty =
        DependencyProperty.Register("SelectedFileName", typeof(string),
        typeof(DropFilesToUploadBehavior), null);

    public string SelectedFileName
    {
        get
        {
            return (string)base.GetValue(SelectedFileNameProperty);
        }
        set
        {
            base.SetValue(SelectedFileNameProperty, value);
        }
    }

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();

        // Save the opacity of the element
        DropUIElementOpacity = AssociatedObject.Opacity;

        // Turn on allow drop
        AssociatedObject.AllowDrop = true;

        // Attach event handlers
        AssociatedObject.DragOver += new DragEventHandler(DropUIElement_DragOver);
        AssociatedObject.DragLeave += new DragEventHandler(DropUIElement_DragLeave);
        AssociatedObject.Drop += new DragEventHandler(DropUIElement_Drop);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        // Attach event handlers
        AssociatedObject.DragOver -= new DragEventHandler(DropUIElement_DragOver);
        AssociatedObject.DragLeave -= new DragEventHandler(DropUIElement_DragLeave);
        AssociatedObject.Drop -= new DragEventHandler(DropUIElement_Drop);
    }

    #region DropUIElement_DragOver
    void DropUIElement_DragOver(object sender, DragEventArgs e)
    {
        // If you hover over the drop point, change it's opacity so users will 
        // have some indication that they can drop
        AssociatedObject.Opacity = (double)0.5;
    }
    #endregion

    #region DropUIElement_DragLeave
    void DropUIElement_DragLeave(object sender, DragEventArgs e)
    {
        // Return opacity to normal
        AssociatedObject.Opacity = DropUIElementOpacity;
    }
    #endregion

    #region DropUIElement_Drop
    void DropUIElement_Drop(object sender, DragEventArgs e)
    {
        // Return opacity to normal
        AssociatedObject.Opacity = DropUIElementOpacity;

        // If there is something being dropped upload it
        if (e.Data != null)
        {
            FileInfo[] Dropfiles = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];

            // Set the file to the FileInfo property
            // this should be bound to the ViewModel by the designer
            FileDialogDialogResult = Dropfiles[0];

            // Check File Extension
            if (!CheckFileExtension(FileDialogDialogResult.Extension))
            {
                // Show error
                MessageBox.Show("Only .gif, .jpg, .jpeg, .doc, 
        .docx, .xls, .xlsx, .pdf, .png, .txt files may be used.");
                // Clear the selected file
                FileDialogDialogResult = null;
            }
            else
            {
                SelectedFileName = ShortenFileName(FileDialogDialogResult.Name);

                // Change to selected file Visual State
                VisualStateManager.GoToState((Control)ParentControl, 
                "FileSelectedState", true);
            }
        }
    }
    #endregion

    #region ShortenFileName
    private string ShortenFileName(string filename)
    {
        string strFilename = "...";

        if (filename.Length > 10)
        {
            strFilename = filename.Substring(0, 10) + " ...";
        }
        else
        {
            strFilename = filename;
        }

        return strFilename;
    }
    #endregion

    #region CheckFileExtension
    private bool CheckFileExtension(string strExtension)
    {
        if (
            string.Compare(Path.GetExtension(strExtension).ToLower(), ".gif") != 0
            & string.Compare(Path.GetExtension(strExtension).ToLower(), ".jpg") != 0
            & string.Compare(Path.GetExtension(strExtension).ToLower(), ".jpeg") != 0
            & string.Compare(Path.GetExtension(strExtension).ToLower(), ".doc") != 0
            & string.Compare(Path.GetExtension(strExtension).ToLower(), ".docx") != 0
            & string.Compare(Path.GetExtension(strExtension).ToLower(), ".xls") != 0
            & string.Compare(Path.GetExtension(strExtension).ToLower(), ".xlsx") != 0
            & string.Compare(Path.GetExtension(strExtension).ToLower(), ".pdf") != 0
            & string.Compare(Path.GetExtension(strExtension).ToLower(), ".txt") != 0
            & string.Compare(Path.GetExtension(strExtension).ToLower(), ".png") != 0
            )
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    #endregion
}

The UploadingStateBehavior and NoFileSelectedStateBehavior are simple Behaviors that simply change state when invoked. For example:

protected override void Invoke(object parameter)
{
    // Change to File Uploading Visual State
    VisualStateManager.GoToState
        ((Control)AssociatedObject, "NoFileSelectedState", true);
}

The Behaviors are triggered by a DataTrigger. See Silverlight DataTrigger is the Answer to View Model / MVVM issues for more information on DataTriggers.

Here is the code for RemoveSelectedFileBehavior:

[System.ComponentModel.Description("Removes Selected File on Event Trigger")]
public class RemoveSelectedFileBehavior : TargetedTriggerAction<Button> 

{
    // Properties

    #region FileDialogDialogResultProperty

    public static readonly DependencyProperty FileDialogDialogResultProperty =
        DependencyProperty.Register("FileDialogDialogResultProperty",
        typeof(FileInfo), typeof(RemoveSelectedFileBehavior), null);

    public FileInfo FileDialogDialogResult
    {
        get
        {
            return (FileInfo)base.GetValue(FileDialogDialogResultProperty);
        }
        set
        {
            base.SetValue(FileDialogDialogResultProperty, value);
        }
    }
    #endregion

    #region ParentControlProperty

    public static readonly DependencyProperty ParentControlProperty =
        DependencyProperty.Register("ParentControlProperty",
        typeof(object), typeof(RemoveSelectedFileBehavior), null);

    public object ParentControl
    {
        get
        {
            return (object)base.GetValue(ParentControlProperty);
        }
        set
        {
            base.SetValue(ParentControlProperty, value);
        }
    }
    #endregion

    #region SelectedFileNameProperty

    public static readonly DependencyProperty SelectedFileNameProperty =
        DependencyProperty.Register("SelectedFileName", typeof(string),
        typeof(RemoveSelectedFileBehavior), null);

    public string SelectedFileName
    {
        get
        {
            return (string)base.GetValue(SelectedFileNameProperty);
        }
        set
        {
            base.SetValue(SelectedFileNameProperty, value);
        }
    }

    #endregion

    // Operations

    #region Invoke

    //Shows OpenFileDialog on event trigger you set in designer

    protected override void Invoke(object parameter)
    {
        // Clear Selected File
        FileDialogDialogResult = null;
        SelectedFileName = "File Name";

        // Change to selected file Visual State
        VisualStateManager.GoToState((Control)ParentControl, "NoFileSelectedState", true);
    }

    #endregion
}

Consuming the Control

The main control communicates with the upload control using View Model Communication. See Expression Blendable Silverlight View Model Communication for more information.

The code to upload the files was taken from the article, Silverlight View Model Style File Manager Drag and Drop Upload (part 2).

View Model / MVVM For Maximum Flexibility

Using Behaviors allowed me to create a control using as much code as I would using code behind. What I gained however, is the ability to create a completely different view with different animations and UI yet still uses the same View Model.

If you are new to View Model Style, it is suggested that you read Silverlight View Model Style : An (Overly) Simplified Explanation for an introduction.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

defwebserver
Software Developer (Senior) http://ADefWebserver.com
United States United States
Michael Washington is a Microsoft MVP. He is a ASP.NET and
C# programmer.
He is the founder of
LightSwitchHelpWebsite.com

He has a son, Zachary and resides in Los Angeles with his wife Valerie.

He is the Author of:
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinprofessionalSagarRS10-Jun-13 23:21 
GeneralMy vote of 5 PinmemberPraveenKumarReddyChinta4-Dec-12 1:21 
Very useful article
GeneralNice PinmemberPraveenKumarReddyChinta4-Dec-12 1:20 
GeneralMy vote of 5 Pinmemberdelibey1-Nov-11 15:08 
GeneralVisual Basic Version Pinmemberir_programmer25-Jan-11 3:53 
GeneralRe: Visual Basic Version Pinmvpdefwebserver25-Jan-11 6:34 
GeneralMy vote of 5 Pinmemberprasad0222-Dec-10 4:19 
GeneralRe: My vote of 5 Pinmemberdefwebserver31-Dec-10 11:43 
GeneralMy vote of 5 PinmemberDrABELL12-Dec-10 16:54 
GeneralRe: My vote of 5 Pinmemberdefwebserver21-Dec-10 11:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140921.1 | Last Updated 25 Jun 2011
Article Copyright 2010 by defwebserver
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid