Replaceable resources in Silverlight

Some time ago I faced a requirement where I needed to be able override my Silverlight resources at runtime, and here’s how it got implemented.  All the resources that we wanted to be able to override got moved into a specific ResourceDictionary, which was brought into the App.xaml resources through a MergedDictionary.  Then at runtime, the following code would be executed:

public void LoadResourcesFromServer()
{
    DownloadStyle(new Uri("../ClientBin/brand/CustomizableResources.xaml", UriKind.Relative));
}

private void DownloadStyle(Uri downloadUri)
{
    WebClient wc = new WebClient();
    wc.DownloadStringCompleted += ParseAndAddStyles;
    wc.DownloadStringAsync(downloadUri);
}

This is just a pretty standard way of downloading a file.  It starts to get interesting when we process the downloaded XAML file (exception-handling code elided for clarity):

private void ParseAndAddStyles(object sender, DownloadStringCompletedEventArgs e)
{
    if (e.Error == null)
    {
        var loaded = (ResourceDictionary)XamlReader.Load(e.Result);
        MergeResources(loaded);
    }
}

private void MergeResources(ResourceDictionary resourceDictionary)
{
    foreach (ResourceDictionary dictionary in Application.Current.Resources.MergedDictionaries)
    {
        if (dictionary.Source.OriginalString == "CustomizableResources.xaml")
        {
            Application.Current.Resources.MergedDictionaries.Remove(dictionary);
            break;
        }
    }
    Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
}

This code parses the XAML into a ResourceDictionary, and then replaces the CustomizableResources dictionary that has been compiled into the XAP file with the one downloaded.

As you can imagine, there are a large number of failure cases which need to be considered in real-life (e.g. the web server gives a 404 or other error, the downloaded XAML fails parsing, or isn’t a ResourceDictionary, etc, etc) but this code is at the heart of our solution to the requirement.

July 28 2011

Silverlight: Cross-domain, but not cross-zone

A colleague approached me to solve a Silverlight issue where a cross-domain web service call was generating a SecurityException.  We checked that clientaccesspolicy.xml was in the correct place and should allow the call and then we ran Fiddler.  What we noticed was that Silverlight was not even attempting the request for the clientaccesspolicy.xml file!

After some investigation and supposition, we eventually traced it down to an issue with Internet Security Zones.  The Silverlight hosting page was being accessed through a fully qualified domain name and was in the Internet zone, while the service was being accessed via computer name and was in the Intranet zone.  The solution was to setup a full DNS entry for the service and use that from our SL application, because then the service host was also in the Internet zone and so the call was allowed.

Now it definitely does make sense that an Internet site should not be able to make cross-domain calls into your Intranet (and kudos to the Silverlight team for taking security this seriously) but it can be a tricky problem to spot when it’s happening in your dev environment!

An MVVM marquee control (in Silverlight and WPF)

Marquee controls have been around for a long time and have been abused for much of that time.  Their place in the user experience should really be very small, since they have a number of drawbacks – information isn’t static for the user to absorb, they typically only display a limited amount of information and they assume a certain reading speed (which may be too slow or too fast).  Nevertheless, there are times when marquee controls are useful, news or stock tickers for example.

So I needed a marquee to display a news ticker in Silverlight, and I didn’t want to have use any nasty code-behind.  Nothing seemed entirely appropriate, so I used the marquee control from Jobi Joy as a starting point and extended it:

  1. The control needed to work in an MVVM application.  This meant that it mustn't rely on code calling “ticker.StartAnimation()” or anything like that – it had to all work by binding to a view model.
  2. I changed the animation so that there is always as much content visible as possible, i.e. the first bit of content starts at the left-hand side, not at the right-hand side
  3. Just for kicks, I added a little bit of acceleration to the scrolling, so that it is not always a constant speed
  4. I got the marquee working in WPF as well as Silverlight

Some friends contributed here too – Marcin extracted the control template into generic.xaml, and Marlon fixed an issue where the marquee would zoom uncomfortably fast if there was too much content.  The result is that you can just use a few lines of XAML to achieve the effect:

<Controls:Marquee InitialPause="2"
                  FinalPause="2"
                  Duration="5"
                  Acceleration="0"
                  Height="20"
                  Grid.Row="0">
    <ItemsControl ItemsSource="{Binding Headlines}" ItemTemplate="{StaticResource HeadlineTemplate}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Controls:Marquee>

The control template for this uses a ScrollViewer to house a Canvas.  The content to be scrolled is placed on the canvas.

<ScrollViewer Width="Auto"
              Height="Auto"
              BorderThickness="0"
              HorizontalContentAlignment="Stretch"
              VerticalContentAlignment="Stretch"
              VerticalScrollBarVisibility="Disabled"
              Margin="0"
              Padding="0">
    <Canvas>
        <ContentPresenter x:Name="PART_Content"
                          VerticalAlignment="Center"
                          HorizontalAlignment="Center" />
    </Canvas>
</ScrollViewer>

An animation gets calculated and setup to scroll the content from right to left:

Storyboard _storyboard = new Storyboard();
DoubleAnimationUsingKeyFrames _animation = new DoubleAnimationUsingKeyFrames();

double totalDuration = InitialPause + Duration * _contentPart.ActualWidth / ActualWidth;
_animation.Duration = new Duration(TimeSpan.FromSeconds(totalDuration));

// _animation.KeyFrames setup omitted here for clarity. Get the source code if you want it.

_storyboard.Duration = new Duration(TimeSpan.FromSeconds(value + FinalPause));

