GZIP Compressed Web Requests in WP7 - Take 2

I earlier wrote about how .NET API in Windows Phone 7 doesn’t zip-compress it’s web requests, and how using Mango’s socket support allows you to circumvent that to get more efficient use of the limited data connection phones often have. The approach had some problems and limitations, and wouldn’t work in all scenarios. Even so, compressing your webrequests is very important on a phone since you can often save 50-80% of the data transmitted, and many people pay by the byte they transmit.

Since my blogpost Windows Phone 7 Mango has gone through a few iterations, and using a socket is no longer necessary, since the “accept-encoding” header needed to request GZIP compressed requests is no longer blocked (thank you to those who voted for getting this unblocked). However, GZIP compression is still not a built in feature (Why not Microsoft?!?). If you set this header, you are still in charge of uncompressing the content. I’ve updated my GZipWebClient class to take advantage of the new allowed header. It simplifies the class quite a lot, as well as made it more stable.

Also I “forked” the DotNetZip library and included in this project, so you don’t need to go find 3rd party dependencies, and in the process removed all parts of the library that is not needed to keep the assembly as compass as possible. So here’s what the client now basically looks like:

public class GZipWebClient : WebClient
{
    [SecuritySafeCritical]
    public GZipWebClient()
    {
    }
    protected override WebRequest GetWebRequest(Uri address)
    {
        var req = base.GetWebRequest(address);
        req.Headers[HttpRequestHeader.AcceptEncoding] = "gzip"; //Set GZIP header
        return req;
    }
    protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
    {
        WebResponse response = null;
        try
        {
            response = base.GetWebResponse(request, result);
            if (response.Headers[HttpRequestHeader.ContentEncoding] == "gzip")
                return new GZipWebResponse(response); //If gzipped response, uncompress
            else
                return response;
        }
        catch
        {
            return null;
        }
    }
}

The key here is adding the header that tells the server “Hey I understand GZipped responses as well”. When the response comes back and the header says “Hey here’s some gzipped content”, I use a custom GzipWebResponse class that uses the DotNetZip library to uncompress the stream. Apart from some trivial code, this is the meat of that class:

internal class GZipWebResponse : WebResponse
{
    WebResponse response;
    internal GZipWebResponse(WebResponse resp)
    {
        response = resp;
    }
    public override System.IO.Stream GetResponseStream()
    {
        return new SharpGIS.ZLib.GZipStream(response.GetResponseStream()); //Uncompress
    }
}

You use it exactly like the normal “WebClient” class, except you instantiate the GZipWebClient instead. The rest of your existing code doesn’t change at all. Example:

WebClient client = new SharpGIS.GZipWebClient();
client.DownloadStringCompleted += client_DownloadStringCompleted;
client.DownloadStringAsync(uri);

You can download the source code below, and you can also get it on NuGet/SymbolSourceusing command “Install-Package SharpGIS.GZipWebClient”

Download source

I also rely heavily on this in my WinPhone Twitter client ‘Peregrine’ and have seen quite a performance improvement when updating your twitter timeline. You can download Peregrine for free here: http://www.windowsphone.com/en-us/apps/75067abc-c9d1-47b7-8ace-76aede3911b2?wa=wsignin1.0

UPDATE!!!

With the latest v1.1 update on nuget (source also updated here), you don’t have to do the above. All you need to write is the following two lines of code and put it in your app.xaml.cs startup file. After this ALL existing code (including 3rd party libraries you don’t have control over) will start using GZIP compression!

WebRequest.RegisterPrefix("http://", SharpGIS.WebRequestCreator.GZip);
WebRequest.RegisterPrefix("https://", SharpGIS.WebRequestCreator.GZip);

If you just want to do this for some domains, only register that prefix part. So no need to go replace all your WebClient as mentioned above. More on how this work in this blogpost.

UPDATE2!!!

