Friday, July 31, 2009

Saving Bitmaps to Isolated Storage in Silverlight 3

There may be times when you wish to save a bitmap image to the user's local storage. Perhaps it was a generated image that is used in the application, or maybe it is an externally referenced image that you are loading locally for caching purposes. I found many examples online of generating a "save dialog" box, but none for saving it.

This is a very bare bones application that demonstrates how to save the image. This is in no industry-standard format - it literally writes out the size of the image in pixels (height, width), then streams out the bytes for alpha, red, green, and blue. It is meant as a foundation to better understand how to get the image data and manipulate it. Once you "know" the pixels, then you can easily start to apply more advanced algorithms like BMP, PNG, JPG, or even GIF to save and retrieve the data.

Here is the XAML - you can create a new Silverlight Application and literally plop in the XAML and code-behind to get started:

<UserControl x:Class="BitmapSaver.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:DesignWidth="640" d:DesignHeight="480">
  <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Canvas HorizontalAlignment="Left" x:Name="CanvasSource" Height="20" Width="100" Grid.Row="0">
                <TextBlock FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" Width="Auto" Height="Auto" Foreground="Red" FontWeight="Bold" Text="BitMapExample"/>                            
        </Canvas>
        <Image HorizontalAlignment="Left" x:Name="BitmapImageRef" Stretch="None" Grid.Row="1"/>
        <TextBlock x:Name="Status" Text="Processing..." Grid.Row="2"/>        
  </Grid>
</UserControl>

In the XAML, there is a grid with three rows. The first is a canvas with red text inside it. This is what we'll turn into a bitmap image and save. The second row is an image "placeholder" for the image that we process. The final row is a simple text block to update the status.

Here is the code-behind:

public partial class MainPage
{
    private const string SAVEDIMG = "SavedBitmapImage.xyz";
        
    /// <summary>
    ///     Default constructor
    /// </summary>
    public MainPage()
    {
        InitializeComponent();
        CanvasSource.Loaded += _MainPageLoaded;
    }

    /// <summary>
    ///     Canvas loaded
    /// </summary>
    /// <param name="sender">
    /// <param name="e">
    void _MainPageLoaded(object sender, RoutedEventArgs e)
    {
        Status.Text = "Checking disk...";
        byte[] buffer = _LoadIfExists(SAVEDIMG); 

        if (buffer.Length > 0)
        {
            Status.Text = "Loading...";
            BitmapImageRef.Source = _GetImage(buffer);
            Status.Text = "Loaded.";
        }
        else
        {
            Status.Text = "Rendering...";
            WriteableBitmap bitmap = new WriteableBitmap(CanvasSource, new TransformGroup());
            BitmapImageRef.Source = bitmap;
            Status.Text = "Saving...";
            _SaveToDisk(_GetSaveBuffer(bitmap), SAVEDIMG);
            Status.Text = "Saved.";
        }                       
    }

    /// <summary>
    ///     Load file, if it exists
    /// </summary>
    /// <param name="fileName">The filename</param>
    /// <returns>The byte array for the file</returns>
    private static byte[] _LoadIfExists(string fileName)
    {
        byte[] retVal;

        using (IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication())
        {
            if (iso.FileExists(fileName))
            {
                using (IsolatedStorageFileStream stream = iso.OpenFile(fileName, FileMode.Open))
                {
                    retVal = new byte[stream.Length];
                    stream.Read(retVal, 0, retVal.Length);
                }
            }
            else
            {
                retVal = new byte[0];
            }
        }
        return retVal;
    }

