Today’s post is going to be a short one about how to import a file through a Visual Studio LightSwitch application (Desktop or Web) and store the data on the backend (e.g. SQL Server).
Sometimes we want to upload files in binary format and store them in the database, like a Word document or PDF file or any other file containing unstructured data. LightSwitch already has the built-in ability to store and retrieve images, but if you want to store other types of files we have to write a small bit of code.
This post is really just me picking out various pieces of code from my other posts and cramming it together to make a new post. Which is a nice break for me, since writing a post often takes about a week (after I write the code and write up a draft blog, then get feedback, and double check the code several times). But with this one I should be done in a day leaving more time to enjoy the very brief Summers we have up here in Fargo, North Dakota.
Alright, let’s get started making a simple LightSwitch Web Application. Let’s create the project, and a table that will hold our file data.
<controls:ChildWindow x:Class="LightSwitchApplication.UserCode.SelectFileWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" Width="394" Height="305" Title="Select File Dialog" > <Grid x:Name="LayoutRoot" Margin="2"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1" /> <Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,79,0" Grid.Row="1" /> <Button Content="Browse" Height="23" HorizontalAlignment="Left" Margin="291,92,0,0" Name="BrowseButton" VerticalAlignment="Top" Width="75" Click="BrowseButton_Click" /> <TextBox Height="23" HorizontalAlignment="Left" Margin="66,92,0,0" Name="FileTextBox" VerticalAlignment="Top" Width="219" IsEnabled="True"/> </Grid> </controls:ChildWindow>
<controls:ChildWindow x:Class="LightSwitchApplication.UserCode.SelectFileWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
Width="394" Height="305"
Title="Select File Dialog" >
<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1" />
<Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,79,0" Grid.Row="1" />
<Button Content="Browse" Height="23" HorizontalAlignment="Left" Margin="291,92,0,0" Name="BrowseButton" VerticalAlignment="Top" Width="75" Click="BrowseButton_Click" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="66,92,0,0" Name="FileTextBox" VerticalAlignment="Top" Width="219" IsEnabled="True"/>
</Grid>
</controls:ChildWindow>
//' Copyright © Microsoft Corporation. All Rights Reserved. //' This code released under the terms of the //' Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html) using System; using System.IO; using System.Windows; using System.Windows.Controls; namespace LightSwitchApplication.UserCode { public partial class SelectFileWindow : ChildWindow { public SelectFileWindow() { InitializeComponent(); } private FileStream documentStream; public FileStream DocumentStream { get { return documentStream; } set { documentStream = value; } } private String safeFileName; public String SafeFileName { get { return safeFileName; } set { safeFileName = value; } } /// <summary> /// OK Button /// </summary> private void OKButton_Click(object sender, RoutedEventArgs e) { this.DialogResult = true; } /// <summary> /// Cancel button /// </summary> private void CancelButton_Click(object sender, RoutedEventArgs e) { this.DialogResult = false; } /// <summary> /// Browse button /// </summary> private void BrowseButton_Click(object sender, RoutedEventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); if (openFileDialog.ShowDialog() == true) { this.FileTextBox.Text = openFileDialog.File.Name; this.safeFileName = openFileDialog.File.Name; this.FileTextBox.IsReadOnly = true; FileStream myStream = openFileDialog.File.OpenRead(); this.documentStream = myStream; } } } }
//' Copyright © Microsoft Corporation. All Rights Reserved.
//' This code released under the terms of the
//' Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html)
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
namespace LightSwitchApplication.UserCode
{
public partial class SelectFileWindow : ChildWindow
public SelectFileWindow()
InitializeComponent();
}
private FileStream documentStream;
public FileStream DocumentStream
get { return documentStream; }
set { documentStream = value; }
private String safeFileName;
public String SafeFileName
get { return safeFileName; }
set { safeFileName = value; }
/// <summary>
/// OK Button
/// </summary>
private void OKButton_Click(object sender, RoutedEventArgs e)
this.DialogResult = true;
/// Cancel button
private void CancelButton_Click(object sender, RoutedEventArgs e)
this.DialogResult = false;
/// Browse button
private void BrowseButton_Click(object sender, RoutedEventArgs e)
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
this.FileTextBox.Text = openFileDialog.File.Name;
this.safeFileName = openFileDialog.File.Name;
this.FileTextBox.IsReadOnly = true;
FileStream myStream = openFileDialog.File.OpenRead();
this.documentStream = myStream;
//' Copyright © Microsoft Corporation. All Rights Reserved. //' This code released under the terms of the //' Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html) using System; using System.Collections.Generic; using System.IO; using System.IO.IsolatedStorage; using System.Linq; using LightSwitchApplication.UserCode; using Microsoft.LightSwitch; using Microsoft.LightSwitch.Framework.Client; using Microsoft.LightSwitch.Presentation; using Microsoft.LightSwitch.Presentation.Extensions; using Microsoft.LightSwitch.Threading; namespace LightSwitchApplication { public partial class EditableFileInformationsGrid { partial void ImportAFile_Execute() { // To invoke our own dialog, we have to do this inside of the "Main" Dispatcher // And, since this is a web application, we can't directly invoke the Silverlight OpenFileDialog // class, we have to first invoke our own Silverlight custom control (i.e. SelectFileWindow) // and that control will be able to invoke the OpenFileDialog class (via the Browse button) Dispatchers.Main.BeginInvoke(() => { SelectFileWindow selectFileWindow = new SelectFileWindow(); selectFileWindow.Closed += new EventHandler(selectFileWindow_Closed); selectFileWindow.Show(); }); } /// <summary> /// Invoked when our custom Silverlight window closes /// </summary> void selectFileWindow_Closed(object sender, EventArgs e) { SelectFileWindow selectFileWindow = (SelectFileWindow)sender; // Continue if they hit the OK button AND they selected a file if (selectFileWindow.DialogResult == true && (selectFileWindow.DocumentStream != null)) { byte[] fileData = new byte[selectFileWindow.DocumentStream.Length]; using (StreamReader streamReader = new StreamReader(selectFileWindow.DocumentStream)) { for (int i = 0; i < selectFileWindow.DocumentStream.Length; i++) { fileData[i] = (byte)selectFileWindow.DocumentStream.ReadByte(); } } // Create a new record for this file, and store the data, name and length FileInformation fileInformation = this.DataWorkspace.ApplicationData.FileInformations.AddNew(); fileInformation.Name = selectFileWindow.SafeFileName; fileInformation.Miscellaneous = "Size of file in bytes: " + fileData.Length; fileInformation.Data = fileData; selectFileWindow.DocumentStream.Close(); selectFileWindow.DocumentStream.Dispose(); } } } }
using System.Collections.Generic;
using System.IO.IsolatedStorage;
using System.Linq;
using LightSwitchApplication.UserCode;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Framework.Client;
using Microsoft.LightSwitch.Presentation;
using Microsoft.LightSwitch.Presentation.Extensions;
using Microsoft.LightSwitch.Threading;
namespace LightSwitchApplication
public partial class EditableFileInformationsGrid
partial void ImportAFile_Execute()
// To invoke our own dialog, we have to do this inside of the "Main" Dispatcher
// And, since this is a web application, we can't directly invoke the Silverlight OpenFileDialog
// class, we have to first invoke our own Silverlight custom control (i.e. SelectFileWindow)
// and that control will be able to invoke the OpenFileDialog class (via the Browse button)
Dispatchers.Main.BeginInvoke(() =>
SelectFileWindow selectFileWindow = new SelectFileWindow();
selectFileWindow.Closed += new EventHandler(selectFileWindow_Closed);
selectFileWindow.Show();
});
/// Invoked when our custom Silverlight window closes
void selectFileWindow_Closed(object sender, EventArgs e)
SelectFileWindow selectFileWindow = (SelectFileWindow)sender;
// Continue if they hit the OK button AND they selected a file
if (selectFileWindow.DialogResult == true && (selectFileWindow.DocumentStream != null))
byte[] fileData = new byte[selectFileWindow.DocumentStream.Length];
using (StreamReader streamReader = new StreamReader(selectFileWindow.DocumentStream))
for (int i = 0; i < selectFileWindow.DocumentStream.Length; i++)
fileData[i] = (byte)selectFileWindow.DocumentStream.ReadByte();
// Create a new record for this file, and store the data, name and length
FileInformation fileInformation = this.DataWorkspace.ApplicationData.FileInformations.AddNew();
fileInformation.Name = selectFileWindow.SafeFileName;
fileInformation.Miscellaneous = "Size of file in bytes: " + fileData.Length;
fileInformation.Data = fileData;
selectFileWindow.DocumentStream.Close();
selectFileWindow.DocumentStream.Dispose();
Additionally, there is an extension that can be added to Visual Studio LightSwitch called Document Toolkit for LightSwitch, which handles importing and viewing Word Documents. It will only work for Desktop applications, and it isn’t free, but other than that it looks like a slick extension.
That’s it for this brief post. I've included a zip file below of the C# code.
Again, love to hear if there are any questions, and if you think something is wrong with this code (or the title) then you are probably right so please let me know.
-Matt Sampson
Nice post, it would also be nice to get some examples of uploading files into a sharepoint library, or saving them to a UNC Path on the network.
I used some of this code to create a sample that uploads files to the file system:
lightswitchhelpwebsite.com/.../Saving-Files-To-File-System-With-LightSwitch-Uploading-Files.aspx
Thanks, Michael.
Nice work on the sample. Flattered to hear you used some of this code :)
Hi Matt,
Great post, very helpfull and NO bugs... Saves me a great amount of time.
This is working on LightSwitch 2011 RTM
Maybe for beginners, simply tell them to rename "ApplicationData" to their own data source name.
Francis
I also posted this question on the LightSwitch forum:
I'm developing a project in which on several occasions I have to add a document to a table. I read the tutorial by Matt Sampson (blogs.msdn.com/.../how-to-import-and-store-a-data-file.aspx) and was able to create the custom control and add files to a table.
But, when this table is a detail table in a master/detail screen and I add this code to the AddAndEditNew_Execute method on the grid this does not work properly. Here is the code fragment:
Document document = this.Documents.AddNew();
document.Name = selectFileWindow.SafeFileName;
document.Misc = "Grootte in bytes: " + fileData.Length;
document.Data = fileData;
I get the error message: IVisualCollection<T>.AddNew() shouldn't be called from the UI Thread. I'm not really smart when it comes to threading so I don't know what this exactly means(I Googled it but didn't find any useful results). I know the custom control needs to run in a separate thread for some reason but I don't understand why the code in the tutorial was able to store the file successfully in a single, separate table through "this.DataWorkspace.ApplicationData.Documents.AddNew()"but when it's in a table linked to a master table and I use "this.Documents.AddNew()" this error occurs.
Does anyone know what I'm doing wrong? Or does anyone have an alternative method of up- and downloading binaries to and from the database?
Thanks,
H.
And how to allow the user to again download files and view them on his computer?
@Willem
Check out this application from Michael Washington:
He's modified the application from this blog, and made it better.
It might fit your needs and solve your bug issue.
Excellent work. I added a bit of code to download the bytes from the database as a file:
partial void DownloadCV_Execute()
// Must invoke on UI thread
// Gte the object which has bytes
Candiadte candidate = this.Candiadtes.SelectedItem;
byte[] data = candidate.CVFile;
SaveFileDialog dlg = new SaveFileDialog();
// set the default extension if you have also stored the file name
dlg.DefaultExt = System.IO.Path.GetExtension(candidate.CVName);
dlg.Filter = string.Format("{0} files(*.{0})|*.{0}", dlg.DefaultExt);
// Ideally you should set the file name, but can't do it in this version of Silverlight. Wait for 5.0
dlg.FilterIndex = 1;
if ((bool)dlg.ShowDialog())
Stream stream = dlg.OpenFile();
foreach (byte datum in data)
stream.WriteByte(datum);
stream.Close();
stream.Dispose();
);
Why are we using "dispatcher" here? Isn't the call to download the file running on the UI thread?
Thanks
@Bilal - When you click on a button, the "button's" code is running in the background thread and NOT the UI thread. So we need to switch back to the UI thread in this instance to get our dialog to display.
It would be really helpful to have an example of how to get the file out of the database. The solution below by Rahul Kumar does not work. Apparently, dialog boxes need to be executed via a button click? However, I have not been successful with anything that I have tried.
Hi Matt
Do you have any article regarding download saved file from lightswitch application to external drive. if Yes then please refer me.
Is it Possible to put progress bar (upload) on the select file dialog?
@Willy - Using a progress bar during import would be possible but you would need a custom silverlight control for it. If you just search on "LightSwitch custom silverlight controls" you will find lots of reference material.
@RASHMI - Check out Michael Washington's blogs: lightswitchhelpwebsite.com/.../Saving-Files-To-File-System-With-LightSwitch-Uploading-Files.aspx he talks about saving file to a hard drive (not a database).