Note that using RegisterPrefix causes this to use an internal custom HttpWebRequest class and that can have some serious effects on your app. Unfortunately Microsoft ‘forgot’ to unlock the Get/Set UserAgent property when they ported from Silverlight (there the user agent was always the browser), and many 3rd party libraries  tries to set this property, which will throw a NotImplementedException. Also note that cookies are not supported either (some libraries use that). They should of course be checking the “SupportsCookieContainer” property before using it, but not all do (I’m looking at you RestSharp), and will therefore throw an exception. However for almost all the simpler scenarios, the above register works like a charm.

UPDATE3!!!

Now also on GitHub: https://github.com/dotMorten/SharpGIS.GZipWebClient

UPDATE4!!!

Update 2 no longer applies :)

Windows Phone: Adding Mango features to a 7.0 WinPhone App

Microsoft just announced what happens to your non-Mango app (WP7.0) if you publish a Mango app (WP7.1) to the marketplace. Basically if you upgrade your app to 7.1, you will no longer be able to maintain the 7.0 app for people who still haven’t upgraded yet (and we know from the last update that that can take a while).

However, it is possible to make a 7.0 app that uses Mango features if the user has upgraded to Mango. That way you can continue to build a 7.0 app, and the moment the user upgrades to Mango, he will get additional features available. All this using a little “reflection magic”. The certification requirements does kind of hint at that this is not allowed, but some apps has already made it through certification, so this seems to be considered OK to do, and it also works great as well.

So how is this done? We can use the method of the Type object to get identifiers of methods, events, constructors, properties etc. by using their names. This way we will never write a piece of code that explicitly uses one of these (which the compiler would reject), but instead at runtime will attempt to locate them, and if successful execute/access it

But before we attempt doing that, we can perform a check for whether this is a mango device (or newer). Mango is v7.1, so it must be version 7.1 or newer:

bool IsMangoDevice = (Environment.OSVersion.Version >= new Version(7, 1));

We can also get access to types we don’t have yet in the 7.0 compiler but the 7.1 device has. For example, the StandardTileData class used to create secondary tiles for your app can be accessed using its full name and assembly:

Type t = Type.GetType("Microsoft.Phone.Shell.StandardTileData, Microsoft.Phone");

If we continue down this path, we can actually create a secondary tile on a mango device, and still have the app be 7.0. That means you can TODAY submit an app to the marketplace that uses mango features, and people with developer devices will get mango features now! (again note that this might cause problems during marketplace certification, but some apps has already made it through and is available today!)

Anyway… here’s the code that creates a secondary tile:

//get the constructor for the StandardTileData and create a new instance of it
var newTileData = t.GetConstructor(new Type[] { }).Invoke(null);
//Get the setter method for the title property
var setMethod = newTileData.GetType().GetProperty("Title").GetSetMethod();
//Set the tile title
setMethod.Invoke(newTileData, new object[] { "This is my new tile" });
//Get the create method for the shell tiles
Type shellTileType = Type.GetType("Microsoft.Phone.Shell.ShellTile, Microsoft.Phone");
var createMethod = shellTileType.GetMethod("Create");
//Create the tile, with the uri and tile data
Uri uri = new new Uri("/MainPage.xaml?Test=This_Came_From_A_Secondary_Tile", UriKind.Relative)
createMethod.Invoke(null, new object[] {  uri, newTileData});

Or how about setting the system tray color?

typeof(Microsoft.Phone.Shell.SystemTray).GetProperty("BackgroundColor").
GetSetMethod().Invoke(null, new object[] { Colors.Red });

Of course you should only call these methods if the device is a mango device, so use the check shown first before executing this code. I created an extension class that neatly encapsulates this and only executes the code if it’s supported. Examples:

//Set tray color if supported
MangoExtensions.SetTrayColor(Colors.Blue);

//Add hold event handler to a control (onHold is of type RoutedEventHandler delegate):
myControl.AddEventHandler_Hold(onHold);

//Show "Create secondary tile" button if supported:
createTileButton.Visibility = MangoExtensions.IsMangoDevice ? Visibility.Visible : Visibility.Collapsed;

//Create/replace tile:
MangoExtensions.CreateTile("My Tile", new Uri("/MainPage.xaml?parameter1=value1", UriKind.Relative));

