DCSIMG
May 2011 - Posts - Arik Poznanski's Blog

Arik Poznanski's Blog

It CAN be done with .NET

News

MVP

MCC

CodeProject MVP

MCPD

MCTS

Subscribe to my blog by email

Arik Poznanski LinkedIn Profile

Email: arik.com at gmail dot com
or, use this form

Locations of visitors to this page


Sela Group

Sela Canada

DZone MVB

Links

Official Blogs

WPF / SL Blogs

Developers Blogs

May 2011 - Posts

How To Use a .NET 4 Based DLL From .NET 2 Based Application?

Introduction

The official answer is you can’t. Even with the In-Process Side by Side execution (SxS) feature, introduced in .NET 4.

The SxS feature was intended to be used when COM is involved. For example, if you got an application that loads plugins, like outlook, and it loads 2 COM plugins, one is using .NET 4 and the other is using .NET 2.0 then it will load two versions of the CLR into the process using the new SxS feature.

What if I simply have a .NET 2 application or DLL that needs to access a .NET 4 DLL?

Personally I’ve encountered two scenarios when I had to solve this problem:

  1. I had a 3rd-party control that would load only in a .NET 3.5 application, but I had to use it in a .NET 4 application.
  2. I wanted to write a plug-in for Windows Live Writer, which must use .NET 2.0, but I needed to use in my plug-in a .NET 4 DLL.

So, what can we do if no COM is involved?

Well, simply add COM to the mixture..

The idea is that you can expose the required classes from your DLL (which uses .NET Framework X) as COM classes (using COM Interop), and then use those classes from your other DLL (which uses .NET Framework Y). Since you are crossing a COM interface, in-process SxS will kick in and work its magic.

Steps to work around the problem

Create a .NET 4 DLL

Suppose we have a .NET 4 DLL which does some .NET 4 functionality. In the attached example our .NET 4 class prints the CLR version, which should be 4. This DLL is compiled with .NET Framework 4.

using System;

namespace Net4Assembly
{
    public class MyClass
    {
        public void DoNet4Action()
        {
            Console.WriteLine("CLR version from DLL: {0}", Environment.Version);
        }
    }
}

 

Create a .NET 2 EXE

Here we create a .NET 2 EXE which will eventually call the .NET 4 DLL, currently all it does is write it’s own CLR version.

using System;

namespace Net2Assembly
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("CLR version from EXE: {0}", Environment.Version);
        }
    }
}

 

Create a .NET 4 to .NET 2 adapter

Here we create a .NET 4 DLL that exposes the same functionality we need from our original .NET 4 DLL only it exposes it in a COM-friendly way. In this example, it only needs to delegate the call to the original implementation, but in more advanced scenarios it should translate the parameters to something more COM friendly. In addition to changing the parameters the classes also implement interfaces (as required by COM) and are marked with ComVisible and Guid attributes to allow access using COM.

Here is our COM visible interface:

using System;
using System.Runtime.InteropServices;

namespace Net4ToNet2Adapter
{
    [ComVisible(true)]
    [Guid("E36BBF07-591E-4959-97AE-D439CBA392FB")]
    public interface IMyClassAdapter
    {
        void DoNet4Action();
    }
}

And our COM visible class, which delegates its calls to the original class.

using System;
using System.Runtime.InteropServices;
using Net4Assembly;

namespace Net4ToNet2Adapter
{
    [ComVisible(true)]
    [Guid("A6574755-925A-4E41-A01B-B6A0EEF72DF0")]
    public class MyClassAdapter : IMyClassAdapter
    {
        private MyClass _myClass = new MyClass();

        public void DoNet4Action()
        {
            _myClass.DoNet4Action();
        }
    }
}

Note: we could have combined Net4Assembly.MyClass and Net4ToNet2Adapter.MyClassAdapter into the same class but I wanted to keep the example general. In real life application you often can’t change the original object and thus you are forced to create a wrapper.

 

Add support to the adapter for registration-free COM activation

Important note: this part is not really necessary for the .NET 4 to .NET 2 interop to work. But without it you will need to start using he registry for registering your .NET COM components and most projects would rather to avoid it if possible. If this is not a problem just register your objects in the registry and move to the next step.

To add support for registration-free COM we need to create two application manifest files.

