Using the SaveFileDialog in Silverlight


This article is compatible with the latest version of Silverlight.

Introduction

The SaveFileDialog is a new dialog control introduced in Silverlight 3 which allows the user to save a file on the client machine.

Overview

To demonstrate the common use of the SaveFileDialog I’ll give an example:

Source code

When you click on the button a dialog window appears which allows you to save a file.

In the XAML we only have a Button and a TextBlock which will show possible errors or a message:

<UserControl x:Class=”FileSaveDialogDemo.MainPage”
    xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;
    xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;
    xmlns:d=”http://schemas.microsoft.com/expression/blend/2008&#8243;
    xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006&#8243;
    mc:Ignorable=”d” d:DesignWidth=”640″ d:DesignHeight=”480″>
    <Canvas x:Name=”LayoutRoot” Background=”White”>
        <Button x:Name=”btnSaveFile” Width=”100″ Height=”20″
                Content=”Save File” Click=”btnSaveFile_Click”
                Canvas.Top=”10″ Canvas.Left=”10″></Button>
        <TextBlock x:Name=”tblError” Canvas.Top=”40″ Canvas.Left=”10″></TextBlock>
    </Canvas>
</UserControl>

The interesting part comes in the code behind:

using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using FileSaveDialogDemo.FilesServiceReference;
namespace FileSaveDialogDemo
{
    public partial class MainPage : UserControl
    {
        #region Fields
        private SaveFileDialog dialog;
        #endregion
        #region Constructors
        public MainPage()
        {
            InitializeComponent();
            this.dialog = new SaveFileDialog();
            try
            {
                this.dialog.DefaultExt = “.txt”;
                this.dialog.Filter = “Text Files|*.txt|Log Files|*.log|All Files|*.*”;
                this.dialog.FilterIndex = 2;
            }
            catch ( Exception ex )
            {
                this.tblError.Text = “Error configuring SaveFileDialog: ” + ex.Message;
            }
        }
        #endregion
        #region Handlers
        private void btnSaveFile_Click( object sender, RoutedEventArgs e )
        {
            bool? dialogResult = this.dialog.ShowDialog();
            if ( dialogResult == true )
            {
                try
                {
                    FilesServiceReference.FilesClient fileClient
                        = new FilesClient();
                    fileClient.GetFileCompleted
                        += new EventHandler<GetFileCompletedEventArgs>(
                            fileClient_GetFileCompleted );
                    fileClient.GetFileAsync();
                    this.tblError.Text = “Getting file from the server…”;
                }
                catch ( Exception ex )
                {
                    this.tblError.Text = “Error calling service: ” + ex.Message;
                }
            }
        }
        void fileClient_GetFileCompleted( object sender, GetFileCompletedEventArgs e )
        {
            try
            {
                byte[] fileBytes = e.Result as byte[];
                using ( Stream fs = ( Stream )this.dialog.OpenFile() )
                {
                    fs.Write( fileBytes, 0, fileBytes.Length );
                    fs.Close();
                    this.tblError.Text = “File successfully saved!”;
                }
            }
            catch ( Exception ex )
            {
                this.tblError.Text = “Error getting result: ” + ex.Message;
            }
        }
        #endregion
    }
}

At first we need an instance of the SaveFileDialog class so we have created the following field:

private SaveFileDialog dialog;

and have initialized it in the constructor:

this.dialog = new SaveFileDialog();

After that we’ve adjusted some of the properties of the SaveFileDialog object:

this.dialog.DefaultExt = “.txt”;
this.dialog.Filter = “Text Files|*.txt|Log Files|*.log|All Files|*.*”;
this.dialog.FilterIndex = 2;

Let’s describe each of the properties:

  • DefaultExt – the default extension is appended to the file name if no extension is written by the user or if the extension is not specified by the filter.
  • Filter – the filter specifies the file type options which the user has when saving the file. In our case the options will be:

Each option consists of name which will be displayed in the dropdown and a wildcard expression specifying the filter by which the files in the directory view will be filtered. In our case we have the following options:
Text Files|*.txt
Log Files|*.log
All Files|*.*

All of them are separated by a vertical line.

Note that if the user does not write an extension it will be taken from the selected wildcard expression if it specifies a valid extension. For example if the user selects the first option a .txt extension will be added but if he/she selects the last option no extension will be added.

  • FilterIndex – the filter index specified the selected by default option. It is not zero based. In our case “Log Files” will be selected by default.

In the button click handler we call the ShowDialog method of the SaveFileDialog:

bool? dialogResult = this.dialog.ShowDialog();

After that we check if the returned result is true:

if (dialogResult == true)

If the result is true we call a service in order to get a text file from the server.

In the Completed event handler of the service we just take the file as a byte array and write it to the stream returned by the OpenFilemethod of the SaveFileDialog:

using (Stream fs = (Stream)this.dialog.OpenFile())
{
    fs.Write(fileBytes, 0, fileBytes.Length);
}

That’s all, your users are now able to save file from the server on their computers.