8 Comments

Here’s a base class for Windows Phone apps which I’ve been using to store user settings. It wraps the IsolatedStorageSettings and it’s a slightly modified version of the SettingsStore-class included in the “A Case Study for Building Advanced Windows Phone Applications – Code Samples”. Here’s an example of how to use the base class (in a Windows Phone 8 app):

    public class MySettingsStore : SettingsStore
    {
        public bool IsFirstAppStart
        {
            get { return this.GetValueOrDefault(true); }
            set { this.AddOrUpdateValue(value); }
        }

        public string FirstName
        {
            get { return this.GetValueOrDefault(""); }
            set { this.AddOrUpdateValue(value); }
        }
    }

The class can be used store more complex objects too, as long as they are serializable:

    [DataContract]
    public class User
    {
        [DataMember]
        public string Name { get; set; }
    }

    public class MySettingsStore : SettingsStore
    {
        public List<User> Users
        {
            get { return this.GetValueOrDefault(new List<User>()); }
            set { this.AddOrUpdateValue(value); }
        }
    }

The GetValueOrDefault-method takes one parameter and this is the default value which is returned if no stored setting is found.

In your app you can store a setting using the following code:

var settingsStore = new MySettingsStore();
...
settingsStore.FirstName = this.FirstNameTextBox.Text;

And to read a setting (or the default value):

var settingsStore = new MySettingsStore();
...
this.FirstNameTextBox.Text = settingsStore.FirstName;

Here’s the full code for the base class SettingsStore. It uses the CallerMemberNameto automatically deduct the key for a setting.

using System;
using System.Collections.Generic;
using System.IO.IsolatedStorage;
using System.Runtime.CompilerServices;

namespace Adafy.Infra
{
    public class SettingsStore
    {
        private readonly IsolatedStorageSettings isolatedStore;

        public SettingsStore()
        {
            this.isolatedStore = IsolatedStorageSettings.ApplicationSettings;
        }

        protected void AddOrUpdateValue(object value, [CallerMemberName]string key = "key")
        {
            var valueChanged = false;

            lock (this)
            {

                try
                {
                    if (value == null)
                    {
                        // Nothing to remove
                        if (!this.isolatedStore.Contains(key))
                            return;

                        this.isolatedStore.Remove(key);
                        this.Save();
                    }

                    // If the new value is different, set the new value.
                    if (this.isolatedStore[key] != value)
                    {
                        this.isolatedStore[key] = value;
                        valueChanged = true;
                    }
                }
                catch (KeyNotFoundException)
                {
                    this.isolatedStore.Add(key, value);
                    valueChanged = true;
                }
                catch (ArgumentException)
                {
                    this.isolatedStore.Add(key, value);
                    valueChanged = true;
                }

                if (valueChanged)
                {
                    this.Save();
                }
            }
        }

        protected T GetValueOrDefault<T>(T defaultValue, [CallerMemberName]string key = "key")
        {
            lock (this)
            {

                T value;

                try
                {
                    value = (T)this.isolatedStore[key];
                }
                catch (KeyNotFoundException)
                {
                    value = defaultValue;
                }
                catch (ArgumentException)
                {
                    value = defaultValue;
                }

                return value;
            }

        }

        private void Save()
        {
            try
            {
                this.isolatedStore.Save();
            }
            catch (Exception)
            {
                return;
            }
        }
    }

}

SettingsStore for Windows Phone 7

CallerMemberName isn’t available in Windows Phone 7. Because of this, each settings must provide an unique key:

    public class MySettingsStore : SettingsStore
    {
        private const string FirstAppStartKey = "MyKey";
        private const string FirstNameKey = "MyKey2";

        public bool IsFirstAppStart
        {
            get { return this.GetValueOrDefault(true, FirstAppStartKey); }
            set { this.AddOrUpdateValue(value, FirstAppStartKey); }
        }

        public string FirstName
        {
            get { return this.GetValueOrDefault("", FirstNameKey); }
            set { this.AddOrUpdateValue(value, FirstNameKey); }
        }
    }

Here’s the full code for WP7 version of the SettingsStore, which is nearly identical to the one provided in Prism.

using System;
using System.Collections.Generic;
using System.IO.IsolatedStorage;
using System.Runtime.CompilerServices;

namespace Adafy.Infra
{
    public class SettingsStore
    {
        private readonly IsolatedStorageSettings isolatedStore;

        public SettingsStore()
        {
            this.isolatedStore = IsolatedStorageSettings.ApplicationSettings;
        }