You can download the entire extension class here: Download

WPF vs. Silverlight - Part 11 - Silverlight on Phone vs. Browser

See intro blogpost here.

Well technically this is not a WPF vs Silverlight post, but a SL vs WP7, but it still kinda belongs in this series.

Generally the differences in the API’s between Browser Silverlight and Windows Phone Silverlight are pretty slim. However dealing with the phone can be quite different anyway. First there’s often less security restrictions on the phone to worry about. Secondly there’s a lot of phone specific APIs like sensor data, camera, contacts etc). The screen is also smaller so often some controls doesn’t make sense to have on the phone, or needs to have a separate layout to enhance the experience on this small touch-centric screen. Lastly (and very importantly) the small amount of memory, processing power and battery life means that performance is a concern. This often forces you to go down a slightly different avenue for your application, and for certain custom controls.

For the API differences I’m again going to be cheap and just point you to the main resource on MSDN that has some really good info on that matter:

Other notable things:

  • In Silverlight for Windows Phone, effects such as Blur and DropShadow are not supported.
  • Custom pixel shaders are not supported, so the PixelShader type is not supported.
  • Silverlight applications on Windows Phone are hosted on the client device and do not run inside of a browser. Silverlight features that are based on a browser host are not supported. These features include the HTML DOM bridge, JavaScript programmability, and the Silverlight plug-in object reference.
  • Isolated storage on Windows Phone does not enforce quotas to restrict the size of isolated storage for Silverlight-based applications. The default quota size of 1 MB does not apply. (however there’s still a 2Gb limit on Isolated Storage for WP7, or less if you run out of space).
  • Manipulation events that Silverlight doesn’t have (well technically they are there but throw a not supported exception), are the same as in WPF, so WP7 has better touch closer to WPF than Silverlight.

Next: WPF vs. Silverlight - Part 11– Silverlight on Phone vs. Browser

WPF vs. Silverlight - Part 10 - XAML Parser Differences

See intro blogpost here.

The XAML parser for WPF and Silverlight are not one and the same, and this also means that there (for whatever reason) are differences in how they interpret the xaml (as kinda hinted at in part 7).

MSDN has a pretty good description on those differences, so I won’t repeat the entire thing (please go read it!), other than point to a couple of important items:

  • Silverlight 4 introduces a XAML parser that behaves differently than Silverlight version 3 and is closer to the WPF XAML parser implementation.  Silverlight includes a XAML parser that is part of the Silverlight core install. Silverlight uses different XAML parsers depending on whether your application targets Silverlight 3 or Silverlight 4. The two parsers exist side-by-side in Silverlight 4 for compatibility. (read: You are more likely to hit issues porting an SL3 app to WPF than from SL4)
  • In some cases, the XAML parsing behavior in Silverlight differs from the parsing behavior in Windows Presentation Foundation (WPF). WPF has its own XAML parser (read: We made a new XAML parser for Silverlight, and cut some corners...)
  • Object elements inside a ResourceDictionary in Silverlight may have an x:Name instead of or in addition to an x:Key. If x:Key is not specified, the x:Name is used as the key. (read: Always use x:Key since this works in both)

Most of the other differences are things supported only in WPF, which is to be expected since Silverlight is a subset. Again this is why I recommend starting with Silverlight and then porting to WPF.

Other things worth noting:

WPF v3.5 does not have a VisualStateManager. Silverlight “invented” this and WPF 4 added it. If you build for WPF 3.5, you can use the WPFToolkit, which adds this support to 3.5.

Compiler conditionals in XAML

