A couple of weeks ago, I requested to build a code sample for Microsoft. It was supposed to make it possible to restyle a Silverlight application based on a SharePoint 2010 theme. In SharePoint 2010, theming is based on OpenXML, and saved as files with an extension of “thmx”.
But I guess the first question would be WHY you would use a huge thmx file just to restyle a Silverlight application… Well, imagine that you are building Silverlight applications that are to be used inside a SharePoint application. Imagine how nice it would be if the Silverlight applications would change appearance if you changed the theme of the SharePoint site. That way, changing the theme would not be a problem, and would not cause a whole lot of rework.
Microsoft’s idea was to use the new Theme control from the Silverlight toolkit. The Theme control is fairly simple. It is basically a container control that you wrap around your elements, and they will all automatically inherit the styles they need. The problem in this case, is that the Theme control expects a Uri to a Xaml file with the styles. In this case, the styles were in thmx files…
The solution? Well, Microsoft’s idea was to have SharePoint open the thmx files and create a Xaml files based on these, and then pass the Xaml files to Silverlight. The problem with this, is that there is apparently no way to know when a SharePoint application is being restyled. So there was no way to know when there was a need to make the conversion from thmx to Xaml.
Having worked with OpenXML before, I suggested to do the conversion client side instead. OpenXML is just XML files in a zipped format, and Silverlight has no problem reading zip or xml files. This would also make it possible to use the solution outside of SharePoint. The Url to the thmx file would be passed to the Silverlight application as a init parameter. The only problem was that the Url would point to a thmx file and not a Xaml file. This could easily be fixed by downloading the thmx file, convert it, save the converted file in isolated storage and then…yeah…and then…there is no real way to pass a Uri that points to a file in isolated storage. And there is no way to pass the Theme control a Stream. So I had to either rebuild the Theme control to accept a Stream, or figure out a way to get a Uri to point to isolated storage.
I won’t go too deep into the source code as it is available for download at http://code.msdn.microsoft.com/slsptheme, but I would like to look at a couple of things that are interesting in this sample. Mainly the two I mentioned above, reading the thmx file and getting a Uri to a file in isolated storage.
Let’s start with the reading of the thmx file. This is nothing complicated at all. As I mentioned before, the OpenXML format is basically just xml files (and possibly other resources) packaged in a zip file. So the first thing we need to do, is download the thmx file. But this is a trivial task, so I will ignore it. But when the file is down, how do I get to look inside it? Well, the way that Silverlight handles zip files might seem a bit odd, but you use a class called StreamResourceInfo. It takes a Stream as a parameter to the constructor. It also takes a content type, but this can be ignored in this case. As soon as you have a StreamResourceInfo that represents the zip file, you can use a static method on the Application object to get a specific file inside it. The method is called GetResourceStream(). It returns another StreamResourceInfo object. To access the actual file, we just need to use the property called Stream. That’s it. In this case, I parse the Stream to an XDocument and do some xml manipulation to get a Xaml file with the style. This Xaml is then stored in isolated storage for future use.
Next, I needed, as mentioned before, to find a way to get the isolated storage file based on a Uri. I had, to be totally honest, absolutely no idea how to do this. There is no way to create a Uri that points to isolated storage. The whole idea with isolated storage is that you have no idea where it is. Well…you do…but you don’t… The solution I used was to use a custom request prefix. Basically, the request system in .NET looks at the first part of a Uri to figure out what class should be used for the request. If you for example pass in a Uri that starts with “http://” it will create an HttpWebRequest. What we can do, is that we can change add a new prefix, and map that to a custom implementation of an interface called IWebRequestCreate. The IWebRequestCreate interface declares a single method, Create(), that returns a WebRequest. In my case I return a ThmxWebRequest, which is a custom implementation that will read from isolated storage. So how do I map the prefix? Well, that is easy. Just call the WebRequest.RegisterPrefix method, passing in a string with the prefix and an instance of an object that implements IWebRequestCreate.
As soon as this is registered, and I have implemented a custom WebRequest that reads from isolated storage, anytime you pass in a Uri that starts with whatever prefix you have used, your custom code will be responsible for getting the file. In this case, I also added the whole conversion part in there, as well as a caching part. So as soon as someone requests a thmx file off the web, my implementation will first check isolated storage for a corresponding Xaml file. If it finds it, it returns it. If not, it downloads it, converts it to Xaml, puts it i isolated storage and then returns a Stream to it. Nice and simple! This way, I didn’t have to modify the Theme control at all.
So…are there any downsides? Well, it does fill up isolated storage quite rapidly. But hopefully you won’t be changing the styling too often. THe sample also includes code for requesting an increase in isolates storage.
So…I suggest you do over to MSDN’s Code Gallery and have a look at it if it seems interesting. Even if you aren’t going to use thmx files, the possibility to add caching like this can be very interesting in a lot of cases.