1. Problem
You want to store resources inside of the Silverlight application container (the .xap
file) and retrieve them at runtime, as opposed to simply putting the
resources, such as images, in the file system and referencing as a URL.
2. Solution
Use the Assembly.GetManifestResourceNames and Assembly.GetManifestResourceStream methods to enumerate and retrieve resources embedded in a Silverlight application.
3. How It Works
To read embedded resources, you
have to first embed them into the application. To embed resources such
as images, video, and XML data, add the resources to the Silverlight
application project, and set the build action for each resource to
Embedded Resource. The next time you build the project, the resources
will be embedded into the application.
To obtain a list of resources available in the application, you can call Assembly.GetManifestResourceNames to obtain the names as a string array:
Assembly app = Assembly.GetExecutingAssembly();
string[] resources = app.GetManifestResourceNames();
Once you have the name of the desired resource, you can call Assembly.GetManifestResourceStream to obtain the resource as a byte array and then convert it to the appropriate type.
4. The Code
The sample application for
this recipe includes three images that have been configured to be an
embedded resource in the assembly. The UI for the recipe has a simple
gradient for the root Grid Background, a Button named RetrieveResourceNames to retrieve the resource names of embedded resources that are available, and a ListBox control named ResourceNames to display the names.
The application also has a Border control to provide a color outline for a nested Image control, which is where the embedded resources are displayed. You apply a simple 5% skew transformation to the Border.
When the application runs, the user can click the button to obtain a
list of available resources, which includes the three images. Selecting
an image name in the ListBox displays the image within the Border control. Figure 1 shows the application UI with an image selected.
The images are named Acadia1, Acadia2, and Acadia3
in the project file system. However, when they are embedded into the
application binary, the namespace is added to the filename. Keep this in
mind when loading resources if you're not first getting their names
using the GetManifestResourceNames.
Notice the first resource listed in Figure 1.
This resource is automatically generated as part of compiling and
generating the application. When the first resource is clicked, simply
ignore it since it isn't an image.
This application has a simple UI to demonstrate the functionality but here is the minor configuration: the ListBox is transparent, and the foreground color for the items is orange; the ListBox's Foreground property is configured with a SolidColorBrush to provide the orange text; the ListBox's Background property is set to Transparent, which results in the transparency; and the ListBox's ItemContainerStyle defaults to a white background, which is modified with this XAML for the style:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Background" Value="Transparent"/>
</Style>
</ListBox.ItemContainerStyle>
Styles can also be resources that are loaded using the StaticResource markup extension.
The code file has two events:
one to retrieve the list of resource names for the button click event
and another that loads the resource into the Image object. As mentioned earlier, the GetManifestResourceNames
returns a string array of resource names, so that is easy enough to do.
The somewhat more complex code is actually retrieving the resource as a
byte array and converting it to an image.
You use a stream object to obtain the array of bytes that represents the binary resource data. You create a new System.Windows.Media.Imaging.BitMapImage object and call the SetSource method, passing in the stream of bytes:
BitmapImage bImage = new BitmapImage();
bImage.SetSource(stream);
This code loads the bytes into a BitmapImage object, which is then set as the Source for the Image control named ImageDisplay that renders the image to the screen. Listings 1 and 2 have the full code listing.
Listing 1. Recipe 10's MainPage.xaml File
<UserControl x:Class="Ch02_ProgrammingModel.Recipe2_10.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot"> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.41*"/> <ColumnDefinition Width="0.59*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="0.172*"/> <RowDefinition Height="0.828*"/> </Grid.RowDefinitions> <Grid.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FF000000"/> <GradientStop Color="#FF696767" Offset="1"/> </LinearGradientBrush> </Grid.Background> <ListBox x:Name="ResourceNames" Background="Transparent" HorizontalAlignment="Stretch" Margin="4,4,15,4" Grid.Row="1" SelectionChanged="ResourceNames_SelectionChanged"> <ListBox.Foreground>
<SolidColorBrush Color="#FFD18726"/> </ListBox.Foreground> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="Background" Value="Transparent"/> </Style> </ListBox.ItemContainerStyle> </ListBox> <Button Height="26.4" HorizontalAlignment="Stretch" Margin="64,4,74,0" x:Name="RetrieveResourceNames" VerticalAlignment="Top" Content="Retrieve Resource Names" d:LayoutOverrides="VerticalAlignment, Height" Click="RetrieveResourceNames_Click"/> <TextBlock HorizontalAlignment="Stretch" Margin="53,0,74,4" VerticalAlignment="Bottom" Text="Select a Resource to Display" TextWrapping="Wrap" Foreground="#FFFFFFFF" Height="22"/> <Border Margin="29.2129993438721, −15.206000328064,32.7869987487793,35.6059989929199" HorizontalAlignment="Stretch" BorderBrush="#FF000000" x:Name="ImageBorder" RenderTransformOrigin="0.5,0.5" Visibility="Collapsed" Height="310.8" VerticalAlignment="Stretch" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" d:LayoutOverrides="Height"> <Border.Background> <SolidColorBrush Color="#FFD28826"/> </Border.Background> <Border.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform AngleX="5" AngleY="5"/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </Border.RenderTransform> <Image x:Name="ImageDisplay" Margin="5,5,5,5" Width="400" Height="300" OpacityMask="#FF000000" /> </Border> </Grid> </UserControl>
|
Listing 2. Recipe 10's MainPage.xaml.cs File
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
namespace Ch02_ProgrammingModel.Recipe2_14
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void RetrieveResourceNames_Click(object sender, RoutedEventArgs e)
{
Assembly app = Assembly.GetExecutingAssembly();
string[] resources = app.GetManifestResourceNames();
ResourceNames.Items.Clear();
foreach (string s in resources)
{
ResourceNames.Items.Add(s);
}
}