As demonstrated in many of the previous parts, you can use compiler conditionals in code-behind (ie. #if SILVERLIGHT...) to write code specific to the platform. Unfortunately you don’t have that luxury in XAML. If you need to have different xaml for the different platforms, I haven’t found a way around duplicating the entire piece of XAML in each project. So no, this won’t work Sad smile :

image

Next: WPF vs. Silverlight - Part 11 - Silverlight on Phone vs. Browser

WPF vs. Silverlight - Part 9 - Reusing code in Visual Studio #2

See intro blogpost here.

In the previous post, I covered how to configure your project to create both a Silverlight and a WPF build. However, I usually like to structure my control libraries slightly different, and place the theme file for a specific custom control next to the control code, instead of having an ever-growing Themes\Generic.xaml file with all the controls merged together. It makes it easier to find the theme for a specific control without having to scroll an enormous XAML file. My Silverlight project will usually look like this:

image

In Generic.xaml I instead “merge” in this file using a Merged Dictionary:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/MyLibrary;component/Controls/TemplatedControl1.Theme.xaml" />
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

As you add more controls, you simply add another entry into the resource dictionary. So the next step would be to link this new TemplatedContro1.Theme.xaml file into WPF, and make your solution look like the following:

image

However, this won’t work in WPF! When you run the WPF version, the template will never get applied, because the template is never found. There seems to be a bug in WPF that the Silverlight and Windows Phone compilers doesn’t have, and it literally took me forever to find this problem: WPF doesn’t like linked resource files that isn’t located in the root of the project! The only workaround I’ve found so far is to move the TemplatedControl1.Theme.xaml to the root of the WPF project (still linked though) and create a new non-linked Generic.xaml file for WPF (since it now has to point to a different location of the theme file).

So your WPF project will instead look like this:

image

Note that the WPF\MyLibrary\Themes\Generic.xaml is no longer linked, but is a copy of the Silverlight file, with the \Controls\ part removed from the ResourceDictionary Source path.

By the way, if you followed the same steps for creating a Windows Phone 7 project, the WP7 compiler doesn’t have this problem so this workaround is specific for WPF only.

Next: WPF vs. Silverlight - Part 10 - XAML Parser Differences

WPF vs. Silverlight - Part 8 - Reusing code in Visual Studio #1

See intro blogpost here.

So now that you know several of the code differences between Silverlight and WPF, how would you set this up in Visual Studio? You could create two projects and copy the code over from one to the other, but then you would have to maintain two sets of code. There’s an easier way:

Create two new Class Library projects in VS2010. One for Silverlight and one for WPF. I recommend calling them the exact same (I know some people like to add WPF or SL to the name to distinguish them, but that forces other libraries that uses them to be different so don’t do that!). If you need one for Windows Phone 7 as well, simply repeat the steps for WPF coming later...

image

image

image

Note that VS will balk at you for naming the project the same. For now just call it for instance “MyLibrary2” or something.

Next in the solution explorer, create a Silverlight and a WPF folder, and move the projects to these. You can now rename the WPF project so it’s called the same. Also go into the project settings and remove the “2” from the assembly name and default namespace. Lastly deleted the default classes that were created for you, as well as the Generic.xaml file in WPF.

image image

OK we are all set. We have two empty projects. One for WPF and one for Silverlight.

In Silverlight add a new “Silverlight Templated Control” item. You now have a custom control in Silverlight. We could copy this to WPF, but instead we’ll link it over. Right-click WPF\MyLibrary, and select “Add existing item”. Browse to the class you added to silverlight, but DON’T click the add button. Instead click the little down-arrow next to the add button and pick “Add as link”.

image image

Repeat the same step for the Generic.xaml file.

Notice in the solution explorer that the “TemplatedControl1.cs” icon in WPF has a little arrow in the lower left corner? This means this is not really located in the WPF\MyLibrary folder, but is pointing to the file in the Silverlight folder.

Now it’s time to remember what was covered in “Part 1”. Add the following code to the new control:

    static TemplatedControl1()
    {
#if !SILVERLIGHT 
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(TemplatedControl1),
            new FrameworkPropertyMetadata(
            typeof(TemplatedControl1))); 
#endif
    }

Also put the “this.DefaultStyleKey…” in a SILVERLIGHT conditional.

Your code should now look something like this:

image

For the most part, from here on the code you write for your Silverlight control, should work in WPF. If you hit a difference between the two or want to enhance the WPF version with WPF specific features, use the conditionals as shown above.

This blogpost will continue in the next part with some interesting file-link bugs in Visual Studio you need to be aware of.