    /// <summary>
    ///     Saves to isolated storage
    /// </summary>
    /// <param name="buffer">The buffer</param>
    /// <param name="fileName"></param>
    private static void _SaveToDisk(byte[] buffer, string fileName)
    {
        using (IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication())
        {
            using (
                IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.CreateNew,
                                                                                 iso))
            {
                stream.Write(buffer, 0, buffer.Length);
            }
        }
    }

    /// <summary>
    ///     Gets an image from storage
    /// </summary>
    /// <param name="buffer"></param>
    /// <returns>The bitmap</returns>
    private static WriteableBitmap _GetImage(byte[] buffer)
    {
        int width = buffer[0]*256 + buffer[1];
        int height = buffer[2]*256 + buffer[3];

        long matrixSize = width*height;

        WriteableBitmap retVal = new WriteableBitmap(width, height);

        int bufferPos = 4; 

        for (int matrixPos = 0; matrixPos < matrixSize; matrixPos++)
        {
            int pixel = buffer[bufferPos++];
            pixel = pixel << 8 | buffer[bufferPos++];
            pixel = pixel << 8 | buffer[bufferPos++];
            pixel = pixel << 8 | buffer[bufferPos++];
            retVal.Pixels[matrixPos] = pixel;
        }

        return retVal; 
    }

    /// <summary>
    ///     Gets the buffer to save to disk from the writeable bitmap
    /// </summary>
    /// <param name="bitmap">The bitmap image</param>
    /// <returns>The buffer of bytes</returns>
    private static byte[] _GetSaveBuffer(WriteableBitmap bitmap)
    {
        long matrixSize = bitmap.PixelWidth*bitmap.PixelHeight;
        
        long byteSize = matrixSize*4 + 4; 

        byte[] retVal = new byte[byteSize];

        long bufferPos = 0;

        retVal[bufferPos++] = (byte) ((bitmap.PixelWidth / 256) & 0xff);
        retVal[bufferPos++] = (byte) ((bitmap.PixelWidth % 256) & 0xff);
        retVal[bufferPos++] = (byte) ((bitmap.PixelHeight / 256) & 0xff);
        retVal[bufferPos++] = (byte) ((bitmap.PixelHeight % 256) & 0xff);

        for (int matrixPos = 0; matrixPos < matrixSize; matrixPos++)
        {
            retVal[bufferPos++] = (byte)((bitmap.Pixels[matrixPos] >> 24) & 0xff);
            retVal[bufferPos++] = (byte)((bitmap.Pixels[matrixPos] >> 16) & 0xff);
            retVal[bufferPos++] = (byte)((bitmap.Pixels[matrixPos] >> 8) & 0xff);
            retVal[bufferPos++] = (byte)((bitmap.Pixels[matrixPos]) & 0xff);    
        }

        return retVal;
    }
}

The main process simply waits for the canvas to be loaded.

We check if the image is saved in isolated storage. If it exists, we render it and attach it to the image. If not, we render it based on the cavas in the first row and then save it.

_LoadIfExists is a basic routine that checks for a file's existence and if it is there, loads it into a byte buffer and returns it. If the file is not there, it returns an empty byte array.

_SaveToDisk takes a byte array and persists it to isolated storage. Possible enhancements to this routine include checking to see if the available storage exists and prompting the user to extend it if needed, as well as organizing files into subdirectories and checking for/creating those as well.

_GetSaveBuffer takes the bitmap and returns a buffer of bytes. The first two bytes are the width, the second two the height, and finally the remaining bytes contain the alpha, red, green, and blue channels for each pixel in the bitmap. Obviously this is where you can conform to any one of the graphics standards available and supply your own compression.

_GetImage takes the buffer for an image saved in the previous format, and renders it to the bitmap so it can be displayed.

The idea behind this example is to run in debug and step through to see. The first time you run it, it will simply copy the image (the text in this case) and display it as well as save it to isolated storage. Any subsequent run will load it from storage and show it unless you clear storage or change the file name.

Again, this is just a basic introduction to manipulating and saving bitmaps in isolated storage.

Jeremy Likness

Thursday, July 30, 2009

Preloading and Caching Images in Silverlight

One thing I found with our image-intensive Silverlight application was that the screens sometimes painted a little slowly due to our decision to store images externally. Instead of having a bloated XAP file, we decided to map a path to the server with the images. Because the application is enterprise and therefore behind SSL, this can sometimes result in a noticeable load time for the images.

