Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / C# 4.0

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

Rate me:
Please Sign up or sign in to vote.
4.96/5 (28 votes)
25 Jun 2011Ms-PL3 min read 106K   2.9K   54   30
An example of a Silverlight Drag And Drop / Or Browse File Upload Control using View Model / MVVM
Image 1

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

Image 2

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.

Image 3

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.

Image 4

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

Image 5

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

The Upload Control

Image 6

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

Image 7

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

Image 8

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

Image 9

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:

C#
[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
}

Image 10

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

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

Image 11

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:

C#
[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

Image 12

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)


Written By
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
AiHelpWebsite.com,
LightSwitchHelpWebsite.com, and
HoloLensHelpWebsite.com.

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

He is the Author of:

Comments and Discussions

 
GeneralMy vote of 5 Pin
SagarRS10-Jun-13 23:21
professionalSagarRS10-Jun-13 23:21 
GeneralMy vote of 5 Pin
PraveenKumarReddyChinta4-Dec-12 1:21
PraveenKumarReddyChinta4-Dec-12 1:21 
GeneralNice Pin
PraveenKumarReddyChinta4-Dec-12 1:20
PraveenKumarReddyChinta4-Dec-12 1:20 
Nice article Smile | :)
GeneralMy vote of 5 Pin
delibey1-Nov-11 15:08
delibey1-Nov-11 15:08 
GeneralVisual Basic Version Pin
ir_programmer25-Jan-11 3:53
ir_programmer25-Jan-11 3:53 
GeneralRe: Visual Basic Version Pin
defwebserver25-Jan-11 6:34
defwebserver25-Jan-11 6:34 
GeneralMy vote of 5 Pin
prasad0222-Dec-10 4:19
prasad0222-Dec-10 4:19 
GeneralRe: My vote of 5 Pin
defwebserver31-Dec-10 11:43
defwebserver31-Dec-10 11:43 
GeneralMy vote of 5 Pin
DrABELL12-Dec-10 16:54
DrABELL12-Dec-10 16:54 
GeneralRe: My vote of 5 Pin
defwebserver21-Dec-10 11:52
defwebserver21-Dec-10 11:52 
GeneralMy vote of 5 Pin
Kanasz Robert7-Dec-10 10:58
professionalKanasz Robert7-Dec-10 10:58 
GeneralRe: My vote of 5 Pin
defwebserver8-Dec-10 2:20
defwebserver8-Dec-10 2:20 
GeneralAwesome!!! Pin
PCoffey7-Dec-10 3:00
PCoffey7-Dec-10 3:00 
GeneralRe: Awesome!!! Pin
defwebserver7-Dec-10 3:06
defwebserver7-Dec-10 3:06 
GeneralMy vote of 5 Pin
Brij3-Dec-10 21:46
mentorBrij3-Dec-10 21:46 
GeneralRe: My vote of 5 Pin
defwebserver4-Dec-10 4:03
defwebserver4-Dec-10 4:03 
GeneralJust a query Pin
Kunal Chowdhury «IN»3-Dec-10 17:17
professionalKunal Chowdhury «IN»3-Dec-10 17:17 
GeneralRe: Just a query Pin
defwebserver3-Dec-10 18:09
defwebserver3-Dec-10 18:09 
GeneralRe: Just a query Pin
Kunal Chowdhury «IN»3-Dec-10 18:13
professionalKunal Chowdhury «IN»3-Dec-10 18:13 
GeneralMy vote of 5 Pin
Kunal Chowdhury «IN»3-Dec-10 17:14
professionalKunal Chowdhury «IN»3-Dec-10 17:14 
GeneralNice job Pin
Pete O'Hanlon3-Dec-10 10:20
subeditorPete O'Hanlon3-Dec-10 10:20 
GeneralRe: Nice job Pin
defwebserver3-Dec-10 10:39
defwebserver3-Dec-10 10:39 
GeneralMy vote of 5 Pin
linuxjr3-Dec-10 8:42
professionallinuxjr3-Dec-10 8:42 
GeneralRe: My vote of 5 Pin
defwebserver3-Dec-10 10:40
defwebserver3-Dec-10 10:40 
GeneralMy vote of 5 Pin
Petr Pechovic3-Dec-10 7:57
professionalPetr Pechovic3-Dec-10 7:57 

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

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