Introduction
I believe Silverlight might finally be the solution that binds Windows and Web-apps together, and from that point of view, I'm very interested in it. So, I started following sessions about Silverlight at Microsoft DevDays, reading tutorials, etcetera. I was struck by the amount of tutorials about the animation and graphic possibilities of Silverlight and the small amount of information that can be found about building line-of-business applications. Moreover, the information is much dispersed.
As I'm giving an introduction on Silverlight to a number of developers of a customer of mine, I started to write a small document outlining the basics of Silverlight. This article is based on that document.
I've also included a small sample application demonstrating a number of the topics that are discussed in this article:
Note that you shouldn't see this application as a reference application or a 'best practice': it was built with the sole purpose of demonstrating some Silverlight technologies.
To keep the download size low, I didn't include the WMV-movie file or the database records, but I've added a SQL schema file that shall allow you to rebuild the DB schema. You'll have to provide some records yourself though. Note: if you add images to the database, these should be compressed using the DeflateStream
class (you'll find an example of this in the Carservice.svc.cs file).
On a side note: this application also uses the following technologies: WCF and LINQ to SQL in a 3-tiered environment.
The application has been built using only controls that come 'out of the box' with Silverlight. If you consider using Silverlight for your projects, you'll definitely want to check out the Silverlight toolkit on CodePlex.
Background
This introduction assumes that the reader doesn't know anything about WPF or Silverlight. Note that this isn't a tutorial: I don't think you'll be able to build a Silverlight application after reading this document. Rather, you should be able to:
- Find your way in a Silverlight sample application and sample code.
- Evaluate if Silverlight is a technology you can use in your projects, or not.
This introduction is in no way complete. There are many other features of Silverlight that I haven't highlighted, but I believe most elements you should know about for developing administrative applications with Silverlight are present.
What is Silverlight?
Silverlight is a new framework from Microsoft that is strongly based on WPF (the original name of Silverlight was WPF/E). The principal aim is to build RIAs with it (Rich Internet Applications). This boils down to: browser-based applications that have the look-and-feel, performance, and response of WinForms applications. Since version 2.0, this framework also supports (a subset of) .NET code in the client. This means that .NET code is executed in the browser, allowing much more processing on the client-side without requiring roundtrips to the server (comparable to JavaScript in a webpage).
The code is compiled and placed as a XAP file on the website. The Silverlight application is hosted in an ASP.NET website by using an asp:silverlight-control
tag in the page markup.
Browser support of Silverlight
Silverlight is supported in Internet Explorer, Mozilla, and Safari on Windows, Mac, and Linux (although support on Linux is rather limited).
Check this link for a list of supported platforms and system requirements.
WPF
General
WPF is the acronym for Windows Presentation Foundation, and is a totally new way of building UIs. There's no more usage of code (WinForms) or HTML (ASP.NET), but it uses XML: a WPF UI is called a XAML file (pronounced as zamel). Silverlight isn't really the same as WPF in the sense that Silverlight is just a subset of the WPF-possibilities. Nevertheless, you'll find most of the core-technology of WPF in Silverlight, and the differences are getting smaller in each consecutive Silverlight version (the expectation is that given enough time, there won't be any significant difference between Silverlight and WPF).
XAML is just a way of building a user-interface declaratively. Take for example the following piece of C# code:
TextBlock tb = new TextBlock() { Foreground = new SolidColorBrush(Colors.Black),
Margin = new Thickness(3), Text = "Name:" };
It is equivalent to the following markup in XAML:
<TextBlock Foreground="Black" Margin="3">Name:</TextBlock>
Note that the element name corresponds to the control name, the attributes correspond to the properties. You can also replace the attributes with child XML-elements. The example below is equivalent to the previous:
<TextBlock Margin="3">
<TextBlock.Foreground>
Black
</TextBlock.Foreground>
Name:
</TextBlock>
If you want to reference the element in code (for example, to set the Text
property of the TextBlock
in code), you must add a Name
attribute. The value of that attribute will become the name of the variable in code.
Events on controls are declared in the same way as the properties are declared: as attributes. An example of a Button
named 'myButton
' with a Clicked
event:
<Button Name="myButton" Click="Button_Click_3"/>
Finally, some properties of the parent can be (must be) declared on a child control. These properties are called 'Attached properties'.
For example:
<Grid>
<TextBlock Grid.Row="0" Grid.Column="1"
Foreground="Black" Margin="3">Name:</TextBlock>
</Grid>
(Although the Row
and Column
properties are really properties of the Grid
layout-control, they're declared on the child TextBlock
control.)
Namespaces
Similar to the using
/Imports
statements that are used to reference namespaces in libraries in C#/VB.NET, such references must also be declared in XAML.
These namespace references are declared in the root element of the XAML file, and have the following format: xmlns:name_of_the_namespace="reference_to_the_namespace"
. By default, a Silverlight UserControl has already two namespace references:
<UserControl x:Class="Test.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
...
</UserControl>
Suppose we want to add a namespace 'data
' that references the System.Controls.Data assembly, then the declaration would look like this: xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
. Further down in the XAML file, the elements from that namespace will be preceded by 'data':
<data:DataGrid x:Name="datagrid Margin="10">
</data:DataGrid>
Layout controls
To layout different controls in the user-interface, a number of layout controls can be used in Silverlight:
Grid |
This layout control corresponds to the TableLayoutPanel from WinForms, and allows to layout controls using Column /Row attributes. |
StackPanel |
The controls are placed below (or next to) each other. |
DockPanel |
The controls are docked. The possibilities are: left, right, top, bottom, and fill. This control is available in WPF but not (yet) in Silverlight. |
Canvas |
This layout control allows to position controls using X and Y coordinates. |
(Most of the time, you'll use layout controls instead of positioning your controls based on the top and left edges of the form, just to keep the layout dynamic.)
The Grid
is without doubt the most powerful layout control when it's about positioning a great amount of controls. The StackPanel
is especially handy when positioning a few controls.
Content controls
Some WPF controls have a Content
property and can contain other controls as 'content'.
An example of this is the Button
. Most of the time, it'll contain just a TextBlock
as a content element:
<Button>
<TextBlock>Click Me</TextBlock>
</Button>
Of course, that one content-element can in turn contain more child elements. For example, a Button
that has a StackPanel
with multiple controls as content:
<Button>
<StackPanel Orientation="Horizontal">
<Image Source="Img/Account16.png"/>
<TextBlock>Click Me</TextBlock>
</StackPanel>
</Button>
ItemsControl
Except the content controls that can only contain one element, there are also controls that can contain multiple elements.
Two examples of these are the ComboBox
and the ListBox
controls:
<ListBox>
<ListBox.Items>
<ListBoxItem><TextBlock>Item 1</TextBlock></ListBoxItem>
<ListBoxItem><TextBlock>Item 2</TextBlock></ListBoxItem>
</ListBox.Items>
</ListBox>
DataGrid
A (big) difference between Silverlight and WPF where Silverlight is in the advantage, is that Silverlight has a DataGrid
out-of-the-box (with WPF, you must use the DataGrid
from the WPF Toolkit on Codeplex).
Remark: the DataGrid
in Silverlight is located in the assembly System.Windows.Controls in the namespace System.Windows.Controls.Data
. It's possible that you'll have to add a reference to this assembly in your project, and you'll also have to add a namespace reference in your XAML file: xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
.
Basically, the DataGrid
is quite simple in usage: you let the DataGrid
to generate the columns automatically (using the attribute AutoGenerateColumns
), or you'll define the columns manually:
<data:DataGrid Name="datagrid" Margin="10" AutoGenerateColumns="False">
<data:DataGrid.Columns>
<data:DataGridTextColumn Header="ID" Binding="{Binding Path=ID}" IsReadOnly="True"/>
<data:DataGridTextColumn Header="Last name" Binding="{Binding Path=Name}"
IsReadOnly="False"/>
<data:DataGridTextColumn Header="First name" Binding="{Binding Path=Firstname}"
IsReadOnly="False"/>
</data:DataGrid.Columns>
</data:DataGrid>
There are three column types: a DataGridTextColumn
, a DataGridCheckBoxColumn
, and a DataGridTemplateColumn
. The last one is very powerful because it allows to declare any control for the edit- and view-mode of the column. This allows you to define a DataGridTemplateColumn
with a ComboBox
or a Button
(for example).
Resources
WPF and Silverlight allow you to store elements (and variables) in the Resources of the application. This way, you only have to define the elements once and you can reuse them anywhere in your application.
For example: an Ellipse
element where the Brush
is defined in the Ellipse
element itself:
<Ellipse Height="84" Width="92" Stroke="#FF000000">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.75,0.25">
<GradientStop Color="#FFF0F2F5"/>
<GradientStop Color="#FF1D6DE2" Offset="1"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
There's nothing wrong with this piece of markup, but if you're using the same Brush
in other controls, you'll have a maintenance problem if you want to change the colors afterwards. It'll also clutter up your markup unnecessarily.
In that case, it's advised to create Resources and define your Brush
in the Resources. Each resource must have a unique identifier, which is the x:Key
attribute. Later, you'll be able to reference these resources using this identifier and the StaticResource
operator.
<UserControl.Resources>
<RadialGradientBrush x:Key="mybrush" GradientOrigin="0.75,0.25">
<GradientStop Color="#FFF0F2F5"/>
<GradientStop Color="#FF1D6DE2" Offset="1"/>
</RadialGradientBrush>
</UserControl.Resources>
<Ellipse Height="84" Width="92"
Stroke="#FF000000" Fill="{StaticResource mybrush}"/>
Resources can be defined in several places. If the resources are only used in one XAML-file, they'd best be defined at that level.
If the resources are used throughout the application, they can be defined in the App.xaml file (in Application.Resources
).
There's also a second XAML Resources file that is used automatically by Silverlight: Themes/generic.xaml. This file can be used for resources of controls that you've created yourself.
Note: in WPF, there's also an element MergedDictionaries
that allows to define and use multiple Resource files in your application. This element is only available from Silverlight version 3 on.
Styles
A very powerful aspect of WPF is the possibility to define styles for the controls. A style is comparable to the CSS-technology in ASP.NET, and is a place where a number of properties for a control are defined and then reused in the application.
A very simple example: suppose you want to define a Margin
of 3 for your Button
s. Of course, you could do this on a per-button basis:
<Button Name="button1" Margin="3"><TextBlock>Button 1<>TextBlock>
But, you might also define a Style
that will be applied to controls of the Button
type, and that defines that the Margin
is 3:
<Style x:Key="marginButtonStyle" TargetType="Button">
<Setter Property="Margin" Value="3"/>
</Style>
Hereafter, the Style
is applied to the Button
(s):
<Button Name="button1" Style="{StaticResource marginButtonStyle}">
<TextBlock>Button 1</TextBlock></Button>
<Button Name="button2" Style="{StaticResource marginButtonStyle}">
<TextBlock>Button 2</TextBlock></Button>
(Styles can be defined inline the control, but will mostly be defined in the Resources.)
In WPF, you can also define a Style
that must be applied to all controls of a given type. In that case, you'll leave the x:Key
attribute out and you won't have to reference the Style
in the control:
<Style TargetType="Button">
<Setter Property="Margin" Value="3"/>
</Style>
(This technique isn't available in Silverlight though, and you always have to reference the Style
explicitly.)
Databinding
Databinding is the technique to bind data to properties of controls. In WPF/Silverlight, this is more elaborate than what was possible in WinForms and ASP.NET.
First of all, you must assign a data source to the controls. One way is to use the DataContext
property. This can be assigned on the control, but might just as well be done on a parent control, if the data source is the same for all controls of that parent (e.g., a layoutcontainer).
For example, suppose the declaration of a User
class:
public class User
{
public string Name { get; set; }
public string Firstname { get; set; }
}
In the code-behind of our XAML page, we have a property User
wherein we store an initialized User
instance. Finally, we assign this User
to the DataContext
property of the UserControl (that is the root-element, thus the parent of all containing controls):
public partial class Page : UserControl
{
public User User { get; set; }
public Page()
{
InitializeComponent();
User = new User() { Name = "Spileers", Firstname = "Xavier" };
this.DataContext = this.User;
}
}
In the markup itself, we bind the properties of the controls to the properties of the User
object. This happens through the Binding
operator:
<TextBlock Margin="3" Text="{Binding Name}"/>
<TextBlock Margin="3" Text="{Binding Firstname}"/>
Note that you can not only bind the Text
properties of a control, but almost any property. Moreover, databinding can also be applied to properties of properties (e.g., User.City.Name
) which wasn't possible previously in WinForms.
There's also a Mode
operator that indicates how the binding reacts. By default, the mode is OneWay
which means that modifications to the data object are propagated to the control, but modifications that are applied in the control aren't applied back to the data object.
With the TwoWay
mode, the modifications that are applied to the data object will be propagated to the control and vice-versa.
In Silverlight, the binding mode seems to by OneWay
by default. In typical input forms, you'll have to change it to TwoWay
. An example:
<TextBox Margin="3" Text="{Binding Path=Name, Mode=TwoWay}"/>
In WPF, you can also data bind properties of controls to each other. See the example below where the value of a Slider
is used to set the width of an Ellipse
element:
<Slider Name="widthSlider" Minimum="50" Maximum="100" Value="75"/>
<Ellipse Width="{Binding ElementName=widthSlider, Path=Value}" Height="50"
Fill="{StaticResource mybrush}"/>
This isn't possible in Silverlight 2 though, but it is from version 3 on.
Data templates
Using a data template, you can define how objects are displayed in controls.
For example: suppose a listbox that displays a list of User
objects (the ItemsSource
is assigned in code):
<ListBox Name="usersListBox">
</ListBox>
The disadvantage is that the system uses the ToString()
method of the User
instance to decide which text will appear in the listbox.
Using the ItemTemplate
property, you can define a DataTemplate
for the ListItem
s so that the users in the list are displayed in a more attractive way:
<ListBox Name="usersListBox" Height="50">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Of course, DataTemplate
s can also be defined in the Resources, so that they can easily be reused in multiple controls.
Animations
WPF and Silverlight allow you to define animations. Basically, an animation is just a transition from one state to another, by modifying one or more properties of controls.
This modification can happen in two ways: gradually in time (fluent), or by changing from one state to another. Although the first way is much subtler than the second, it isn't possible for any type of property. The most important types that are supported are: Double
, Color
, and Point
.
Take for example the following buttons:
<Button Name="button1><TextBlock>Hide button 2</TextBlock></Button>
<Button Name="button2"><TextBlock>Button 2</TextBlock></Button>
Suppose that clicking on the first button must have as result that the second button briefly disappears. This is possible by building an animation that sets the Opacity
property of the second button to zero. An animation is based on a Storyboard
that can group multiple animations. This Storyboard
is named, which can then be used in code to start the animation(s).
This results in the following XAML:
<Button Name="button1" Click="Button_Click">
<Button.Resources>
<Storyboard x:Name="buttonanimation">
<DoubleAnimation Storyboard.TargetName="button2"
Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:0.25" AutoReverse="True"/>
</Storyboard>
</Button.Resources>
<TextBlock>Hide button 2</TextBlock>
</Button>
(The Storyboard
is stored here as a Resource in the Button
. Of course, it might just as well be defined on UserControl level.) The animation above will modify a property of type Double
(Opacity
) and takes a total time of 25/100 second (Duration
) to go to a value of 0.0 (To
). Finally, the animation resets the property to the initial value before the animation started (AutoReverse=True
).
The event-handler in code is:
private void Button_Click(object sender, RoutedEventArgs e)
{
buttonanimation.Begin();
}
Notice that this animation is very fluent: you can see the button gradually disappear.
You can also accomplish the disappearance by passing from one state to another: these are animations using key frames. Key frames define different states at different points in time:
<Button Name="button1" Click="Button_Click">
<Button.Resources>
<Storyboard x:Name="buttonanimation">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="button2"
Storyboard.TargetProperty="Opacity" FillBehavior="Stop">
<DiscreteDoubleKeyFrame Value="0.5" KeyTime="0:0:0.10"/>
<DiscreteDoubleKeyFrame Value="0" KeyTime="0:0:0.25"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Button.Resources>
<TextBlock>Hide button 2</TextBlock>
</Button>
This animation defines that at 10/100 second, the Opacity
property has the value 0.5 and that at 25/100 second, the Opacity
has the value 0. FillBehavior=Stop
indicates that once the animation is executed, the values should be reset to their initial values.
When executing the animation, you'll notice it's a lot less fluent than the previous one. That is because the transition from one state to another is rather abrupt. That's why animations with key frames will mostly only be used for properties that can't be animated otherwise.
Note: Expression Blend is a must for defining animations.
Control tsemplates
Control templates allow you to define the appearance of controls. This is very complete in the sense that you can design you control from scratch if you want to.
The following piece of XAML represents the control template of a Button
:
<ControlTemplate x:Key="mybuttontemplate" TargetType="Button">
<Grid>
<Border Name="outerborder" BorderThickness="1" BorderBrush="Blue"
Background="{StaticResource mybrush}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</Grid>
</ControlTemplate>
(The ContentPresenter
is the placeholder where the content will be placed.)
When creating the control, its control template can be assigned using the Template
property:
<Button Margin="3" Template="{StaticResource mybuttontemplate}">
<TextBlock>Test</TextBlock>
</Button>
Of course, there's also a mechanism to let the control react when the mouse hovers over it or when it's clicked (for example).
All these interactions are managed in Silverlight by the Visual State Manager (shortened to VSM). In WPF, this is governed by triggers, but it's expected that the VSM-concept will also be added to WPF.
The VSM is located in the System.Windows
namespace: xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
.
The VSM can describe different states of the control. Animations are used for the transition from one state to another:
<ControlTemplate x:Key="mybuttontemplate" TargetType="Button">
<Grid>
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualState x:Name="Normal"/>
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="outerborder"
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
To="GoldenRod" Duration="0:0:0.5" />
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="outerborder"
Storyboard.TargetProperty="(Border.Background).(
GradientBrush.GradientStops)[0].(GradientStop.Color)">
<DiscreteColorKeyFrame Value="Yellow" KeyTime="0:0:0"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="outerborder"
Storyboard.TargetProperty="(Border.Background).(
GradientBrush.GradientStops)[1].(GradientStop.Color)">
<DiscreteColorKeyFrame Value="GoldenRod" KeyTime="0:0:0"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Border Name="outerborder" BorderThickness="1" BorderBrush="Blue"
Background="{StaticResource mybrush}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</Grid>
</ControlTemplate>
There are two states defined in this example: MouseOver
and Pressed
. In the first state, the button will change color; in the second state, the backcolor of the button is modified.
Navigation between pages
Navigation between pages isn't that easy in Silverlight 2. A Silverlight application has a root-visual element, and all other elements are children of this root element. By default, the page that the solution starts with is the root-visual element (see the App.xaml.cs file).
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new Page();
}
The root-visual can't be replaced by another User Control once the application is started. But you can simulate navigation by providing a custom root-visual element and setting the content of that custom element on the fly. For example, start by building an inherited UserControl where a UIElement
can be passed through and be set as Content
:
public partial class NavFrame : UserControl
{
public NavFrame()
{
InitializeComponent();
}
public void Navigate(UserControl content)
{
this.Content = content;
}
}
Set this User Control as root-visual element of your application (in App.xaml.cs) and provide methods to navigate to other User Controls:
private Page _page1;
private NavFrame _frame;
private void Application_Startup(object sender, StartupEventArgs e)
{
_frame = new NavFrame();
this.RootVisual = _frame;
NavigatePage1();
}
public void NavigatePage1()
{
if (_page1 == null) _page1 = new Page();
_frame.Navigate(_page1);
}
In code, you can call these methods to navigate to other pages.
private void Button_Click(object sender, RoutedEventArgs e)
{
(Application.Current as App).NavigatePage1();
}
Note: Silverlight 3 has a navigation-framework that simplifies all this and also provides support for the Back and Forward buttons in the browser.
Integrating media
Silverlight provides a MediaElement
control for playing media.
<MediaElement Name="mediaPlayer" />
Silverlight 2 supports the following formats: WMV, WMA, MP3, WMVA,WMVC1, and ASX. Of course, it's not interesting to package the media files in the Silverlight project itself (or they'll be downloaded integrally before starting the application); they should be streamed from the website when needed. It's interesting to note that the Silverlight application only knows the path to the location of the XAP-file (default: Clientbin): all paths are relative to this location. A good practice is to make subfolders in this directory and place the media in there:
This file can be reached by the following code:
Uri uri = new Uri(Application.Current.Host.Source, "Media/Fiesta_car.wmv");
mediaPlayer.Source = uri;
(The code above is in the event handler of a Play-button that will start the playback of the media)
You can place the media anywhere else in the website, but the path should then be relative to the ClientBin folder. If the media file is placed in the root of the website, the code would be:
Uri uri = new Uri(Application.Current.Host.Source, "../Fiesta_car.wmv");
mediaPlayer.Source = uri;
Communication between client and server
There are only limited technologies that are supported by Silverlight for the communication between client and server: SOAP 1.1 Web Services and REST services are about the only mainstream possibilities (there are a couple of others, but they're not that easy to implement).
The easiest way to provide services for your Silverlight application is to build them using WCF. Taking into account the restriction in the previous paragraph; this means we must define a service with a bindingtype of basicHttpBinding
(SOAP 1.1) or webHttpBinding
(REST).
In code, the service can be referenced just as you would in a 'regular' WinForms or ASP.NET application: Visual Studio will generate a proxy for you. You'll notice that all methods can only be invoked asynchronously: this means events will have to be used to receive the return state of the call to the server.
By default, Silverlight can only invoke services that are placed in the same domain and have the same port number (e.g., www.tri-s.be:80) as the website the application originated from. To consume a service that is placed in another domain (or has a different port number), a policy file must be provided and placed in the root of the web application on the server.
The policy file must be called clientaccesspolicy.xml and has a similar content:
="1.0" ="utf-8"
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
The sample policy file content above allows all traffic to any domain and to any port.
Note: Silverlight has no support for DataSet
/DataTable
/DataRow
objects! Thus it makes no sense to build services that return any of those objects.
[Update: there are alternatives available that emulate the DataSet
/DataTable
/DataRow
functionality (Silverlight Dataset, for example). Thanks to Dewey for pointing this out.]
Storing data locally
Silverlight allows an application to store data locally on the PC of the user. This data can only be placed in IsolatedStorage, which is a location where the user isn't able to retrieve it. You can compare storing data in IsolatedStorage with cookies from a website.
An example of code that stores data in IsolatedStorage:
using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = file.OpenFile("app.data",
System.IO.FileMode.Create))
{
using (StreamWriter writer = new StreamWriter(stream))
{
}
}
}
To retrieve the data from IsolatedStorage, the following code can be used:
using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
{
if (file.FileExists("app.data"))
{
using (IsolatedStorageFileStream stream = file.OpenFile("app.data",
System.IO.FileMode.Open))
{
using (StreamReader reader = new StreamReader(stream))
{
}
}
}
}
If you want to read files from the client machine where the application is executed, there's an OpenFileDialog
you can use (the file can be read, but with approval of the user). There isn't an equivalent SaveFileDialog, though.
[Update: from Silverlight 3 on, you can use the SaveFileDialog
in Silverlight applications to store files locally. Thanks to Jemery Likness and Dewey for pointing this out.]
Printing in Silverlight
Sending output to a printer on the client isn't supported in Silverlight. Your only alternative is to navigate to an ASP.NET page that contains the data to be printed. Bummer!
[Update: you can export the data in a file (a spreadsheet, for example) and let the user save this to the client PC using a SaveFileDialog
. You can also export the complete visual element to a graphical file if you wish (see the comments section below).]
History
- 2009-08-15: Submitted to CodeProject.
- 2009-08-31: Added a few updates.