After doing some research, I found that Silverlight actually uses the browser's own image caching mechanisms. In other words, when you point to an external image on the web, the browser will cache it just like an ordinary image stored in a web page. The first time, it will pull down, but after that it should be fast.

Knowing this, it was simple to make a little image helper to help me preload images so they are ready before the user is. The class looks like this:

public static class ImageHelper
{
   public static void PreLoad(string imagePath)
   {
      const string JS = "var img = new Image(); img.src='{0}';";
      HtmlPage.Window.Eval(string.Format(JS, AppUriHelper.GetRelativeUri(imagePath)));
   }
}

That's it ... in my App.cs I can now preload images easily, by doing this:

...
ImageHelper.PreLoad("Images/mycachedimage.jpg");
...

The application hooks into the JavaScript of the DOM for the browser page, then creates an image object that triggers a load into your browser of the image, even if you haven't shown it within the Silverlight application yet.

Oh, what is AppUriHelper? It's just a simple utility to help me build uris that follow the application wherever it is installed:

public static class AppUriHelper
{        
   private static readonly Uri _baseUri;
   private static readonly string _uriRoot;

   static AppUriHelper()
   {
      _uriRoot = System.Windows.Browser.HtmlPage.Document.DocumentUri.AbsoluteUri;
      int lastSlash = _uriRoot.LastIndexOf("/");

      // we do this twice to get the "second to last" slash - leave this out if
      // your app is hosted in the root, this assumes /subdir/mypage.aspx
      _uriRoot = _uriRoot.Substring(0, lastSlash);
      lastSlash = _uriRoot.LastIndexOf("/");
      _uriRoot = _uriRoot.Substring(0, lastSlash + 1);

      _baseUri = new Uri(_uriRoot, UriKind.Absolute);
   }

   public static Uri GetBaseUri()
   {
      return _baseUri; 
   }

   public static Uri GetRelativeUri(string relativePath)
   {
      string newUri = string.Format("{0}{1}", _uriRoot, relativePath);
      return new Uri(newUri, UriKind.Absolute);
   }
}

Of course, GetRelativeUri is probably not named well because it returns an absolute Uri ... it really is "Get Absolute Uri from Relative Path".

Jeremy Likness

Tuesday, July 28, 2009

Silverlight DataContext Changed Event

One known issue with Silverlight is that the DataContext bound to a control may change, but there is no readily available change event. Unlike WPF, you don't have an explicit event to register with in order to track changes. This becomes a problem in controls like the DataGrid control which reuses the same control instances for each page. Even though fresh data is bound, if your control isn't aware that the data context changed, it will keep stale content.

If you search online you'll find the solution is simple: you create a dependency property that is actually based on the data context (call it a "dummy" property) and then register for changes to that property. I was glad to find the solution but wanted something a little more reusable (remember, I like the DRY principle: don't repeat yourself, so when I find myself writing the same line of code more than once I have to go back and refactor).

The solution? I was able to find something that I think works well and involves an interface and a static class.

First, I want to identify when a control should be aware of changes to DataContext and also provide a method to call when this happens. That was easy enough. I created IDataContextChangedHandler and defined it like this:

public interface IDataContextChangedHandler<T> where T: FrameworkElement 
{
   void DataContextChanged(T sender, DependencyPropertyChangedEventArgs e);
}

As you can see, it is a simple interface. A method is called with the sender (which will presumably be the control itself) and the arguments for a dependency property changed event. It is typed to T, of course.

Next, I used generics to create a base class that manages the "fake" dependency property:

public static class DataContextChangedHelper<T> where T: FrameworkElement, IDataContextChangedHandler<T>
{
    private const string INTERNAL_CONTEXT = "InternalDataContext"; 

    public static readonly DependencyProperty InternalDataContextProperty =
        DependencyProperty.Register(INTERNAL_CONTEXT,
                                    typeof(Object),
                                    typeof(T),
                                    new PropertyMetadata(_DataContextChanged));

    private static void _DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        T control = (T)sender;
        control.DataContextChanged(control, e);
    }

    public static void Bind(T control)
    {
        control.SetBinding(InternalDataContextProperty, new Binding());
    }
}