        protected void AddOrUpdateValue(object value, [CallerMemberName]string key = "key")
        {
            var valueChanged = false;

            lock (this)
            {

                try
                {
                    if (value == null)
                    {
                        // Nothing to remove
                        if (!this.isolatedStore.Contains(key))
                            return;

                        this.isolatedStore.Remove(key);
                        this.Save();
                    }

                    // If the new value is different, set the new value.
                    if (this.isolatedStore[key] != value)
                    {
                        this.isolatedStore[key] = value;
                        valueChanged = true;
                    }
                }
                catch (KeyNotFoundException)
                {
                    this.isolatedStore.Add(key, value);
                    valueChanged = true;
                }
                catch (ArgumentException)
                {
                    this.isolatedStore.Add(key, value);
                    valueChanged = true;
                }

                if (valueChanged)
                {
                    this.Save();
                }
            }
        }

        protected T GetValueOrDefault<T>(T defaultValue, [CallerMemberName]string key = "key")
        {
            lock (this)
            {

                T value;

                try
                {
                    value = (T)this.isolatedStore[key];
                }
                catch (KeyNotFoundException)
                {
                    value = defaultValue;
                }
                catch (ArgumentException)
                {
                    value = defaultValue;
                }

                return value;
            }

        }

        private void Save()
        {
            try
            {
                this.isolatedStore.Save();
            }
            catch (Exception)
            {
                return;
            }
        }
    }
}

The SettingsStore classes could be combined with the help of conditional compilation.

11 Comments

image

The WebBrowser control in Windows Phone 8 has one serious problem: It sometimes forgets to render the HTML. This happens with the NavigateToString-method, Navigate-method works fine. Unfortunately though this problems also affects the Windows Phone 7 apps deployed to Windows Phone 8 phones.

After the repro, the bottom part of this post shows one hack which gets around this problem.

Repro

Here’s a simple repro of the problem. The program can display the HTML page either by navigating to the URL or by downloading the content to string and using the NavigateToString. The same app has been deployed to both the Windows Phone 7 and Windows Phone 8 emulators (this problem is visible also on the real devices).

The code:

public partial class MainPage 
{ 
    private const string url = "http://assets.softwaremk.org/temp/NotWorking.html"; 

    public MainPage() 
    { 
        InitializeComponent(); 
    }

    private async void NavigateStringClick(object sender, RoutedEventArgs e) 
    { 
        var client = new WebClient(); 
        var content = await client.DownloadStringTaskAsync(url);

        WebBrowser.NavigateToString(content); 
    }

    private void NavigateUrlClick(object sender, RoutedEventArgs e) 
    { 
        WebBrowser.Navigate(new Uri(url)); 
    } 
}

The HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
</head>
<body>
    Some content
</body>
</html>

Test results

Windows Phone 7 & Navigate

OK

image

Windows Phone 7 & NavigateToString

OK

image

Windows Phone 8 & Navigate

OK

image

Windows Phone 8 & NavigateToString

BROKEN

image

Observations

The problem seems to be related to the HTML meta tags. The test HTML has two meta tags and if either of them is removed, the page is displayed correctly.

Solution

Here’s a solution which gets around this problem. It’s not beautiful by any means but it seems to do the trick.

  1. Download the HTML content as string
  2. Store it to the IsolatedStorage
  3. Use WebControl to navigate to the isolated storage
var client = new WebClient(); 
var content = await client.DownloadStringTaskAsync(url);

var store = IsolatedStorageFile.GetUserStoreForApplication();

using (var writeFile = new StreamWriter(new IsolatedStorageFileStream("htmlcontent.html", FileMode.Create, FileAccess.Write, store))) 
{ 
    writeFile.Write(content); 
    writeFile.Close(); 
}

var uri = new Uri("htmlcontent.html", UriKind.Relative);

WebBrowser.Navigate(uri);

Result:

image

21 Comments

image Based on the discussion on StackOverflow, I decided to test out the different IconicTileTemplate sizes. The idea was to try to find out:

  1. What are the correct dimensions for images used in the IconicTileTemplate
  2. What image is used when the iconic tile is in wide mode

The test images

I used the free Syncfusion Metro Studio to create the following images (the actual images have a transparent background):

110x110 pixels

image

202x202 pixels

image

132x202 pixels:

image

72x110 pixels:

image

72x72 pixels:

image

The emulator

I used the 720p emulator to run the tests.

Test run #1

image

  • Small tile: 110x110
  • Medium tile: 202x202
  • Support for large tiles enabled

Results:

The medium tile image is used when the tile is in default size:

image

The small tile image is used when the tile is in small size:

image

The medium tile image is used when the tile is in wide mode:

image

When the tile is in wide mode and it has large content set, the small tile image is used:

image

Test run #2

image

  • Small tile: 72x110
  • Medium tile: 132x202
  • Support for large tiles enabled

Results:

The medium tile image is used when the tile is in default size:

image

The small tile image is used when the tile is in small size:

image

The medium tile image is used when the tile is in wide mode:

image

When the tile is in wide mode and it has large content set, the small tile image is used:

image

Test run #3

image

  • Small tile: 72x72
  • Medium tile: 132x202
  • Support for large tiles enabled

Results:

Tile in wide mode, large content set:

image

Conclusion

The first question was: What are the correct dimensions for images used in the IconicTileTemplate?

As the answer on StackOverflow points out, the images used in IconicTileTemplate should have the following sizes:

  • Small: 72x110
  • Medium: 132x202

When we compare the 132x202 and 202x202 images, the difference is quite minimal: The latter one looks maybe more natural (more like the 1st party apps) and it’s smaller, but both the tiles have the correct aspect ratios:

image

The largest difference can be found when the tile is in wide mode and the tile has content. In these situations the Small image is used. Here’s a screenshot again with 110x110 image:

image

And here’s the one with 72x110:

image

And for comparison, this is the one with 72x72:

image

Only the 72x110 looks correct.

The second question was: What image is used when the iconic tile is in wide mode?

The answer depends on whether your tile has content set or not:

  • Without content: The medium tile image is displayed.
  • With content: The small tile image is displayed.

11 Comments

imageOne of the nice additions to the Windows Phone 8 is that the 3rd party apps can now update the phone’s lock screen. The apps can be grouped to two categories based on how they manipulate the lock screen:

  • Lock screen background image providers
  • Lock screen notification providers

In this tutorial we’ll focus on the notification providers. We’ll take an existing Windows Phone 7 app and upgrade it to use the new Windows Phone 8 lock screen features.

The old app

In this tutorial a Windows Phone 7 news reader app is upgraded to show notifications on the lock screen. The app already have live tiles which shows the count of unread news items and the title of the latest article. The app updates the live tile using the following code:

var tile in ShellTile.ActiveTiles.First(); 
var tileData = new StandardTileData() 
{ 
    Count = newNews.Count > 99 ? 99 : newNews.Count, 
    BackTitle = latestNews.Publisher, 
    BackContent = latestNews.Title 
};

tile.Update(tileData);

Upgrading to Windows Phone 8 (and fixing the problems caused by Async CTP 3)

Upgrading our app to Windows Phone 8 happens through the project’s context menu:

image

It’s quite likely that the upgrade goes smoothly and we have a working WP8 app without having to do any changes. Unless we’ve used the Async CTP3, in which case we’re going to see errors like these:

Cannot find all types required by the 'async' modifier. Are you targeting the wrong framework version, or missing a reference to an assembly?

The solution to these is to remove the reference to AsyncCtpLibrary_Phonefrom your projects and to add the package Microsoft.Bcl.Async through NuGet (make sure to select the “Include prereleases”):

image

Adding the lock screen notification capabilities to the app

Now that we’ve upgraded the app to Windows Phone 8, we’re ready to add the lock screen notification capabilities to the app. This requires only few actions:

  1. The WMAppManifest.xml is modified to include the Lock Screen notification capabilities
  2. A new 30x30 icon is added to the project

Modifying the manifest

First step is to modify the app’s manifest file. This happens by manually editing the WMAppManifest.xml. Right click the file (located in project’s Properties-folder) and select Open With – Source Code (Text) Editor:

image

We need to add a new Extensions element into the file and declare the notification capabilities there:

&lt;Extensions&gt; 
&nbsp; &lt;Extension ExtensionName="LockScreen_Notification_IconCount" ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" TaskID="_default" /&gt; 
&nbsp; &lt;Extension ExtensionName="LockScreen_Notification_TextField" ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" TaskID="_default" /&gt; 
&lt;/Extensions&gt;

These should be added just under the Tokens-element:

image

The new icon

In order to show notifications on the lock screen, the app must include a new 30x30 pixel sized icon. The documentation states that the “image must contain only white pixels plus some level of transparency.” So let’s add a new “lockscreen.png” to the root of the project and set it’s Build Action to “Content”:

image

We again have to modify the WMAppManifest.xml to include the new icon. When the app was upgraded to Windows Phone 8, the upgrade process added a new DeviceLockImageURI-property inside the Tokens-element. The lock screen icon is declared there:

<DeviceLockImageURI IsRelative="true" IsResource="false">lockscreen.png</DeviceLockImageURI>

The next step

The next step is to just sit back and enjoy. This feature doesn’t require any code changes.When the app updates the live tile, the lock screen is automatically updated too.

But.

This only happens if the user manually adds the app to the Settings – lock screen – “Choose apps to show quick status”:

image

image

In order for the application to show text notifications on the lock screen (in our case the title of the latest news item), the app must be set to show “detailed status”:

image

Testing

Now only thing left is to test the functionality. The app uses a scheduled agent to update the tile. But as we want to be sure that the lock screen is updated, we need to use the Visual Studio’s “Simulation Dashboard”to turn on the lock screen:

image

From the dashboard we can change the emulator to display the lock screen with a single click:

image

Now we can be sure that the lock screen is receiving its notifications:

image

The text content on the lock screen is the value from the “BackContent” propertyof the StandardTileData. The values from Title or BackTitleare not displayed.

Directing the user to make the app a lock screen application

Adding the support for lock screen notifications is rather easy. But because the feature requires a manual interaction from the user, it can be a good idea to push the user a little further. Instead of just asking them to navigate to the settings – lock screen, the following code automatically moves the user to the correct settings page:

await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings-lock:"));

Secondary tiles and notifications

The lock screen notifications are updated through the main tile.

Conclusion

The new lock screen functionalities of the Windows Phone 8 are a nice addition to the OS. And it doesn’t require much from the developer to add these new capabilities: A few lines of XML and a new icon are all that is required. There’s no code involved.

0 Comments

imageBackground

Keeping the Windows Phone app’s assemblies small is one of the optimization techniques when you want to improve the application’s startup time. As the documentation states:

When you submit your applications to the Windows Phone marketplace, they are signed for verification purposes and the signature is checked each time the application starts. The amount of time it takes to check the assembly signatures increases as the size of the application assemblies increases.

In this tutorial I’ll demonstrate how to split a Windows Phone app into multiple assemblies – which are loaded on-demand, not on startup - when using Caliburn.Micro.

The Problem

If you want to use Caliburn.Micro and you also want to split the app into multiple assemblies, the problems starts with the app’s bootstrapper and IOC. Given the scenario that we have the following assemblies:

  • LoadAssembliesOnDemand – The main assembly, which starts the app.
  • Module1 – Another assembly which will contains views (pages and user controls) and view models
  • Module2 – Another assembly with views and view models

Now if we configure the Caliburn.Micro’s container as we usually do, the code ends up looking like the following:

container.PerRequest<MainPageViewModel>(); 
container.PerRequest<Module1.MyPageViewModel>(); 
container.PerRequest<Module2.AnotherViewModel>();

And here’s the debug output from our app’s startup:

image

As you can see, configuring the container as we did forces the platform to load both the Module1 and Module2 assemblies, even though we don’t need them yet. What we need is a way to configure the IOC container  later, so that the external assemblies are loaded only on-demand.

The Solution

The solution to this problem is to use NuGet to get the following package:

Caliburn.Micro.WP71.Recipes.ExternalModules

This Caliburn.Micro recipe allows you to add assembly-level bootstrappers into your app.

If we get back to our example, with the help of this recipe we can remove all the Module1 and Module2 container initialization code from our main bootstrapper:

container.PerRequest<MainPageViewModel>();

Instead we call the recipe’s Initialize-method. Here’s our bootstrapper’s complete Configure-method:

protected override void Configure() 
{ 
    container = new PhoneContainer(RootFrame); 
    container.RegisterPhoneServices(); 

    container.PerRequest<MainPageViewModel>();

    Caliburn.Micro.WP71.Recipes.ExternalModules.ExternalModuleLoader.Initialize(); 
}

Now we can add the following class into our Module1 project:

public class Bootstrapper : ExternalAssemblyBootstrapper 
{ 
    protected override void ConfigureContainer(PhoneContainer container) 
    { 
        container.PerRequest<MyPageViewModel>(); 
        container.PerRequest<FeaturePageViewModel>(); 
    } 
}

And the following into Module2:

public class Bootstrapper : ExternalAssemblyBootstrapper 
{ 
    protected override void ConfigureContainer(PhoneContainer container) 
    { 
        container.PerRequest<AnotherViewModel>(); 
    } 
}

These bootstrappers are executed only when Caliburn.Micro tries to access the given assembly for the first time. If we now start the app, we can see that Module1 and Module2 aren’t loaded on the startup:

image

Now if we navigate to MyPage.xaml inside the Module1:

navigation.UriFor<MyPageViewModel>() 
    .Navigate();

We can see that the Module1.dll is loaded:

image
Also if add a breakpoint to the Module1’s Bootstrapper, you can see that the code in there is executed. But only once, when you navigate to the page the first time:

image

The bindings between views and view models work as they should:

image

The implementation

The recipe works by making modifications to Caliburn.Micro’s ViewModelLocator and ViewLocator. The modifications gets around the issues caused by Windows Phone platform not allowing the call to Assembly.Load, prohibiting the use of Caliburn.Micro’s built-in SelectAssemblies and AssemblySource capabilities.

There are some limitation with the recipe’s current implementation. For example, it only works with the Caliburn.Micro’s built-in PhoneContainer. Also it expects that the ViewModel class names all end with ViewModel.

Links

The recipe’s source code is available from GitHub. The assembly is available through Nuget.

The example app’s source code is available from the same GitHub repository.