The first application manifest specifies dependent assemblies for the client executable. Note that since this manifest replaces the default .NET manifest I’ve added some extra standard manifest stuff (trustinfo), but only the first part is really needed for the registration-free COM to work. To add it, add a file to the client project named app.manifest (“Add new item” –> “Application Manifest”) and change the project properties to use this file.

Following is the content of app.manifest for the Net2Aseembly.exe client in our example:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly
  xmlns="urn:schemas-microsoft-com:asm.v1"
  manifestVersion="1.0">
  <assemblyIdentity
    type = "win32"
    name = "Net2Assembly"
    version = "1.0.0.0"
    />
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Net4ToNet2Adapter"
        version="1.0.0.0" />
    </dependentAssembly>
  </dependency>

  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <!-- UAC Manifest Options
            If you want to change the Windows User Account Control level replace the
            requestedExecutionLevel node with one of the following.

        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />

            If you want to utilize File and Registry Virtualization for backward
            compatibility then delete the requestedExecutionLevel node.
        -->
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>

 

The second application manifest, describes the COM components which are exposed in the assembly. It needs to be set as the application manifest which resides as a native Win32 resource inside the DLL. 

Unfortunately, this can’t be done as easily as the previous manifest. In Visual Studio 2010, the relevant field in the project properties is disabled when the project is of type Class Library. So we must go to the Net4ToNet2Adapter.csproj file and change it ourselves. The change is easy, just add the following lines in the relevant place:

<PropertyGroup>
  <ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>

Following is the content of app.manifest for the Net4ToNet2Adapter.dll in our example:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="Net4ToNet2Adapter"
    version="1.0.0.0" />
  <clrClass
    clsid="{A6574755-925A-4E41-A01B-B6A0EEF72DF0}"
    progid="Net4ToNet2Adapter.MyClassAdapter"
    threadingModel="Both"
    name="Net4ToNet2Adapter.MyClassAdapter"
    runtimeVersion="v4.0.30319"
    />
</assembly>

 

Use our .NET 4 DLL via COM

Now all you need to do is create an instance of your .NET 4 class from your .NET 2 executable using COM:

using System;
using Net4ToNet2Adapter;

namespace Net2Assembly
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("CLR version from EXE: {0}", Environment.Version);
            
            Type myClassAdapterType = Type.GetTypeFromProgID("Net4ToNet2Adapter.MyClassAdapter");
            object myClassAdapterInstance = Activator.CreateInstance(myClassAdapterType);
            IMyClassAdapter myClassAdapter = (IMyClassAdapter)myClassAdapterInstance;
            myClassAdapter.DoNet4Action();
        }
    }
}

Note: Since the interface IMyClassAdapter should be duplicated in the client, I’ve added the source file IMyClassAdapter.cs as a link to the client project.

 

The result

The result of running this simple console application is:

image

I think the image speaks for itself.

I’ve uploaded the full source of this example to MSDN Code Gallery.

That’s it for now,
Arik Poznanski.

A Customizable WPF MessageBox

Recently I came across the need to customize the look of the standard message box in the application. To do this I’ve decided to create a new class named WPFMessageBox, which will have the same interface as the standard one, will behave the same way and finally would be fully customizable, the WPF way, using control templates.

Following is the result of this effort.

image

Note: In the image you can see an example for a message box customization. Of course you are limited only by your imagination. The default control template looks like the standard message box.

Features

  1. The class WPFMessageBox has the exact same interface as the current WPF MessageBox class.
  2. Implemented as a custom control, thus fully customizable via standard WPF control templates.
  3. Has a default control template which looks like the standard MessageBox.
  4. Supports all the common types of message boxes: Error, Warning, Question and Information.
  5. Has the same “Beep” sounds as when opening a standard MessageBox.
  6. Supports the same behavior when pressing the Escape button as the standard MessageBox.
  7. Provides the same system menu as the standard MessageBox, including disabling the Close button when the message box is in Yes-No mode.
  8. Handles right-aligned and right-to-left operating systems, same as the standard MessageBox.
  9. Provides support for setting the owner window as a WinForms Form control.

 

How to use it?

Using the WPFMessageBox class is (exactly) as simple as presenting a standard MessageBox.

Instead of writing:

MessageBoxResult result = MessageBox.Show("message text", "caption", MessageBoxButton.YesNoCancel, MessageBoxImage.Error);