As you can see, the class does a few things and works for any framework element, which is a "basic building block" that supports binding. It is typed to the FrameworkElement but also requires that the target implements IDataContextChangedHandler. It creates a dependency property. Because the data context can be any object, the type of the dependency is object, but the type of the parent is the framework element itself ("T"). When something happens to the property, it will invoke _DataContextChanged.

The event handler is sent the control that raised the event as well as the arguments for the old and new properties in the data context. We simply cast the sender back to its original type of T. Then, because we know it implements IDataContextChangedHandler, we can simply call DataContextChanged.

Finally, there is a static call to bind the control itself.

Now let's put the pieces together. Let's say you have a control that makes a gauge based on a data value, and you want to put the control in the grid. You need to know when the DataContext changes, because you will update your gauge. The control will look like this:

public partial class Gauge : IDataContextChangedHandler<Gauge> 
{
   public Gauge() 
   {
      InitializeComponent();
      DataContextChangedHelper<Gauge>.Bind(this); 
   }

   public void DataContextChanged(Gauge sender, DependencyPropertyChangedEventArgs e)
   {
      if (e.NewValue != null)
      {
         int gaugeLevel = (int)e.NewLevel;
         _UpdateImage(gaugeLevel);
      } 
   }
}

And there you have it - to register for the data context changing, we simply implemented IDataContextChangedHandler and then registered by calling Bind in our constructor.

Jeremy Likness

Saturday, July 25, 2009

Silverlight WCF Bindings with HTTPS

If you'll recall from my post Abstracting WCF Service Calls in Silverlight, it is relatively straightforward to build a service and reuse it in a way that you can install the application anywhere and have it function correctly.

I failed to include some extra steps that are required when the service is secured via SSL. I knew that the bindings would require extra information in order to facilitate this, but I wanted to see if there was a simple, standard solution for doing it.

Glad I was able to find the solution here, which makes perfect sense: enable HTTPS as a transport option, then set it on the Silverlight side where applicable.

Jeremy Likness

Thursday, July 23, 2009

DataGrid in Silverlight and Sorting/Paging: DataContext Changed

I ran into an interesting bug today. I have a grid with a few custom controls. For example, we have a battery icon that indicates how much battery life a mobile device has remaining. The custom control takes the percentage of "life" left in the battery and creates a visual image to show in the grid.

The problem was that as I was paging and sorting the grid, I quickly realized that the images weren't changing. They rendered the very first time just fine, but after that it was the same set of images, regardless of whether I paged or sorted or not. The only way to get a fresh set was by refreshing the grid.

After some research, I found the issue. For performance reasons, the grid will create the control for each row and then reuse that control. For example, if I have 15 rows in my grid, Silverlight will generate 15 controls. However, when I sort or page, instead of creating 15 new controls, Silverlight simply updates the binding to the existing controls.

The problem is that there is no easy event to notify my control that the DataContext changed! I was plugging into the Loaded event, which only was happening on the first rendering. After doing some research online, I finally found the solution, which requires three steps.

Step One — Create a dependency property

We are going to create our own dependency property simply to know when the data context changes. It's a "dummy" property that gets tied to the DataContext itself. The snippet looks like this (in this case, my control is called ControlName). Note that I am asking it to raise an event called _DataContextChanged when the property is updated, and that I don't actually create a "InternalDataContext" property anywhere.

public static readonly DependencyProperty InternalDataContextProperty =
            DependencyProperty.Register("InternalDataContext",
                                        typeof (Object),
                                        typeof (ControlName),
                                        new PropertyMetadata(_DataContextChanged));

Step Two — Bind the property to DataContext

This ensures our dependency property is wired in to call our custom event. In the constructor, simply invoke:

SetBinding(InternalDataContextProperty, new Binding());

Now we've got the glue in place.

Step Three — act on the change!

I created the event, _DataContextChanged, to respond to any updates:

private static void _DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    ControlName control = (ControlName) sender;
    if (e.NewValue != null)
    {
        ControlName.DoSomething(e.NewValue);
    }
}