Next: WPF vs. Silverlight - Part 9 - Reusing code in Visual Studio #2

WPF vs. Silverlight - Part 7 - Case sensitivity

See intro blogpost here.

Silverlight in general seems less restrictive when it comes to your XAML. For instance case sensitivity. I was recently trying to use a class modifier on my UserControl using the following:

<UserControl x:ClassModifier=”Internal>

However this doesn’t work in WPF. It turns out that the "internal" keyword must be lower-case in WPF. Luckily this also works in Silverlight, so stick with the right casing and you’re good :-)

Next: WPF vs. Silverlight - Part 8 - Reusing code in Visual Studio

WPF vs. Silverlight - Part 6 - Debug.WriteLine

See intro blogpost here.

I often use System.Diagnostics.Debug.WriteLine to write out values or warnings that I or the developer should be aware of, but not necessarily is an error. However, Silverlight has an overload WPF doesn't have (hey I thought Silverlight was only a subset of WPF?!? - well... if it were we didn't need this blogpost serie).

Here are the overloads in Silverlight:

    public static void WriteLine(object value);
    public static void WriteLine(string message);
    public static void WriteLine(string format, params object[] args); //NOT IN WPF! 

and WPF: 

    public static void WriteLine(object value);
    public static void WriteLine(string message);
    public static void WriteLine(object value, string category);
    public static void WriteLine(string message, string category); 

Notice that the 3rd method in Silverlight which is equivalent of using string.Format, doesn’t exist in WPF. Therefore it's safer to use WriteLine(string.Format(format,args)) instead which works in both.

Next: WPF vs. Silverlight - Part 7 - Case sensitivity

WPF vs. Silverlight - Part 5 - XAML Control Instantiation

See intro blogpost here.

Take a look at the following XAML:

    <UserControl Loaded="UserControl_Loaded">
        <my:MyControl Loaded="MyControl_Loaded" />
    </UserControl> 

Can you guess in what order the following events and methods will be triggered when the XAML loads:

    - UserControl.Constructor
    - MyControl.Constructor
    - UserControl.Loaded event
    - MyControl.Loaded event
    - MyControl.OnApplyTemplate method

Come on... just guess...
  
The gotcha here is that it will be different for Silverlight and WPF!

Silverlight WPF
UserControl.Constructor UserControl.Constructor
MyControl.Constructor MyControl.Constructor
MyControl.Loaded event MyControl.OnApplyTemplate method
UserControl.Loaded event UserControl.Loaded event
MyControl.OnApplyTemplate method MyControl.Loaded event

Notice how OnApplyTemplate for the custom control fires before the Loaded events in WPF whereas in Silverlight if fires after. Therefore if you have code in OnApplyTemplate)() that relies on the the Loaded event having fired first, this probably won't work in WPF (nevermind the fact that your code is probably poorly designed if that's the case :-). Also note that the order the two loaded events fires are opposite.

This is also documented on MSDN:

The timing of the Loaded event in Silverlight differs from the timing of the FrameworkElement.Loaded event in WPF. Specifically, the WPF Loaded event occurs after the template is applied. In Silverlight, the Loaded event is not guaranteed to occur after the template is applied

A workaround for this is also suggested here: http://pagebrooks.com/archive/2008/11/30/tweaking-onapplytemplate-event-timing-in-silverlight-2.aspx

Next: WPF vs. Silverlight - Part 6 - Debug.WriteLine

WPF vs. Silverlight - Part 4 - Animations

See intro blogpost here.

If you want to work with animations programmatically, and modify them as they run, WPF requires you to start and stop the animation with a few extra parameters, to prevent them from being "frozen". Silverlight doesn't freeze elements, so this is not necessary there.

#if SILVERLIGHT 
    myStoryboard.Begin(); 
    myStoryboard.Stop(); 
#else 
    myStoryboard.Begin(element, true); //true allows for changing animation later
     myStoryboard.Stop(element); //element parameter required when Begin was called with element
#endif

Next: WPF vs. Silverlight - Part 5 - XAML Control Instantiation