simply write:

MessageBoxResult result = WPFMessageBox.Show("message text", "caption", MessageBoxButton.YesNoCancel, MessageBoxImage.Error);

All of the methods of the original MessageBox are supported:

public static class WPFMessageBox
{
    public static MessageBoxResult Show(string messageBoxText);
    public static MessageBoxResult Show(string messageBoxText, string caption);
    public static MessageBoxResult Show(Window owner, string messageBoxText);
    public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button);
    public static MessageBoxResult Show(Window owner, string messageBoxText, string caption);
    public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon);
    public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button);
    public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult);
    public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon);
    public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult, MessageBoxOptions options);
    public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult);
    public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult, MessageBoxOptions options);
}

 

How to customize it?

The main point of creating this WPF message box was to customize it, right? so I’d better show you how this is done.

To customize the WPFMessageBox we use the standard control templates way:

<Style TargetType="msgbox:WPFMessageBoxControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type msgbox:WPFMessageBoxControl}">
                <!-- your new template here -->
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

where the msgbox prefix is defined as follows:

xmlns:msgbox="clr-namespace:MessageBoxUtils;assembly=WPFMessageBox"

Of course, you can use Blend to do the customizations.

Can I see a real example of such customization?

Sure, following is the control template for the message box you see in the image. Note the binding you need to use to properly set the visibility of the buttons and the image.

<Style TargetType="{x:Type msgbox:WPFMessageBoxControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type msgbox:WPFMessageBoxControl}">
                <Grid Background="LightBlue" FlowDirection="{Binding ContentFlowDirection}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="55" />
                        <RowDefinition Height="2" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="40" />
                        <RowDefinition Height="55" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="60" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>

                    <TextBlock Text="{Binding Message}" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="2" TextWrapping="Wrap" TextAlignment="Left" HorizontalAlignment="{Binding ContentTextAlignment}" VerticalAlignment="Top" Margin="10 10 10 10" />

                    <Border Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Background="Yellow">
                        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 5 0" >
                            <Button Content="_Yes" Visibility="{Binding YesNoVisibility}" Command="{Binding YesCommand}" IsDefault="{Binding IsYesDefault}" Margin="5 5 5 5" Height="24" Width="80" />
                            <Button Content="_No" Visibility="{Binding YesNoVisibility}" Command="{Binding NoCommand}" IsDefault="{Binding IsNoDefault}" Margin="5 5 5 5" Height="24" Width="80" />
                            <Button Content="O_K" Visibility="{Binding OkVisibility}" Command="{Binding OkCommand}" IsDefault="{Binding IsOkDefault}" Margin="5 5 5 5" Height="24" Width="80" />
                            <Button Content="_Cancel" Visibility="{Binding CancelVisibility}" Command="{Binding CancelCommand}" IsDefault="{Binding IsCancelDefault}" Margin="5 5 5 5" Height="24" Width="80" />
                        </StackPanel>
                    </Border>
                    
                    <StackPanel Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal">
                        <Image Source="{Binding MessageImageSource}" HorizontalAlignment="Left" VerticalAlignment="Center" Height="32" Width="32" Margin="10 0 0 0" />
                        <Image Source="{Binding MessageImageSource}" HorizontalAlignment="Left" VerticalAlignment="Center" Height="32" Width="32" Margin="10 0 0 0" />
                        <Image Source="{Binding MessageImageSource}" HorizontalAlignment="Left" VerticalAlignment="Center" Height="32" Width="32" Margin="10 0 0 0" />
                        <Image Source="{Binding MessageImageSource}" HorizontalAlignment="Left" VerticalAlignment="Center" Height="32" Width="32" Margin="10 0 0 0" />
                        <Image Source="{Binding MessageImageSource}" HorizontalAlignment="Left" VerticalAlignment="Center" Height="32" Width="32" Margin="10 0 0 0" />
                        <Image Source="{Binding MessageImageSource}" HorizontalAlignment="Left" VerticalAlignment="Center" Height="32" Width="32" Margin="10 0 0 0" />
                    </StackPanel>
                </Grid>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

 

Where can I get it?

I’ve uploaded the source to a new CodePlex project named WPF MessageBox.

That’s it for now,
Arik Poznanski.