As you can see, the sender is my control, so I cast it back. Then, if there actually is a new value, I do something with it. Note that you can also get the old value and perform actions against that as well. That's it - now my control is "aware" when the grid changes and can behave appropriately.

Jeremy Likness

Thursday, July 16, 2009

Silverlight: Using Multiple Bindings and IValueConverter for Complex Templates

I'm going to give a simple example, but by "complex" I think you'll see how this can be extended.

I've addressed this issue before in a different way, and I think this is much better (see IMultiValueConverter with Silverlight). Basically, I have a compound object I need databound. In this case, I have a HyperlinkButton that needs both a URL and text, so it is getting values from multiple fields. Let's assume our class is Product with properties ID and Name. The URL is going to be "Products.aspx?id={id}" where {id} is, well, the product id.

First, we'll create a value converter to take the id and return the URL. Make a class called ProductUrlConverter and implement IValueConverter. It's one-way, so we'll leave ConvertBack unimplemented. For purity's sake, we should change it to throw a NotSupportedException if we don't intend to implement it. Now we can tackle the other method:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
   string val = value.ToString();
   return new Uri(string.Format("Products.aspx?id={0}", val), UriKind.Relative);
}

As you can see, we simply return the Uri that is relative to the product. Now for our grid. We want to fix the width of the column and ensure that longer product names wrap. We do that with a style:

<Style TargetType="TextBlock" x:Key="GridTextStyle">
   <Setter Property="TextWrapping" Value="Wrap"/>
</Style>

We also need to add a reference to the converter in the UserControl.Resources section, like this (Converters is my namespace for the converter)

...
<Converters:ProductUrlConverter x:Key="UrlConverter"/>
...

Now, we define the grid and our templates. The template for the navigation button looks like this:

<Controls:DataGridTemplateColumn SortMemberPath="Name"
    Header="Product" CanUserResize="True" CanUserReorder="True" CanUserSort="True" IsReadOnly="True" Width="100">
    <Controls:DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <HyperlinkButton NavigateUri="{Binding ID, Converter={StaticResource UrlConverter}}">
                <HyperlinkButton.Content>
                    <TextBlock Style="{StaticResource GridTextStyle}" Text="{Binding Name}"/>
                </HyperlinkButton.Content>
            </HyperlinkButton>
        </DataTemplate>
    </Controls:DataGridTemplateColumn.CellTemplate>
</Controls:DataGridTemplateColumn>

That's all there is to it - now I have a link that shows a friendly name, navigates to the product page when clicked, and wraps if the name is too long for the column.

Jeremy Likness

Tip: Sizing the Silverlight control for FireFox and other browsers

Cross-browser compatibility is very important to us and we often test functionality in multiple browsers. It was disappointing when I found that my Silverlight 3 application was no longer rendering in FireFox 3. I tested it in Internet Explorer, Chrome, and everything else, and while FireFox would not throw an error and even let me right-click and see the "Silverlight" dialog, the contents of my control simply weren't displaying!

After asking on several forums and getting blank stares and looks like, "Is this guy kidding? It works FINE in FireFox" I finally was struck today with a thought and it turned out right.

I was trying to size the page to modestly fit within the viewing area of the browser, like this:

<style type="text/css">
#silverlightControlHost 
{
    width: 100%;
    height: 100%;
    text-align: center;
}
</style>
<div id="silverlightControlHost">
    <object id="silverlightObject" width="100%" height="80%" ...

The 80% was a hack. For some reason with absolute vs. relative positioning of DIVs, the percentage-based width/height breaks in IE and so 100% was extending off-screen due to our header div. Of course this didn't help me at all on FireFox, because it wasn't showing at all. The thought that hit me was if browsers are struggling with our complex master page layout, perhaps FireFox didn't like my percentages and was making the application impossibly small?

I tested my theory and swapped out the width/height with actual pixel values of 640 and 480 respectively. I refreshed the page and ... voila! there was my application, happy as a lark but small enough to fit on a Commodore 64 screen.

