Advanced Silverlight bitmaps
Written by Ian Elliot   
Monday, 30 August 2010
Article Index
Advanced Silverlight bitmaps
Rendering to bitmaps

Bitmap handling in Silverlight, using WriteableBitmap in particular, differs greatly from WPF. In this article we look as the problems of loading bitmaps and generating them dynamically.

Banner

 

Bitmap handling - using WriteableBitmap in particular differs greatly from WPF. In this article we look as the problems of loading bitmaps and generating them dynamically.

Bitmaps from streams

Although a bitmap source seems only to be creatable from other bitmaps or UIElements you can in fact create a bitmap from any suitably formatted stream using the SetSource method.

The only real problem here is that the stream of bytes that you provided have to take the form of a valid jpeg, png or gif and this makes it difficult to convert a raw bit stream into a finished bitmap.

As a simple example, let’s use the OpenFileDialog to read in an image stored on the local machine. This has to be called from a button handler or some user initiated code otherwise you generate a security error. So add a button to and an image control to a new Silverlight project and:

using System.Windows.Media.Imaging;
using System.IO;

To create a stream we first use the OpenFileDialog:

private void button1_Click(
 object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "JPEG Files (*.jpg;*.jpeg)|
*.jpg;*.jpeg | All Files (*.*)|*.*";
ofd.FilterIndex = 1;
bool?  result=ofd.ShowDialog();

Notice that we can’t use DialogResult as we would in WPF because Silverlight does things differently.

The result of the dialog box is either true or false according whether the user clicked OK or CANCEL however this is returned as a nullable Boolean which can be either true, false or null. As in Silverlight only dialog boxes always return with true or false we can simply cast to a simple Boolean or work with it as a nullable type.

If the user clicked the OK button we can open a stream to the file:

if (result==true)
{
Stream stream = ofd.File.OpenRead();

Now we create a BitmapImage and set its source to the stream. When this is assigned to the image control the selected image file will be displayed:

 BitmapImage bi = new BitmapImage();
bi.SetSource(stream);
image1.Source = bi;
stream.Close();
}

If you want to be able to access and change the pixels of the image you have read in then you can simply change the BitmapImage to a WriteableBitmap with an arbitrary size:

WriteableBitmap bi = 
new WriteableBitmap(1,1);

The size of will be adjusted after reading in the file.

Alternatively you can construct a WriteableBitmap from the BitmapImage (which inherits from BitmapSource):

WriteableBitmap wbm = 
new WriteableBitmap(bi);

Finally to display the picture you have just loaded place an Image control on the page and add:

image1.Source = bi;

Loading from a Resource

If you know what image file you want to load then rather than getting the user to pick it for you using a File Open dialog box it is much more sensible to include the appropriate file as a resource. There are many ways to do this but including it in the ClientBin directory is one of the simplest and works well in most cases. 

If you want to see this in action copy a suitable bitmap into the ClientBin directory within the Silverlight web site created for you by Web Developer or Visual Studio – make sure it’s a jpeg or a png file. You can use the Add Existing Item menu option which automatically copies any files  into the ClientBin and adds the file to the probject.

To load the image you would use:

BitmapImage bit1 = new BitmapImage(
new Uri("/test.jpg",UriKind.Relative));
image1.Source = bit1;

assuming the file was called test.jpg.

There is one big difficulty in working with images and this is the way Silverlight handles any errors or exceptions in "silent" mode.  For example, if you get the file name wrong then everything will appear to work but you won't see any images appear in the Image control. If this happens to you - check the file name and path.

Another problem is that the picture will appear in the Image control without any special provisions being made and it looks as if the file is being loaded in a synchronous fashion - it isn't and this can cause problems if you don't understand what is happening.

For example if you follow the creation of the BitmapImage immediately by:

WriteableBitmap wbm = 
new WriteableBitmap(bit1);

in an attempt to create a WriteableBitmap based on the BitmapImage, then you will most likely see an "object not set "error message because of the premature use of bit1.

The difference is that creating an image from a URI source is an asynchronous download but loading a local file is a blocking synchronous call.

To solve the problem you have to wait for the ImageOpened event before using the bitmap.

However even this isn’t straightforward as by default the bitmap isn’t loaded until it's needed. So if you try and load a bitmap and put the code that uses it into the ImageOpened event handler the event handler will never be called because the bitmap is never used!

And it's no good trying to cause the bitmap to be loaded by setting it as the source of an invisible image control or by asking for its properties. Silverlight seems to be insistent that if you don’t display the bitmap then it isn’t loaded. The solution to the problem is to use the eventhandler but you also have to set:

bit1.CreateOptions = 
BitmapCreateOptions.None;

which forces the bitmap to be downloaded even if it isn’t being displayed.

Banner

<ASIN:0470534044>

<ASIN:1430224258 >

<ASIN:0672330628 >

<ASIN:0470524650 >

<ASIN:1430229888 >



Last Updated ( Monday, 30 August 2010 )