Sela Haifa Open House: Windows 7 features and Multi-Touch Session – Slide Deck and Demos

Today (24.05.2011) I’ve delivered two sessions about “Using Windows 7 Features in .NET applications” in Sela Haifa Open House which was presented in Philips building in Haifa.

Dear Philips (and other) developers, I had a great time presenting today's Windows 7 sessions. Thank you for coming!

 

In the sessions I talked about how to use new Windows 7 features like the new Taskbar, sensors API, the Ribbon control supplied with Windows 7, Application restart and Recovery and plenty of Multi-Touch.
We’ve seen numerous examples, mainly in C++ Win32, WinForms and WPF.

You can download both the presentations and the demos here.

That’s it for now,
Arik Poznanski.

kick it on DotNetKicks.com Shout it

Sela Developer Days 2011 – Save the Date!

Sela Technology Center is proud to present Sela Developer Days 2011 which will be held on June 26-30.

image_thumb_42F339DB

One stage - hottest topics - Israel and world lead experts.
A rare opportunity to hear all about the next big things in our technological world.

  • 25 full-day workshops
  • 15 lead experts
  • 5 days

You can find the full agenda on the conference website.

Although I recommend ALL the lectures, I believe my blog readers will most enjoy the following workshops:

See you there!

That's it for now,
Arik Poznanski.

.NET User Group: Windows 7 features and Multi-Touch Session – Slide Deck and Demos

Yesterday (18.05.2011) I’ve delivered two sessions about “Using Windows 7 Features in .NET applications” in the Israeli .NET User Group, at Microsoft offices in Ra’anana.

I want to thank all the attendees who came to hear the sessions, I truly enjoyed it.

In the sessions I talked about how to use new Windows 7 features like the new Taskbar, sensors API, the Ribbon control supplied with Windows 7, Application restart and Recovery and plenty of Multi-Touch.
We’ve seen numerous examples in C++ Win32, C# WinForms, C# WPF and C# Silverlight.

Since we were a little short on time, I couldn’t answer everyone's questions. So if you got an unanswered question, feel free to contact me by mail (find it on my blog left panel) or using the blog contact form.

You can download both the presentations and the demos here.

That’s it for now,
Arik Poznanski.

Speed up Visual Studio Builds

Recently I got involved in a big project where we had a single solution with approximately 100 projects.

Why 100 Projects in a Solution?

The reason for a 100 projects solution is that like in many modular systems these days, we have the following three tiers:

  1. A few core / common projects every project will use.
  2. A large amount of modules, independent of each other. This tier directly depends on tier 1.
  3. A few end-projects which loads the different modules. This tier indirectly depends on tier 2.

image

So, yes, we could create several solutions with each tier compiling only when needed and using DLL reference instead of project references, but the amount of changes in all the tiers was still large enough and I’ve already seen this kind of build process fail miserably. So this was no go.

Build time took 15 minutes for the whole solution. Since we enforced a gated check-in policy in the company, this was really a pain point for the developers.

Note that the developers’ computers were strong enough, with 8GB ram, Intel Core i7 CPU and SSD disks.

So I’ve started investigating what can be done to improve the situation.

Step 1: Build Projects in Parallel

Although the PC has 8 logical cores, the build system in Visual Studio 2010, when using C#, still uses only a single core! (Note this is not the case in C++ build system).

So after browsing the web, I’ve found how you can manually trigger msbuild yourself as a Visual Studio external tool to compile your solution in parallel.

image

More details on how to set this up can be found in the following post by Scott Hanselman: http://www.hanselman.com/blog/HackParallelMSBuildsFromWithinTheVisualStudioIDE.aspx

After setting this up I’ve got an approximate build time of 10.2 minutes. Not bad for a few minutes of work! Also, got the following beautiful image out of my CPUs, where you can really see them at work:

image

Step 2: Beware of Copy Local = True

In our 100-projects solution lots of projects reference each other, obviously. In addition to these references we also reference several 3rd party components, practically from each module.

All the above caused that whenever we would compile the solution, over 4.5 GB of files were written. The majority (95%) of the writes were DLLs which were copied to the output folder of each project.

To check out how many writes are done in your compilation check out this post.

Anyway, 4.5 GB takes a long time to write, even on an SSD drive.