if (_storyboard.Children.Count == 0)
{
    _storyboard.Children.Add(_animation);
    Storyboard.SetTargetProperty(_animation, new PropertyPath("(Canvas.Left)"));
    Storyboard.SetTarget(_animation, _contentPart);

    _storyboard.RepeatBehavior = RepeatBehavior.Forever;
}

_storyboard.Begin();

The result should show up below, if you have Silverlight 4 installed on your machine.

The full source code and samples in WPF and Silverlight can be found in the SharpFellows Toolkit.  And just for practice I used the Reactive Framework in the sample to asynchronously download and display some news headlines from a website.  Enjoy!

Silverlight duplex communication, part 2: Reactive Extensions

 

In part 1 of this series I posted a Silverlight chat application that used duplex communications over HTTP.  Now to me, incoming data or events imply that the Reactive Framework (Rx) is probably a good fit for the application.  And so I decided to refactor the chatroom application to use Rx.

Introduction to Reactive Framework

Other (possibly  better) introductions can be found here, here or here, but I think that I a metaphor described by a colleague of mine is appropriate.  Usage of IEnumerable<T> can be likened to a water well – you pull the water out just as fast as you want it.  On the other hand, usage of IObservable<T> (a key Rx interface) can be likened to a spring of water – it just keeps pumping water at you and you better try and keep up!

On a slightly more detailed note, the main interfaces within Rx are IObservable<T> and IObserver<T>.  An Observable represents an event source while the Observer repesents an event sink.  Of course, an Observer can also be an Observable … think for a moment about an event filter that passes on some events while ignoring others.  Classes that implement IObservable<T> and IObserver<T> are known in Rx as “subjects”.

Interestingly, Rx chooses to represent an “event” as a method call.  So being an Observer (i.e. you receive events) actually just means that you receive method calls on your IObserver<T> interface.  And being an Observable (i.e. you raise events) actually just means that you make method calls to Observers who have registered an interest by subscribing.

Now you may be wondering what the generic type parameter T is used for.  Well that is the type of data that accompanies the event.  Bearing in mind that an event is just a method call, how do can you attach data to this?  Rx assumes that the data is actually the one and only parameter to the method.

This pattern is really at the heart of Rx.  It’s very easy to get lost in the plethora of extension methods and classes, but it’s crucial to always remember and try to apply that pattern.

Reactive Framework Extension Methods

And there are indeed a lot of very useful extension methods provided by the Rx framework.  Some of them perform filtering, some are useful in combining event streams (and so reducing concurrency) and others are useful in splitting event streams (and so increasing concurrency and parallelism). The best resource I’ve found has to be the Rx Wiki.  It describes all the operators and provides one or more example usages for most of them.

Using Rx in Silverlight

And now back to the subject in the title of this post!  :-)  There are a few assemblies which form the core of Rx for Silverlight:

  • System.Observable: contains the definitions for IObserver<T> and IObservable<T>
  • System.Reactive: contains all the extensions that support LINQ over Observers and Observables
  • System.CoreEx: concurrency constructs and other classes that support Rx

Note that on the desktop version of .Net 4.0, IObserver<T> and IObservable<T> are now included in mscorlib, which means that System.Observable is not used on that platform.  Sadly, this is not the case with Silverlight 4.0.

Creating an Observable

I’ll let the code speak for itself here:

public class ChatRoom
{
    // Other code omitted for clarity

    private readonly Subject<ChatData> _messages = new Subject<ChatData>();

    public IObservable<Timestamped<ChatData>> Messages
    {
        get { return _messages.Timestamp().AsObservable(); }
    }

    public void AddReceivedMessage(ChatData chatData)
    {
        _messages.OnNext(chatData);
    }
}

 

As you can see, we create a subject (_messages) and then expose it via the Messages property so that other code can consume the events.  When we are notified of a new message being received off the wire, we simply publish it on the subject.

Note that the code that actually listens to the network and calls the AddReceivedMessage method is included in the download and was explained in part 1 of this series.

We are making use of one interesting Rx feature here … the Timestamp extension method.  It takes an IObservable<T> and returns an IObservable<Timestamped<T>>, which in effect just adorns the object with the a new property holding the current date and time.

Consuming the Observable

Here is an embarrassingly trivial example:

ChatRoom.Instance.Messages.Subscribe(data => messages.Add(data));

This code is listening for events being raised from the subject and, when each one occurs, adding the new message to the “messages” collection which is bound onto the UI.  To be honest, this is not a particularly good illustration of what Rx is capable of, since the line of code here replaced an equally simple line of code that didn’t use Rx!

So let’s suppose that incoming message were extremely high volume, and instead of updating the UI as each message comes in, you decide you want to do it 4 times per second.  Without Rx this involves timers and so on.  With Rx, you replace the above line with the following:

ChatRoom.Instance.Messages
     .BufferWithTime(TimeSpan.FromMilliseconds(250))
     .Subscribe(buffer => buffer.ForEach(msg => messages.Add(msg)));

 

Or perhaps you want to implement some kind of command protocol using the message stream.  Something like this would work:

ChatRoom.Instance.Messages
    .Where(msg => msg.Data.StartsWith("pls.forward:"))                    
    .Subscribe(msg => ForwardMessage(msg));

 

Or (and IMO this is the most impressive aspect of Rx) if you want to do all of the above simultaneously, all you need to include the relevant lines.  You see, all the aspects of message processing here are totally separated and independent of each other.  And to me, that’s sweeeeeet!

Download the code: DuplexChatClient-with-Rx.zip (52.78 kb)
Older Posts