Fixed size just won't do it and I was tempted to do some server-side hack to emit the size dynamically when I realized my good friend, JQuery, could come to the rescue once again. The refined mark-up indeed has a fixed size:

<style type="text/css">
#silverlightControlHost 
{
    width: 100%;
    height: 100%;
    text-align: center;
}
</style>
<div id="silverlightControlHost">
    <object id="silverlightObject" width="1024" height="768" ...

But then I add a tiny little snippet that makes the world a better place:

<script type="text/javascript">
    $(document).ready(function() {
    $("#silverlightObject").height($("#silverlightControlHost").height());
    $("#silverlightObject").width($("#silverlightControlHost").width());
    });
</script>

It's simple. The DIV sizes itself fine, it's the object inside the div that was having problems. So, once the page is loaded, I just override the height/width parameters for the object to completely fill the parent div. Works like a charm, tested on IE 6, IE 8, FireFox 3, and Chrome. (OK, so the IE 6 testing proves that IE doesn't have a clue how to size DIVs, but at least it shows, albeit with some scrollbars).

Jeremy Likness

UI Virtualization: Large Data Sets in Silverlight

Great post at Bea Stollnitz's blog about handling very large data sets in Silverlight.

Jeremy Likness

Wednesday, July 15, 2009

The Law of Demeter

Phil Haack has an interesting post today about The Law of Demeter, basically about how much a given class should "know" or interact with other classes. Very closely tied to my discussions of SOLID and DRY principles.

Jeremy Likness

Monday, July 13, 2009

Abstracting WCF Service Calls in Silverlight 3

While working with WCF in Silverlight is a blessing, it is also a challenge because of the way Silverlight manages service references. There are often extra steps required to ensure a service is Silverlight compatible, then additional considerations relative to making the service consumable on the Silverlight side and ensuring security concerns are met, etc. The purpose of this post is to provide a relatively simple and lightweight framework for abstracting the services in your applications to provide a more solid foundation for using them.

The service fundamentally begins on the web server side. Silverlight provides additional templates including a "Silverlight-enabled WCF service." Use this to add a new service reference. This is the first place that the fun can begin.

Despite the friendly sounding name, I've actually had the designer add a service and then declare custom bindings in the web.config with a mode of binary. This isn't something Silverlight knows how to consume. After receiving the ambiguous "NotFound" error and then digging deeper and finding my service was causing a "415 unsupported media type" error in the Silverlight client, I realized my service had generated incorrectly.

The first step was to dig into the web.config and find the service behaviors for my service. The "basicHttpBinding" is the one Silverlight is friendly with, something custom or binary will not. This snippet of web.config demonstrates some of the fundamentals for having the service correct on the server side:

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />    
    <behaviors>
       <serviceBehaviors>
          <behavior name="Silverlight.Service.CustomServiceBehavior">
             <serviceMetadata httpGetEnabled="true" />
             <serviceDebug includeExceptionDetailInFaults="false" />
          </behavior>
       </serviceBehaviors>
    </behaviors>
    <services>  
    <service behaviorConfiguration="Silverlight.Service.CustomServiceBehavior"
       name="Silverlight.Service.CustomService">  
       <endpoint address="" binding="basicHttpBinding"          contract="Silverlight.Service.ICustomService"/>
       <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    </service>
  </services>
</system.serviceModel>

The basics: aspNetCompatibilityEnabled, basicHttpBinding, and the "mex" binding for meta data exchange. Now we're good and can publish this somewhere to reference from the Silverlight client.

On the client, things get more interesting. You could hard code the endpoint if you knew the location it was being published to, but I needed something much more flexible. What I did know was that the client would always be in a folder relative to the web host with the services, so by extracting my local Uri, I can recreate the service Uri on the fly. What was bothering me was doing this over and over for each service, so I finally created a generic BaseService class.

The class is based on the client and channel for the service. From this, it knows how to construct a url. Because my service named "CustomService" is actually at "Silverlight/Service/CustomService.svc", a little bit of parsing the host and changing the endpoint is all that is needed.

Here is the class:

public abstract class BaseService<TClient,TChannel> : ClientBase<TChannel> where TClient: ClientBase<TChannel>, new() where TChannel: class
{
    private const string BASE_SERVICE = "Silverlight/Service/{0}.svc";

    private static readonly string _baseUri;

    static BaseService()
    {
        _baseUri = System.Windows.Browser.HtmlPage.Document.DocumentUri.AbsoluteUri;
        int lastSlash = _baseUri.LastIndexOf("/");
        _baseUri = _baseUri.Substring(0, lastSlash+1);            
    }

    private readonly TClient _channel; 

    protected BaseService()
    {        
        if (_baseUri.Contains("http"))
        {
            Binding binding = new BasicHttpBinding();
            EndpointAddress endPoint =
                new EndpointAddress(string.Format("{0}{1}", _baseUri,
                                                  string.Format(BASE_SERVICE, typeof (TChannel).Name)));
            _channel = (TClient)Activator.CreateInstance(typeof (TClient), new object[] {binding, endPoint});                
        }    
        else
        {
            _channel = Activator.CreateInstance();
        }
    }

    protected TClient _GetClientChannel()
    {
        return _channel; 
    }
}

Basically, the service encapsulates creating the client channel for communicating with the service. The static constructor takes path to the silverlight application and then using string manipulating to find the virtual directory it is hosted in (note if you are running the client in a subfolder, you'll have to trim more slashes to get back to the application root).

Note the use of the Activator to instantiate new objects that are typed to the channel/client we need. The first passes an object[] array, which causes the activator to find the constructor that best matches the parameters, in this case we are basically doing this:

_channel = new CustomServiceClient(binding, endpoint);

When the service is constructed, it checks to see the the base URI contains "http." The reason we do this is because when you run debug, the URI is actually on the file system (file:). We assume your default set up for the endpoint is sufficient for debugging and simply instantiate the client without any parameters. If, however, you are running in a web context, the endpoint is overridden with the reconstructed path. Note that we take the name of the channel and then format it into the path so that a channel called CustomService gets mapped to Silverlight/Service/CustomService.svc.

I then created a special event argument to handle the completion of a service call. It will pass back the entity that the service references, as well as the error object, so the client can process as needed:

public class ServiceCompleteArgs<T> : EventArgs where T: class 
{
    public T Entity { get; set; }

    public Exception Error { get; set; }

    public ServiceCompleteArgs(T entity, Exception e)
    {
        Entity = entity;
        Error = e;
    }
}

Now we can inherit from the base class for our actual service calls. Let's assume CustomService returns an integer. My custom service helper will look like this:

public class CustomServiceClientHelper : BaseService<CustomServiceClient, CustomService>
{
    public event EventHandler<ServiceCompleteArgs<int>> CustomServiceCompleted;

    public CustomServiceClientHelper() 
    {
        _GetClientChannel().GetIntegerCompleted += _CustomServiceClientHelperGetIntegerCompleted;
    }        

    public void GetInteger()
    {
        _GetClientChannel().GetIntegerAsync();
    }

    void _CustomServiceClientHelperGetIntegerCompleted(object sender, GetIntegerCompletedEventArgs e)
    {
        if (CustomServiceCompleted != null)
        {
            int result = e.Error == null ? e.Result : -1; 
            CustomServiceCompleted(this, new ServiceCompleteArgs(result,e.Error));
        }
    }
}

Now it's very easy for me to reuse the service elsewhere without understanding how the endpoint or data is bound - in my code, putting the result in a TextBlock is as simple as this:

public void Init()
{
    CustomServiceClientHelper client = new CustomServiceClientHelper();
    client.CustomServiceCompleted += _ClientCustomServiceCompleted;
    client.GetInteger();
}

void _ClientCustomServiceCompleted(object sender, ServiceCompleteArgs<int> e)
{
    if (e.Error == null)
    {
        DisplayTextBlock.Text = e.Entity.ToString();
    }
    else 
    {
        HandleError(e.Error);
    }
}       

Now you've got a simple framework for plugging in services and consuming them on the client side that will update its references based on the location it is installed. Of course, there is much more you can do and I suggest the following articles for further reading:

Jeremy Likness

Friday, July 10, 2009

Silverlight 3 is Released!

Finally - it's in releases! No more beta or no end-user run license.

OK, off to download my copy now!

Jeremy Likness

Tuesday, July 7, 2009

IMultiValueConverter with Silverlight

A converter is a powerful concept with WPF and Silverlight. It implements IValueConverter and allows you to map a value to a display entity. For example, if you have an application that stores temperatures, you might use a converter to map the temperature to the image of a thermometer that visually represents the temperature. A weather application might take the text "party cloudy" and map this to an image of clouds.

While Silverlight supports value converter, one limitation is that you cannot convert multiple values at once. For example, if you are converting a value in a data grid, your converter doesn't, by default, have access to the other values or properties in the row. This makes it difficult, for example, to take a first name and last name and convert those into a single representation of "full name."

There is a workaround! With a little creativity you can do just about anything.

In my case, I had a data grid and wanted to show a hyperlink button. I needed two values to go into the button: the text to describe the link, and an internal id to pass to the link. Consider a product list: my products have internal identifiers and external descriptors. So identifier with ID=1 maps to product with Name="Backpack". How do I create a link like this:

<a href="http://localhost/products.aspx?id=1">Backpack</a>

The first step is to create a control that can accept multiple bindings. We do this by adding a dependency property so data can be bound to the property. In this case, I simply inherited from the HyperlinkButton and added my identifier property:

public class ProductHyperlink : HyperlinkButton
{
    private const string BASEURI = "http://localhost/products.aspx?id={0}";

    public static readonly DependencyProperty IDProperty =
        DependencyProperty.Register("ID", typeof (int), typeof (ProductHyperlink), null);

    public ProductHyperlink()
    {
        Click += _ProductHyperlinkClick;
    }

    void _ProductHyperlinkClick(object sender, RoutedEventArgs e)
    {
        NavigateUri = new Uri(string.Format(BASEURI, ID));            
    }

    public int ID
    {
        get
        {
            return (int) GetValue(IDProperty);
        }
        set
        {
            SetValue(IDProperty, value);                 
        }
    }
}

Notice how I intercept the click and bind the id with the URL to my Uri. This enables me to navigate directly to the link I want. The data grid looks like this:

<data:DataGrid x:Name="DeviceGrid" AutoGenerateColumns="False" Margin="5">        
    <data:DataGrid.Resources>            
        <DataTemplate x:Key="ProductTemplate">
            <Controls:ProductHyperlink ID="{Binding ID}" Content="{Binding Name}"></Controls:DeviceHyperlink> 
        </DataTemplate>
    </data:DataGrid.Resources>
    <data:DataGrid.Columns>
        <data:DataGridTemplateColumn data:Header="Product" CellTemplate="{StaticResource ProductTemplate}" CanUserSort="True" 
                 SortMemberPath="Name"/>                
    </data:DataGrid.Columns>
</data:DataGrid> 

We create a template that binds both the ID and the Name to our custom control. Remember, the control will turn the ID into a Uri that links to the product when it is clicked by the user. The column simply maps to the template. Note that by indicating the user can sort and providing a sort member path, I instruct the grid automatically how to sort my custom column even though I'm not binding directly to a single property.

That's all there is to it - you can use a similar method by creating custom panels, text blocks, etc, and even taking multiple bindings and injecting the custom controls inside your custom container.

Jeremy Likness

Monday, July 6, 2009

Beginning Silverlight: Tic-Tac-Toe

As part of the learning process for Silverlight, I put together a Tic-Tac-Toe game. It was a perfect way to understand layout, controls, late-binding and other features. I wrote an article about the experience that is published at The Code Project:

Beginning Silverlight: Tic-Tac-Toe

You can play the game online here and download the source code from my MGet page.

Enjoy!

Jeremy Likness