So the next step was to eliminate those writes. To do this, we changed almost all of the “Copy local” settings in all the referenced DLLs from the default True to False. This will prevent the referenced DLLs to be copied to the output folder of each project.

image

Note: some file names were blacked to protect the client’s properties.

In addition we also changed the output folder of all the projects to a single folder, so all generated DLLs are copied to the one and only place where we actually run them. Doing so dropped the writes while compiling to under 200 MB, a huge time saver. Specifically, build time dropped to 7.5 minutes!

Step 3: Use RAM Disk

A RAM disk is a logical disk which resides entirely on the RAM.
It is extremely fast (faster than any SSD), but it is erased on every power-down, so only use it for temporary files.

Of course, you should have enough RAM to spare for this disk (the memory is pre-allocated for the disk use only), but on an 8GB PC, it’s usually not an issue.

There are several programs you can use to set up a RAM disk. I used DataRam RamDisk which supplies a free version with the ability to create a RAM disk up to 4 GB (1 or 2 GB should be sufficient for any build).

Configuration of the RAM disk is very easy:

image

After you download, install and format your new RAM disk, you can move your single output folder to it. If you want to keep your output folder in the same build drive you can simply create a symbolic link between the current output folder and a folder on the RAM disk. This way makes the using of the RAM disk optional, only for users with sufficient memory.

To create a symbolic link between your build folder and your new RAM disk folder use the following line:

mklink /D C:\Dev\MyCurrnetBuildFolder\Source\bin R:\bin

where R: is your RAM disk folder.

Note: you should change the path according to your build folder and your RAM drive settings.

Result: Adding the RAM disk reduced compilation time to less than 5 minutes! This is a 66% reduction of the original time!

Summary

In this post we’ve seen how you can decrease build time. Of course, I can’t make any guarantees. Every project has its own characteristics and problems, but the steps provided can probably reduce the build time if you’re fit the profile of standard line-of-business application.

That’s it for now,
Arik Poznanski.

Coming session: Using Windows 7 Features in .NET Applications, at Sela Haifa in Israel

In case you missed one of my previous Windows 7 sessions, here is your chance to catch it.

On May 24th I’ll be giving two sessions about “Using Windows 7 Features in .NET Applications”, as part of Sela Haifa Open House.

On the first session “The 7 Show: From XP to Win 7” we will talk about new features in Windows 7 that will modernize your application.
We will learn how to work with the new Taskbar, how to work with Sensors, how to use the Ribbon interface and more.
The session will be rich with examples in .NET, WPF and WinForms.

On the second session “Touch Everywhere” we will see how to use the Multi-touch feature which comes out of the box in Windows 7.
We will learn about the possible level of support (Good, Better & Best) and learn about helper libraries that provide a convenient API when working with multi-touch devices.
The session will include many examples in all the common UI technologies: C++, WinForms, WPF and Silverlight.

The entrance is free, but requires registration.

Hope to see you there!

That’s it for now,
Arik Poznanski.

How to Check How Many Writes Are Done While You Build?

In the following post I’ll show you how you can measure how many read and writes (in bytes) your build takes. This may prove useful since the amount of writes can have a tremendous effect on the build total time. More on how to shorten your build time in a future post.

Step 1: Download Process Monitor

The key for measuring the build read and writes is to download the great tool by Sysinternals, Process Monitor.

Process Monitor is a tool which monitors all the processes in your system for several activities, like: file-access, registry, etc. It has rich support for filtering and summarizing the information.

image

Step 2: Setup a filter on your build process

Select the “Filter” menu and then “Filter…” menu item.
Now add a new filter with “Process Name” is “devenv.exe”, or “msbuild.exe”, depending on how you build your project.

image

Also disable all activities but the “file system” activity, since we want to focus on these alone.

image

Step 3: Run your build process

Clear the captured data from the process monitor to get a clean screen.

image

Now invoke your build process with your favorite tool: Visual Studio or msbuild.

Step 4: Get the results

Select the “Tools” menu and then “File Summary…” menu item.

image

Note: some file names were blacked to protect the client’s properties.

Step 5: Check by folder

Present the file summary by folders to get a better understanding on which folders you get most of the writes.

image

This is it. Here you can already see exactly how many read and writes you are doing and also how much time was invested in those writes (check the File Time column).

In a future post I’ll cover how you can decrease build time using this information.

That’s it for now,
Arik Poznanski.

Coming session: Using Windows 7 Features in .NET Applications, at .NET User Group in Israel

On May 18th I’ll be giving two sessions about “Using Windows 7 Features in .NET Applications”, as part of the monthly .NET User Group sessions, in Microsoft offices, Ra’anana, Israel.

On the first session “The 7 Show: From XP to Win 7” we will talk about new features in Windows 7 that will modernize your application.
We will learn how to work with the new Taskbar, how to work with Sensors, how to use the Ribbon interface and more.
The session will be rich with examples in .NET, WPF and WinForms.

On the second session “Touch Everywhere” we will see how to use the Multi-touch feature which comes out of the box in Windows 7.
We will learn about the possible level of support (Good, Better & Best) and learn about helper libraries that provide a convenient API when working with multi-touch devices.
The session will include many examples in all the common UI technologies: C++, WinForms, WPF and Silverlight.

The entrance is free, but requires registration.

Hope to see you there!

That’s it for now,
Arik Poznanski.

Managing Tombstone State in a Windows Phone 7 Application

Before we continue, I trust that you know exactly the meaning of tombstone state in a Windows Phone 7 application. If this is not the case, simply read Yochay’s great posts on the application execution model (Part 1, Part 2, Part 3).

Every Windows Phone 7 application needs to manages tombstoning correctly to provide a better user experience and pass the application certification requirements.

When your application goes into tombstone state, it is your responsibility to save all the application and page state, so that when the page gets reloaded it can recover properly and reload the previous data. This was the user doesn’t even realizes that the application process got terminated and reloaded.

To ease the handling of the tombstone state I’ve created a simple StateManager class which helps serializing the data when going into tombstone state and deserializing the data when you get back.

How to Use StateManager?

Suppose your current page has the following data members you want to save across tombstoning:

  • LastPosts
  • Posts
  • Comments
  • Images

Now all you need to do is to save them when the application goes into tombstone state and reload them when it gets back from tombstone state.

Saving the Data

The proper place for the code that saves the data resides in the OnNavigatedFrom method. In this method you should call the helper extension method SaveState for each member of the page that you want to save, passing the value and the key on which to save it:

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    this.SaveState("LastPostsKey", LastPosts);
    this.SaveState("PostsKey", Posts);
    this.SaveState("CommentsKey", Comments);
    this.SaveState("ImagesKey", Images);
}

Loading the Data

The proper place for the code that loads the data when returning from tombstone state resides in OnNavigatedTo method. In this method you should call the helper extension method LoadState for each member of the page that you want to load, passing the key related to the member’s value:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    LastPosts = this.LoadState<ObservableCollection<RssItem>>("LastPostsKey");
    Posts = this.LoadState<ObservableCollection<RssItem>>("PostsKey");
    Comments = this.LoadState<ObservableCollection<RssItem>>("CommentsKey");
    Images = this.LoadState<ObservableCollection<ImageItem>>("ImagesKey");
}

Definition of StateManager

So how is the StateManager class defined? It is as simple as the following two extension methods:

/// <summary>
/// State Manager
/// </summary>
public static class StateManager
{
    /// <summary>
    /// Saves a key-value pair into the state object
    /// </summary>
    /// <param name="phoneApplicationPage">The phone application page.</param>
    /// <param name="key">The key.</param>
    /// <param name="value">The value.</param>
    public static void SaveState(this PhoneApplicationPage phoneApplicationPage, string key, object value)
    {
        if (phoneApplicationPage.State.ContainsKey(key))
        {
            phoneApplicationPage.State.Remove(key);
        }

        phoneApplicationPage.State.Add(key, value);
    }

    /// <summary>
    /// Loads value from the state object, according to the key.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="phoneApplicationPage">The phone application page.</param>
    /// <param name="key">The key.</param>
    /// <returns>The loaded value</returns>
    public static T LoadState<T>(this PhoneApplicationPage phoneApplicationPage, string key)
        where T : class
    {
        if (phoneApplicationPage.State.ContainsKey(key))
        {
            return (T)phoneApplicationPage.State[key];
        }

        return default(T);
    }
}

Note: this code was first published as part of the “Using Pivot and Panorama Controls” lab found in the Windows Phone Training Kit for Developers, which I wrote for Microsoft.

That’s it for now,
Arik Poznanski.