DCSIMG
February 2012 - Posts - Alex Golesh's Blog About Silverlight Development

February 2012 - Posts

Windows 8 Consumer Preview and Visual Studio 11 Beta – Process Lifetime Management (Part 11/11)

Windows 8 Metro applications introduces new (for desktop version of Windows) philosophy behind application execution - when application runs and when it is terminated. In this post I will overview this process also known as Process Lifetime Management (or PLM in short). In addition, I will showcase creation of custom Splash Screen experience which becomes important especially with in PLM in mind.

Windows 8 designed to run not only on desktop PCs and laptops (which usually have descent amount of RAM and CPU resources), but also on tablets and low-powered PCs (with limited amount or RAM, slower CPU and usually running from battery). New Windows 8 Metro applications are full-screen applications (most of the time), meaning that for given time only one (in some scenarios, like snapping – two) application interacts with user. With this in mind, it is simply not optimal to have more than one active application; applications which are not in foreground can be “suspended”. For us, developers, the experience is like pausing application on break point in Visual Studio’s debugger: the application is alive, all memory (and thus all variables and objects) preserved, but it is not executed. Such applications keep holding memory resources but will not run any process meaning will not waste CPU resource (which leads to lower battery drain).

The application lifecycle

So what really happens? When user launches the application (I will explain the “launch” term more in depth later) it begins invocation, shows Splash Screen (as defined in application manifest) and have only 15 seconds to present a window to the user.

Note: If application is not capable with this requirement it will be terminated (crashed). I will overview the ways to work around this behavior in Custom Splash Screen section.

When user launches additional application, first application being moved to background – it gets “suspended” signal and have 5 seconds to save its state. After saving state application remains in-memory, but not receiving CPU allocation and other system resources. If user returns to the application it resumes immediately, since it preserved in-memory and it is just a matter of continue running from previous time (remember Tombstoning and Fast Application Switch/FAS in Windows Phone 7?). Application gets “resuming” signal in order to restore its previous state.

Note: If application is not capable of finish saving state with 5 seconds from receiving “suspend” signal it will be terminated (crashed).

At some point of time there will be too much applications “suspended” in memory. In low-memory devices it will happen more frequently, on powerful desktop PCs probably less frequently but also happen. In this case new application launch will cause to system look for oldest and most memory-consuming suspended application and will remove it from memory or in other words terminate the app. Since application is not running it will not get any notification about the termination.

Note: Application must use “suspend” signal save its state and prepare for potential termination.

If user launches the terminated application again it will follow full application initialization process as it was during first launch.

In order to support this behavior, application provided by Suspending and Resuming events which can be registered and handled:

public App()
{
    this.InitializeComponent();
    this.Resuming += App_Resuming;
    this.Suspending += App_Suspending;
}
void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
    //Get deferral opreation
    SuspendingDeferral deferral = e.SuspendingOperation.GetDeferral();
    //Save state - 5 seconds only!

//...

    Windows.Storage.ApplicationData.Current.LocalSettings.Values.Add("SUSPENDED", DateTime.Now.ToString());
    System.Diagnostics.Debug.WriteLine("Suspending the app");
    //Notify complete with 5 seconds - otherwise the app will be crashed
    deferral.Complete();
}
void App_Resuming(object sender, object e)
{
    System.Diagnostics.Debug.WriteLine("Resuming app");
    //Reload state if resuming
    //...
    DateTime suspendedAt = DateTime.Parse(Windows.Storage.ApplicationData.Current.LocalSettings.Values["SUSPENDED"].ToString());     Windows.Storage.ApplicationData.Current.LocalSettings.Values.Remove("SUSPENDED");     System.Diagnostics.Debug.WriteLine("Originally suspended at " + suspendedAt.ToString());
}
SuspendingDeferral is class responsible to managing suspending operation. If application has nothing to save in its state (rarely occurred), it can just safely skip getting the deferral object or simply get it and immediately report Complete.

To debug Suspend/Resume/Terminate behavior VS11 provides Debug Location menu, which can be added to the menus bar:

image

image

How application lunched?

In Windows 8 application can be executed using multiple possible actions: user taps on application’s tile on main screen (or in all applications list), invokes search with the application, shares data with application, etc. Also when launching application from main screen tile, it can be first time launch or launch after application was suspended and terminated (as described above).  To deal with this, application provided with WinRT provides number of different overrideable functions at Application level – OnLauched and OnActivated (and all On_XXX_Activated).

OnLaunched event invokes when the application is launched normally by user and provides LaunchActivatedEventArgs which helps to deal with different possible launch conditions (launch kind, previous execution state, arguments, etc.).

LauchActivatedEventArgs.Kind helps manage different application launch scenarios depending on how it was launched:

image

LauchActivatedEventArgs.PreviousExecutionState helps to deal with situation when application was previously terminated/suspended to help create illusion of continues execution:

image

OnActivated (and other activated handlers like OnShareTargetActivated, OnFileActivated, OnSearchActivated, OnFileOpenPickerActivated and others) invoked when application activated either via file picker experience, share or search charm and provides relevant arguments for specific activation (like ShareOperation in case of share target activation, or search parameters in case of search activation) along with ActivationKing and PreviousExecutionState.

Custom Splash Screen

When application Activated or Launched it has only 15 seconds to present a windows. During this time system presents user with pre-defined Splash Screen. What if application needs potentially more time (for example getting data from cloud-based services)? What if application developer decides to provide some information about loading process? All those scenarios requires some custom splash screen (with potentially custom logic). But what happens with built-in one? How to deal with it?

For this scenarios OnLaunched and OnActivated events provide information about SplashScreen which used to get information about system-managed splash screen and provide seamless alternative with custom splash screen. In my sample I created custom splash screen, which “entertains” user for some time simulating real process behind the scene.

Let’s see the code. I created additional page, called ExtendedSplash which uses same image as declared in application manifest and have some additional elements to present text and waiting experience.

The page initialized as follows:

private SplashScreen splash;
private bool dismissed = false;
string activationArgs = null;
int count = 0;
string[] msgs = { "Loading...", 
                    "Please wait...", 
                    "Progressing really well...", 
                    "Just few more blinks...", 
                    "Finally! Done!" };
//Ctor
public ExtendedSplash(SplashScreen splashScreen, bool dismissed, string activationArgs)
{
    this.InitializeComponent();
    //Save system copy of splash screen for future reference and activation args
    splash = splashScreen;
    this.dismissed = dismissed;
    this.activationArgs = activationArgs;
    //move/resize local image exactly as on system splash screen
    extendedSplashImage.SetValue(Canvas.LeftProperty, splash.ImageLocation.X);
    extendedSplashImage.SetValue(Canvas.TopProperty, splash.ImageLocation.Y);
    extendedSplashImage.Height = splash.ImageLocation.Height;
    extendedSplashImage.Width = splash.ImageLocation.Width;
    //subscribe to resize event to handle new size of splash screen while active
    Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);
    //Simulate work by running timer...
    DispatcherTimer timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromSeconds(1);
    timer.Tick += (s, args) =>
    {
        count++;
        if (count < msgs.Length)
        {
            //Present next message to user to show some progress
            txtMessage.Text = msgs[count];
        }
        else if (count >= msgs.Length && this.dismissed)
        {
            timer.Stop();
            timer = null;            // When finished, navigate to real first page
            var rootFrame = new Frame();
            rootFrame.Navigate(typeof(BlankPage), activationArgs.Length == 0 ? null : activationArgs);
            // Place the frame in the current Window and ensure that it is active
            Window.Current.Content = rootFrame;
        }
    };
    timer.Start();
    txtMessage.Text = msgs[count];
}

When screen being resized:

void ExtendedSplash_OnResize(Object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
    // Safely update the splash screen image coordinates
    if (null != splash)
    {
        // Re-position the extended splash screen image due to window resize event.
        this.extendedSplashImage.SetValue(Canvas.LeftProperty, splash.ImageLocation.X);
        this.extendedSplashImage.SetValue(Canvas.TopProperty, splash.ImageLocation.Y);
        this.extendedSplashImage.Height = splash.ImageLocation.Height;
        this.extendedSplashImage.Width = splash.ImageLocation.Width;
    }
}

Finally, provide event handler function, which will be used in App class to subscribe for system splash screen dismiss event:

internal void dismissedEventHandler(Windows.ApplicationModel.Activation.SplashScreen sender, object e)
{
    this.dismissed = true;
}

In App class, redirect navigation to custom splash screen instead of real main screen:

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    SplashScreen splashScreen = args.SplashScreen;
    ExtendedSplash eSplash = new ExtendedSplash(splashScreen, false, args.Arguments);
    splashScreen.Dismissed += new TypedEventHandler<SplashScreen, object>(eSplash.dismissedEventHandler);
    Window.Current.Content = eSplash;
    Window.Current.Activate();
}

Running the app reveals seamless integration between system provided and custom splash screens:

image 

Note: In my case I decided to provided system splash screen for OnActivated experience, but same technique applies also on application activation

Working extended splash screen video:

Custom Splash Screen

That’s it for now.

Stay tuned for more interesting stuff to come!

Alex



                    
                

Windows 8 Consumer Preview and Visual Studio 11 Beta – Live tiles, toasts, badges and Push Notifications (Part 10/11)

Windows 8 provides interesting way to “interact” with user even when application is not active anymore.

 

Push Notifications

Windows Push Notifications (WNS) in Windows 8 is quite similar to the Windows Phone 7 Push Notification (MPNS) model. It uses cloud-based push notification services to deliver notifications to registered clients.

To enable application receiving push notifications developer must register it at Windows Push Notifications & Live Connect site. The process is very simple – developer provides Package display name and publisher found in application manifest:

image

and site provides with updated package name (looks like BUILD.XXXXXXXX-XXXX-XXXX-XXXX), client secret code and package security identifier (SID, looks like ms-app://s-1-15-2-2289547533-2580522492-1560851697-4285800683-875472074-159972757-1111111111). Developer must modify package name in application manifest. Created applications can be managed Live App management site and retrieve information about any registered application, usage, caps, etc.

image

After application registered and ready to receive notifications from WNS, let’s overview how the whole process works:

image

1. Metro style application uses WinRT APIs (Notification Client Platform on diagram) to open new channel to server (or reconnect with previously created channel). At the end of the process, application receives unique URI which will be used in next step.

2. Any 3rd party application (Metro, classic, WPF, cloud-based service), which has URI obtained in previous step + client secret/SID combination can request Authorization token from WNS and then POST message to provided URI using received authorization token.

3. 3rd party application (in my simplest case – console app) requests authorization token using client secret and SID, prepares XML payload (the message which WNS sends to client) and sends POST message to URI (from 1st step)

4. WNS verifies provided information, alters Windows 8 client machine and delivers the message payload to client notification platform. Client notification platform responsible on updating tile/badge, showing toast notification, updating lock screen (if application has lock screen access) and deliver notifications to Metro app if still active.

Now let’s see the application described in step 2. It has 2 main functions – GetAccessToken and Push:

private static Stream GetAccessToken(string sid, string secret)
{
    string url = "https://login.live.com/accesstoken.srf";
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    string content = "grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com";
    string data = string.Format(content, sid, secret);
    byte[] notificationMessage = Encoding.Default.GetBytes(data);
    request.ContentLength = notificationMessage.Length;
    using (Stream requestStream = request.GetRequestStream())
    {
        requestStream.Write(notificationMessage, 0, notificationMessage.Length);
    }
    var response = (HttpWebResponse)request.GetResponse();
    Stream result = response.GetResponseStream();
    return result;
}
private static HttpStatusCode Push(string pushUri, string accessToken, string type, string notificationData)
{
    var subscriptionUri = new Uri(pushUri);
    var request = (HttpWebRequest)WebRequest.Create(subscriptionUri);
    request.Method = "POST";
    request.ContentType = "text/xml";
    request.Headers = new WebHeaderCollection();
    request.Headers.Add("X-WNS-Type", type);
    request.Headers.Add("Authorization", "Bearer " + accessToken);
    byte[] notificationMessage = Encoding.Default.GetBytes(notificationData);
    request.ContentLength = notificationMessage.Length;
    using (Stream requestStream = request.GetRequestStream())
    {
        requestStream.Write(notificationMessage, 0, notificationMessage.Length);
    }
    var response = (HttpWebResponse)request.GetResponse();
    return response.StatusCode;
}

The functions are pretty simple – first returns stream with Authorization token, second uses URI obtained in step 1 (see diagram) + authorization token + notification type (set as custom header, X-WNS-Type) and sends notification payload using HTTP PUSH method.

The main function:

static void Main(string[] args)
{
    string pushUri = args.Length > 0 ? args[0] : "https://db3.notify.windows.com/?token=AQQAAABS%2bAYaiRtCeOtc9bhaC2xYXo7lv5A5fHEIWuDUePmnl6fxnKMf6wTMgXKp7NqSdSZ3kygzJrak%2fnGhtYV0iqJ1jIuSRJx0%2ftc6tzao1rIX2awu0p4SnYn7K5PItwKeBcY%3d";
    string secret = HttpUtility.UrlEncode("MY_APP_SECRET");
    string sid = HttpUtility.UrlEncode("MY_APP_SID");
    var obj = System.Json.JsonObject.Load(GetAccessToken(sid, secret));
    string accessToken = obj["access_token"].ToString();
    HttpStatusCode status;
    //Send Badge notification
    string badgeNotification = "<?xml version='1.0' encoding='utf-8'?><badge value=\"2\"/>";
    status = Push(pushUri, accessToken, "wns/badge", badgeNotification);
    Console.WriteLine("Badge notification send reault: " + status.ToString());
    //Send Toast notification
    string tostNotification = "<?xml version='1.0' encoding='utf-8'?><toast><visual><binding template=\"ToastImageAndText02\"><image id=\"1\" src=\"Images/Love.png\" alt=\"Placeholder image\"/><text id=\"1\">Love is in the air (PUSHED)!</text><text id=\"2\">Someone sends you love waves!</text></binding></visual></toast>";
    status = Push(pushUri, accessToken, "wns/toast", tostNotification);
    Console.WriteLine("Toast notification send reault: " + status.ToString());
    //Send Tile notification
    string tileNotification = "<?xml version='1.0' encoding='utf-8'?><tile><visual><binding template=\"TileWideSmallImageAndText03\"><image id=\"1\" src=\"ms-appx:///Images/LoveInTheAir.jpg\"/><text id=\"1\">Someone loves you from the distance!</text></binding></visual></tile>";
    status = Push(pushUri, accessToken, "wns/tile",tileNotification);
    Console.WriteLine("Tile notification send reault: " + status.ToString());
    Console.WriteLine("Press ENTER to exit...");
    Console.ReadLine();
}

Also this code is pretty simple it has some important information – the notification payload XML formats. In case of Toast and Tile notification this format actually dictates which information and how it will be presented to user. To learn more about Toast/Tile formats please refer to documentation.

Now when external parts are ready, let’s write Metro app functionality.

Updating tiles/toasts/badges

Tile:

imageimage

Note: Windows 8 enables developer to provide two tiles sizes. Please refer to documentation for full templates list.

Toast:

image

Note: WinRT provides multiple tile and toasts templates. Please refer to documentation for full templates list.

Badges:

image

Badge notification can be either number or one of pre-defined glyphs:

image

Note: The location of badge is predefined.

Let’s get started with updating those items. First I will show how to open push notification channel and obtain URI:

async Task SetupPushNotifications()
{
    var pushNotificationChannel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
    var channelUri = pushNotificationChannel.Uri;
    System.Diagnostics.Debug.WriteLine(channelUri); //This URI will be used in application described above
    //Optional for my sample - not really used
    //pushNotificationChannel.PushNotificationReceived += pushNotificationChannel_PushNotificationReceived;
}
void pushNotificationChannel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
{
    switch (args.NotificationType)
    {
        case PushNotificationType.Badge:
            //Not used in sample, but usefull to process incoming badge notification messages
            //notificationContent = args.BadgeNotification.Content.GetXml(); 
            //...
            break;
        case PushNotificationType.Tile:
            //Not used in sample, but usefull to process incoming tile notification messages
            //notificationContent = args.TileNotification.Content.GetXml();
            //...
            break;
        case PushNotificationType.Toast:
            //Not used in sample, but usefull to process incoming toast notification messages
            //notificationContent = args.ToastNotification.Content.GetXml();
            //...
            break;
    }
    // prevent the notification from being delivered to the UI
    args.Cancel = true;
}

The code snippet creates push notification channel for main application tile and prints out received URI to debug console. I will be using this URI in app described above. Usually, application subscribes to received push notification messages using PushNotificationReceived event, but in my simple case I just provided the code snippet which is not really used.

Note: If application uses secondary tiles (additional tiles created from within application code – will be covered later in this post), it must use CreatePushNotificationChannelForSecondaryTileAsync method of PushNotificationChannelManager to get unique URI for the tile. As described above, delivered notification will be processed by client push notification platform and presented on main screen.

Now let’s see how to issue an update from within the application.

Generally, updating all the of tiles/badges/toasts boils down to creating correct XML according to selected template, populating values and invoking Update method of desired UpdaterManager. Example of building such XML (in this case toast notification):

StringBuilder builder = new StringBuilder("<toast");
if (!String.IsNullOrEmpty(Launch))
{
    builder.AppendFormat(" launch='{0}'", Util.HttpEncode(Launch));
}
if (Duration != ToastDuration.Short)
{
    builder.AppendFormat(" duration='{0}'", Duration.ToString().ToLowerInvariant());
}
builder.Append(">");
builder.AppendFormat("<visual version='{0}'", Util.NOTIFICATION_CONTENT_VERSION);
if (!String.IsNullOrWhiteSpace(Lang))
{
    builder.AppendFormat(" lang='{0}'", Util.HttpEncode(Lang));
}
if (!String.IsNullOrWhiteSpace(BaseUri))
{
    builder.AppendFormat(" baseUri='{0}'", Util.HttpEncode(BaseUri));
}
builder.Append(">");
            
builder.AppendFormat("<binding template='{0}'>{1}</binding>", TemplateName, SerializeProperties(Lang, BaseUri));
builder.Append("</visual>");
AppendAudioTag(builder);
            
builder.Append("</toast>");
return builder.ToString();//...
protected string SerializeProperties(string globalLang, string globalBaseUri)
{
    globalLang = (globalLang != null) ? globalLang : String.Empty;
    globalBaseUri = String.IsNullOrWhiteSpace(globalBaseUri) ? null : globalBaseUri;
    StringBuilder builder = new StringBuilder(String.Empty);
    for (int i = 0; i < m_Images.Length; i++)
    {
        if (!String.IsNullOrEmpty(m_Images[i].Src))
        {
            string escapedSrc = Util.HttpEncode(m_Images[i].Src);
            if (!String.IsNullOrWhiteSpace(m_Images[i].Alt))
            {
                string escapedAlt = Util.HttpEncode(m_Images[i].Alt);
                builder.AppendFormat("<image id='{0}' src='{1}' alt='{2}'/>", i + 1, escapedSrc, escapedAlt);
            }
            else
            {
                builder.AppendFormat("<image id='{0}' src='{1}'/>", i + 1, escapedSrc);
            }
        }
    }
    for (int i = 0; i < m_TextFields.Length; i++)
    {
        if (!String.IsNullOrWhiteSpace(m_TextFields[i].Text))
        {
            string escapedValue = Util.HttpEncode(m_TextFields[i].Text);
            if (!String.IsNullOrWhiteSpace(m_TextFields[i].Lang) && !m_TextFields[i].Lang.Equals(globalLang))
            {
                string escapedLang = Util.HttpEncode(m_TextFields[i].Lang);
                builder.AppendFormat("<text id='{0}' lang='{1}'>{2}</text>", i + 1, escapedLang, escapedValue);
            }
            else
            {
                builder.AppendFormat("<text id='{0}'>{1}</text>", i + 1, escapedValue);
            }
        }
    }
    return builder.ToString();
}

To ease on routine work, updated version of Metro samples includes reusable library named “NotificationsExtensions” which greatly simplifies this work. My sample uses this library and all the “ContentFactory” classes in code below are defined there:

//Update badge
BadgeNumericNotificationContent badgeContent = new BadgeNumericNotificationContent();
if (badgeCount > 0)
    badgeContent.Number = badgeCount;
BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(badgeContent.CreateNotification());
//Update application's main tile
ITileWideSmallImageAndText03 tileContent = TileContentFactory.CreateTileWideSmallImageAndText03();
tileContent.TextBodyWrap.Text = message;
tileContent.Image.Src = "ms-appx:///Images/" + image;
tileContent.RequireSquareContent = false;
TileUpdateManager.CreateTileUpdaterForApplication().Update(tileContent.CreateNotification());
//Fire toast notification
IToastImageAndText02 templateContent = ToastContentFactory.CreateToastImageAndText02();
templateContent.TextHeading.Text = "Love is in the air!";
templateContent.TextBodyWrap.Text = message;
templateContent.Image.Src = "Images/Love.png";
templateContent.Image.Alt = "Placeholder image";
IToastNotificationContent toastContent = templateContent;
ToastNotification toast = toastContent.CreateNotification();
ToastNotificationManager.CreateToastNotifier().Show(toast);

Once XXX_Content populated with data this code snippet uses relevant UpdateManager to update/show relevant item.

Secondary Tiles

Updating secondary tiles:

Uri logo = new Uri("ms-appx:///images/Love.png");
Uri wideLogo = new Uri("ms-appx:///images/WideSecondary.png");
SecondaryTile secondaryTile = new SecondaryTile("LoveCatcher.SecondaryTile",
                                _isABoy.Value ? "A Boy" : "A Girl",
                                _isABoy.Value ? "Lonely Boy" : "Lonely Girl",
                                "pinnedID=" + (_isABoy.Value ? "BOY" : "GIRL"),
                                TileOptions.ShowNameOnWideLogo | TileOptions.ShowNameOnLogo,
                                logo,
                                wideLogo);
bool isPinned = await secondaryTile.RequestCreateForSelectionAsync(GetElementRect(this));
txtStatus.Text = isPinned ? "Secondary tile successfully pinned." : "Secondary tile not pinned.";

User receives request to pin secondary tile and must approve/dismiss this request. Optionally he can change tile title:

image

If both tiles provided (regular and wide), the user can switch between them:

imageimage

Pinned tile arguments can be used to provide alternative loading path – more about it in next post.

Enabling lock screen access

Content published at application’s tile (tile text and badge) can be presented also when PC is locked. To enable application work under lock screen and present badge/tile, application can require user agreement by invoking the following code:

BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
switch (status)
{
    case BackgroundAccessStatus.AllowedWithRealTimeConnectivity:
        //Allowed to work under lock screen (and in background) with real-time connectivity
        //...
        break;
    case BackgroundAccessStatus.AllowedWithoutRealTimeConnectivity:
        //Allowed to work under lock screen (and in background), but without real-time connectivity
        //...
        break;
    case BackgroundAccessStatus.Denied:
        //User declined to work under lock screen 
        //...
        break;
    case BackgroundAccessStatus.Unspecified:
        //The access status not known yet. Good indication to request it
        //...
        break;
}

If access status is not defined yet, user will be presented with system message:

image

Anytime application can request background access:

switch (BackgroundExecutionManager.GetAccessStatus())
{
    case BackgroundAccessStatus.AllowedWithRealTimeConnectivity:
        //...
        break;
    case BackgroundAccessStatus.AllowedWithoutRealTimeConnectivity:
        //...
        break;
    case BackgroundAccessStatus.Denied:
        //...
        break;
    case BackgroundAccessStatus.Unspecified:
        //...
        break;
}

Alternatively user can add/remove supporting application using control panel:

image

Badge and tile notification shown on lock screen:

image

Note: Lock screen tile notification includes only texts. If original tile notification had some graphics it will not be presented on lock screen.

 

Working demo:

Live tiles, toast notification and badges. Lock screen notifications

 

 

That’s it for now. Stay tuned to last part.

Alex

Windows 8 Consumer Preview and Visual Studio 11 Beta – Show Message Dialogs and Popup Menus (Part 9/11)

This post is about popups. Why popups? Who need them?

Popups needed to present some important info to the user in modal way or to present some overlays above other screen elements.

Lets start from Message Box.

WinRT lacks MessageBox support in classic understanding or this UI element.

image

While showing classic dialog box over Metro application is definitely possible from technical point of view (see screenshots in the post about In-App purchases), it completely destroys Metro look and feel of the application. For this reason WinRT provides MessageDialog class in Windows.UI.Popups namespace. This class not only enables showing popups directly over Metro application but also provides it integrated look and feel and enables customizing the dialog box buttons.

Show simple dialog with one standard button (labeled “Close”), simply create a new instance of MessageDialog class and invoke it asynchronously:

Windows.UI.Popups.MessageDialog dialog = 
     new Windows.UI.Popups.MessageDialog("Copyright by Alex Golesh, (c) 2012", 
                                         "Valentine Love Catcher");
dialog.ShowAsync();

This code produce the following dialog:

image

It is possible to customize the experience, and provide custom number of buttons, set default button, create custom button labels, define commands invoked on button press and get the command object after dismissing the dialog:

private async void btnAbout_Click(object sender, RoutedEventArgs e)
{
    Windows.UI.Popups.MessageDialog dialog = 
        new Windows.UI.Popups.MessageDialog("Copyright by Alex Golesh, (c) 2012", 
                                            "Valentine Love Catcher");
    //Customize dialog buttons
    dialog.Commands.Add(new UICommand("Good work!", new UICommandInvokedHandler((args) =>{
        //Executes code on closing dialig using this command
        loveFound = !loveFound;
    })));
    dialog.Commands.Add(new UICommand("Thanks!"));
    dialog.Commands.Add(new UICommand("Just close this dialog"));
    //Makes "Thanks!" a default button being invoed while user press Enter key
    dialog.DefaultCommandIndex = 1;
            
    //Returns UICommand object which makes possible to know how this dialog was dismissed
    var dismissedBy = await dialog.ShowAsync();
    txtStatus.Text = "Dismissed by button labeled '" + dismissedBy.Label + "'";
}

This code snippet produces the following message box:

image

Note: all buttons dismiss the dialog. To wire some functionality (like asking use question providing “Yes”/”No” buttons) on dialog dismiss, use “await” syntax. ow l

Now lets look at Context Menu.

Same namespace (Windows.UI.Popups) enables creating popup (formally context) menus by using new PopupMenu class. Working with this menu very similar to working with MessageDialog – the only real difference is that you cannot provide content/label texts but only UICommands.

private async void imgBoy_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
    Windows.UI.Popups.PopupMenu popupMenu = new PopupMenu();
    popupMenu.Commands.Add(new UICommand("Look for soulmate", new UICommandInvokedHandler((args) =>
    {
        //Invoke some functionality
        btnSearch_Click(sender, e);
    })));
    if (null != licenseInformation)
    {
        //Add additional commands only if user purchased some in-app products
        if (licenseInformation.ProductLicenses["ValentineBouquet"].IsActive || 
            licenseInformation.ProductLicenses["NewYearGift"].IsActive)
            popupMenu.Commands.Add(new UICommandSeparator()); //Create separator line
        if (licenseInformation.ProductLicenses["ValentineBouquet"].IsActive)
            popupMenu.Commands.Add(new UICommand("Send valentine bouquet"));
        if (licenseInformation.ProductLicenses["NewYearGift"].IsActive)
            popupMenu.Commands.Add(new UICommand("Send New Year gift"));
    }
    var dismissedBy = await popupMenu.ShowAsync(e.GetPosition(this));
    txtStatus.Text = "Dismissed by command labeled '" + dismissedBy.Label + "'";
}

This code snippet produces the following result:

image

Note: like in MessageDialog, any command releases the popup menu. To wire some functionality on popup menu dismiss, use “await” syntax.

This is it about the popups.

 

Stay tuned for the next part.

Alex

Windows 8 Consumer Preview and Visual Studio 11 Beta – Quick tip: using location services (Part 8/11)

In this short post I will show how to use location services in you app. Working with location services requires declaring Location capability in application manifest:

image

The location services provides access to location functionality, such as cell triangulations, WiFi (through IP address), and GPS. Also great many modern devices supports resolving location in some way from mentioned before, application must handle the case where location services cannot resolve the location or user has disabled location services from the Control Panel.

The location service accessed through Windows.Devices.Geolocation namespace. Geolocator class responsible for resolving the location and (like sensors) supports two work modes: polling and events.

Using events based model requires subscribing to PositionChanged event to get notifications once position changes and optionally StatusChanged event which raised when the ability of the Geolocator to provide updated location changes. Also Geolocator class supports specifying DesiredAccuracy, MovementThreshold and ReportInterval to support various accuracy VS speed requirements of specific application.

Disclaimer: Since my app demo application (Valentine Love Catcher) not really need to use those services, I will show relatively simple scenario – polling.

Sample app will be using location (if available) when sharing information and only in case proximity connection is not established yet. App initializes location services during general localizations:

private async void InitializeLocationServices()
{
    //Initialize geolocator object
    Geolocator geoLocator = new Geolocator();
    if (null != geoLocator)
        try
        {
            //Try resolve the current location
            position = await geoLocator.GetGeopositionAsync();
        }
        catch (Exception)
        {
            //Nothing to do - no GPS signal or some timeout occured...
        }
}

When application sharing data, if position already resolved the values will be used in message transferred to Share Target application:

if (null != position)
{
    string city = position.CivicAddress.City;
    string country = position.CivicAddress.Country;
    string state = position.CivicAddress.State;
    string zip = position.CivicAddress.PostalCode;
    string msg = "I am located in " + country;
    if (city.Length > 0)
        msg += ", city of " + city;
    if (state.Length > 0)
        msg += ", " + state;
    if (zip.Length > 0)
        msg += " near zip code " + zip;
}
//...

Pretty simple…

That’s it for now.

 

Stay tuned for next post,

Alex

Windows 8 Consumer Preview and Visual Studio 11 Beta – Licensing and In-app purchases (Part 7/11)

Windows 8 Metro applications are distributed by Windows Store. The store handles all installation tasks, updating to the new versions when published by developer. Also it handles purchasing the application, supporting trial mode (if application developer chooses to support it) and handles in-app purchases.

All licensing supported by Windows.ApplicationModel.Store namespace.

image

Note: CurrentApp provides license information for the current app.This object obtains its data from the Windows Store, which is not currently (Consumer Preview timeframe) supported.

For development purposes we will use CurrentAppSimulator class, which completely mimics APIs of CurrentApp class but enables developing licensing support before publishing the app.

Let’s see the licensing initialization code:

private async void InitializeLicenseEngine()
{
#if DEBUG
    //Point to XML infomration provoded with application instrad of getting data from Windows Store
    await InitializeProxyFolderAsync();
    await SetupProxyFile("LicenseInfo.xml");
#endif
    initializeLicense();
    UpdateCurrentLicenseMode();
}
private void initializeLicense()
{
#if DEBUG
    //Use simulator for debugging
    licenseInformation = CurrentAppSimulator.LicenseInformation;
#else
    //Use real Windows Store data
    licenseInformation = CurrentApp.LicenseInformation;
#endif
    //Subscribe to license changes
    licenseInformation.LicenseChanged += licenseInformation_LicenseChanged;
}
void licenseInformation_LicenseChanged()
{
    //Update application logic on license changes
    UpdateCurrentLicenseMode();
}

For development purposes two helpers functions are used to provide response as it is provided by Windows Store.

async Task InitializeProxyFolderAsync()
{
    // setup licensing proxy XML folder
    proxyFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Microsoft\\Windows Store\\ApiData", CreationCollisionOption.OpenIfExists);
    installLocation = Windows.ApplicationModel.Package.Current.InstalledLocation;
}
async Task SetupProxyFile(string fileName)
{
    // open the proxy file
    StorageFile proxyFile = await installLocation.GetFileAsync(fileName);
    // create proxy file in application data
    StorageFile targetFile = await proxyFolder.CreateFileAsync("WindowsStoreProxy.xml", CreationCollisionOption.ReplaceExisting);
    // replace the contents
    await proxyFile.CopyAndReplaceAsync(targetFile);
}

The XML file with all license info packed with the application to enable debugging:

image

The file content uses same XML structure as real response from Windows Store:

<?xml version="1.0" encoding="utf-16" ?>
<CurrentApp>
  <ListingInformation>
    <App>
      <AppId>3C14D306-D8F8-4066-A45B-0FB3464C67F3</AppId>
      <LinkUri>http://apps.microsoft.com/app/3C14D306-D8F8-4066-A45B-0FB3464C67F3</LinkUri>
      <CurrentMarket>en-us</CurrentMarket>
      <AgeRating>4</AgeRating>
      <MarketData xml:lang="en-us">
        <Name>Valentine Love Catcher</Name>
        <Description>Sample application to demonstrate new APIs of Windows 8</Description>
        <Price>5.99</Price>
        <CurrencySymbol>$</CurrencySymbol>
      </MarketData>
    </App>
    <Product ProductId="NewYearGift">
      <MarketData xml:lang="en-us">
        <Name>Happy 2013 New Year Gift</Name>
        <Price>1.99</Price>
        <CurrencySymbol>$</CurrencySymbol>
      </MarketData>
    </Product>
    <Product ProductId="ValentineBouquet">
      <MarketData xml:lang="en-us">
        <Name>Valentine Bouquet</Name>
        <Price>2.99</Price>
        <CurrencySymbol>$</CurrencySymbol>
      </MarketData>
    </Product>
  </ListingInformation>
  <LicenseInformation>
    <App>
      <IsActive>true</IsActive>
      <IsTrial>true</IsTrial>
      <ExpirationDate>2012-05-01T00:00:00.00Z</ExpirationDate>
    </App>
    <Product ProductId="NewYearGift">
      <IsActive>false</IsActive>
      <ExpirationDate>2013-01-02T00:00:00.00Z</ExpirationDate>
    </Product>
    <Product ProductId="ValentineBouquet">
      <IsActive>false</IsActive>
    </Product>
  </LicenseInformation>
</CurrentApp>

It defines ListingInformation (product name, rating, price, etc.), in-app products (if supported) and licensing information (trial, active, expiration for application and all products).

Application uses this information to adjust its functionality. In case of my sample application all the proximity functionality is disabled and changes this support when license changed:

private async void UpdateCurrentLicenseMode()
{
    //Show/hide "Buy Now" button based on Trial mode
    btnBuyNow.Visibility = licenseInformation.IsTrial ? Visibility.Visible : Visibility.Collapsed;
    //Check trial/active to enable/disable NFC
    if (licenseInformation.IsTrial || !licenseInformation.IsActive)
    {
        //Disable proximity sensor if not active or in trial mode
        _proximityDevice = null;
        //Get Windows Store (or simulator) listing info and suggest to purchase the app
#if DEBUG
        ListingInformation listing = await CurrentAppSimulator.LoadListingInformationAsync();
#else
        ListingInformation listing = await CurrentApp.LoadListingInformationAsync();
#endif
        txtStatus.Text = "Proximity disabled in trial or non-licensed modes.\nPurchase this application for only " + listing.FormattedPrice;
    }
    else if (!licenseInformation.IsTrial && licenseInformation.IsActive && null == _proximityDevice)
    {
        //Enable proximity when application become active
        _proximityDevice = ProximityDevice.GetDefault();
        if (null != _proximityDevice)
            txtStatus.Text = "Proxmity device is available and ready to go!";
    }
    //Check supported in-app products to enable/disable some UI elements
    if (!licenseInformation.ProductLicenses["ValentineBouquet"].IsActive)
        btnFlower.IsEnabled = true;
    else
        btnFlower.IsEnabled = false;
    if (!licenseInformation.ProductLicenses["NewYearGift"].IsActive)
        btnBallons.IsEnabled = true;
    else
        btnBallons.IsEnabled = false;
}

This code snippet update application’s UI and enables buying application and in-app products:

imageimage

Pressing “Buy now” button invokes purchasing process:

private async void btnBuyNow_Click(object sender, RoutedEventArgs e)
{
    //Invoke only if still in trial
    if (licenseInformation.IsTrial)
    {
        try
        {
            //Purchase the application
#if DEBUG
            await CurrentAppSimulator.RequestAppPurchaseAsync();
#else
            await CurrentApp.RequestAppPurchaseAsync();
#endif
            //Purchased finished successfully
            txtStatus.Text = "You successfully upgraded your app to the fully-licensed version.";
        }
        catch (Exception)
        {
            //If exception thrown, provide user with information why application were not purchased
            txtStatus.Text = "The upgrade transaction failed. You still have a trial license for this app.";
        }
    }
    else
    {
        txtStatus.Text = "You already bought this app and have a fully-licensed version.";
    }
    UpdateCurrentLicenseMode();
}

While using simulator simple dialog enables to simulate response form Windows Store to debug possible responses:

image

Almost same process is used to purchase in-app products:

private async Task PurchaseProduct(string ProductID)
{
    try
    {
       //Start produc purchaseing
#if DEBUG
        await CurrentAppSimulator.RequestProductPurchaseAsync(ProductID);
#else
        await CurrentApp.RequestProductPurchaseAsync(ProductID);
#endif
        //Provide user some feedback about purchase success
        ListingInformation listing = await CurrentAppSimulator.LoadListingInformationAsync();
        txtStatus.Text = "You successfully purchased " + listing.ProductListings[ProductID].Name + " for " + listing.ProductListings[ProductID].FormattedPrice;
    }
    catch (Exception)
    {
        //Handle purchase exception
        txtStatus.Text = "The upgrade transaction failed. You still have a trial license for this app.";
    }
    UpdateCurrentLicenseMode();
}

As with application purchase scenario, the simulator dialog enables to simulate response form Windows Store to debug possible responses:

imageimage

Note: since this approach uses Windows Store simulator and embedded license information the purchases are not preserved between application launches (not activations – see PLM post to understand the differences) and not synced across different user devices.

 

That’s al I have to say about that © Forest Gump

 

 

Stay tuned for next part.

Alex

Windows 8 Consumer Preview and Visual Studio 11 Beta – Quick tip: using sensors (Part 6/11)

Windows 8 supports multiple sensors. Some of them could be installed on your device, some of them not.

When I planned this series, I had pretty hard choice to make about the sensor – which one to use. Accelerometer/Gyrometer/Compass/Inclinometer/Orientation are cool sensors, but their usage pretty tightly bound to specific scenarios like games (Accelerometer/Gyrometer/Inclinometer), some location aware applications (Compass) or at least orientation-aware apps (Orientation). I had to make a choice either create simple “show-the-values” boring sample or game which using those sensors (at least some of them) or showcase go with non-standard approach presenting LightSensor which was easy bound to my sample app.

Since all the sensors supports same APIs, I decided to go with latter approach.

Working with all sensors are roughly the same as scenario presented in this simple post. To initialize sensor, use GetDefault function:

//Initialize the sensor
lightSensor = LightSensor.GetDefault();
if (null != lightSensor)
{
    //Sensor is supported!
    //Optionally set reporting interval. Cannot exceed hardware supported minimum reporting inteval
    lightSensor.ReportInterval = lightSensor.MinimumReportInterval;
    //Subscribe to ReadingChanged event to handle new value
    lightSensor.ReadingChanged += new TypedEventHandler<LightSensor, LightSensorReadingChangedEventArgs>(LightSensor_ReadingChanged);
}
else
{
    //Sensor is not supported. Use alternative app path 
    //...
}

If specific sensor supported you can optionally update ReportInterval property specifying how fast/slow your app requires new data. Polling new data slowly helps to preserve battery life (on battery powered devices) but can possible degrade the application functionality (especially in game scenarios when sensor, such as accelerometer, is used for main input).

Note: ReportInterval cannot be lower than hardware reported MinimumReportInterval provided by sensor.

Working with sensor possible using two approaches – polling current reading when necessary using GetCurrentReading function:

var currentReading = lightSensor.GetCurrentReading();

or provide event handler function which invoked every time sensor has a new value (like showed in first code snippet). In both cases, sensor reading object provides all relevant information (for sensor) + reading timestamp. In my sample app LightSensorReading used to change application’s background color:

private void LightSensor_ReadingChanged(LightSensor sender, LightSensorReadingChangedEventArgs args)
{
    Dispatcher.InvokeAsync(CoreDispatcherPriority.Normal, (s, a) =>
    {
        LightSensorReading reading = (a.Context as LightSensorReadingChangedEventArgs).Reading;
        LayotRoot.Background = new SolidColorBrush(Windows.UI.Color.FromArgb(255, (byte)(reading.IlluminanceInLux), 0, 0));
    }, this, args);
}

Note: The event handler arrives out of UI thread, so Dispatcher.InvokeAsync technique must be used to access UI elements.

 

That’s in for now. Stay tuned for next post.

Alex

Windows 8 Consumer Preview and Visual Studio 11 Beta – Local and Roaming application data storage (Part 5/11)

In previous post (Settings – LINK) I explained how to create application’s settings screed and customize it with application specific UI and data. In this post I will explain how to preserve application settings and other application data.

Wind8 CP enables few locations to store application data. Application can use temporary directory to save any kind of information which was possible also before Windows 8. In addition WinRT provides native support saving data in application’s data store as collection of Key Value Pairs. This data can be stored locally or using roaming store. Roaming store will be synchronized by Windows if user permits data synchronization (and when such synchronization is permitted):

image

To take advantage of this mechanism application must use ApplicationData class from Windows.Storage namespace. This class provides access to both, LocalSettings and RemoteSettings. Both classes exposes sample APIs, which enables abstracting Save/Load functionality from actual data location.

In my sample application I am supporting both types of storages. As you probably remember from previous post, my settings screen supports switching between stores by enabling and disabling settings synchronization.

image

Let’s get started. Regardless to actual data location (local store or cloud), application subscribes to DataChanged event. This event occurs when roaming data is synchronized (in my scenario occurs when changed on some other device running same application).

Note: In my case application synchronizes only settings, then it reloads synchronized data once it was changed on other device. In other scenarios it might or might not make sense reloading synchronized data during application execution and depends exclusively on application.

protected override void OnNavigatedTo(NavigationEventArgs e)
{ 
    //Sunbscribe to DataChanged event to get notification if data changed in some other device
    ApplicationData.Current.DataChanged += (sender, args) =>
    {
        //Reload settings when they changes somewhere
        ReloadSettigns();
    };
    //Get settings
    ReloadSettigns();
    //...
}

Let’s see the ReloadSettings function:

private void ReloadSettigns()
{
    //Application logic prefers roaming storage over local one
    roamingSettings = ApplicationData.Current.RoamingSettings;
    if (roamingSettings.Values.Count == 0)
        roamingSettings = ApplicationData.Current.LocalSettings;
    //Application settings build from exactly 8 values. If something is missing use default values
    if (roamingSettings.Values.Count == 8)
    {
        //Value is object and must be cast to final type
        RemotePushEnabled = (bool)roamingSettings.Values["RemotePushEnabled"];
        LocationEnabled = (bool)roamingSettings.Values["LocationEnabled"];
        SensorsEnabled = (bool)roamingSettings.Values["SensorsEnabled"];
        LocalPushEnabled = (bool)roamingSettings.Values["LocalPushEnabled"];
        SharingEnabled = (bool)roamingSettings.Values["SharingEnabled"];
        ProximityEnabled = (bool)roamingSettings.Values["ProximityEnabled"];
        BackgroundAccess = (bool)roamingSettings.Values["BackgroundAccess"];
        SyncSettings = (bool)roamingSettings.Values["SyncSettings"];
    }
}

As you remember from previous post, when some value changed on Settings screen, the value assigned to corresponding variable and SaveSettings function being executed. Let’s see the function:

private void SaveSettings()
{
    //Clean previous values
    CleanSettings();
    //If user prefers to sync data in the cloud use RoamingSettings, otherwise use LocalSettings
    if (SyncSettings)
        roamingSettings = ApplicationData.Current.RoamingSettings;
    else
        roamingSettings = ApplicationData.Current.LocalSettings;
    //Add values to settings
    roamingSettings.Values.Add("RemotePushEnabled", RemotePushEnabled);
    roamingSettings.Values.Add("LocationEnabled", LocationEnabled);
    roamingSettings.Values.Add("SensorsEnabled", SensorsEnabled);
    roamingSettings.Values.Add("LocalPushEnabled", LocalPushEnabled);
    roamingSettings.Values.Add("SharingEnabled", SharingEnabled);
    roamingSettings.Values.Add("ProximityEnabled", ProximityEnabled);
    roamingSettings.Values.Add("BackgroundAccess", BackgroundAccess);
    roamingSettings.Values.Add("SyncSettings", SyncSettings);
    //Signal data changed which will trigger cloud sync process when system synchronizes data next time
    ApplicationData.Current.SignalDataChanged();
}
private void CleanSettings()
{
    if (null != roamingSettings)
        roamingSettings.Values.Clear();
}

Note: Cleaning all values instead of replacing them is probably not the best practice which can increase synchronization traffic usage, but in case of such simple example (and for code brevity) I preferred this approach.

In addition settings can be organized in Containers:

//Create new container
roamingSettings.CreateContainer("GeneralSettings", ApplicationDataCreateDisposition.Always);
//Setting named value in specific container
roamingSettings.Containers["GeneralSettings"].Values.Add("RegisteredUser", "AlexG");
//Getting named value form specific container
string user = (string)roamingSettings.Containers["GeneralSettings"].Values["RegisteredUser"];
//Delete container with all values
roamingSettings.DeleteContainer("GeneralSettings");

When using roaming settings important to know how much data being synced and how much could be synced. ApplicationData class provides information about roaming storage quota and usage:

ulong quota = ApplicationData.Current.RoamingStorageQuota;
ulong used = ApplicationData.Current.RoamingStorageUsage;

In addition, it is possible to create basic version management of data stored in any store by querying current version and setting new version:

//Get current version
uint version = ApplicationData.Current.Version;
//Set new version
await ApplicationData.Current.SetVersionAsync(123, new ApplicationDataSetVersionHandler((version) => {
    if (version.CurrentVersion == version.DesiredVersion)
    {
         //Worked
    }
    else
    {
         //Something went wrong...
    }
}));

Last, but not least, ApplicationData class provides access to Local/Roaming/Temporary folders. All classes exposes the sample API and enables managing files and folders using standard WinRT async patterns:

//Create file in roaming folder. Also possible to create file/folder in ApplicationData.Current.TemporaryFolder or ApplicationData.Current.LocalFolder
StorageFile file = await ApplicationData.Current.RoamingFolder.CreateFileAsync("settings.dat", CreationCollisionOption.ReplaceExisting);//Open file for writing and write some binary buffer...
Stream stream = await file.OpenStreamForWriteAsync();
stream.Write(buffer, offset, count);
await stream.FlushAsync();

That’s it for now.

Stay tuned for next part.

Alex

Windows 8 Consumer Preview and Visual dStudio 11 Beta – Customizing Settings Charm (Part 4/11)

Before Windows 8 every application invented its own way to present application settings. Here are very few of them:

imageimage

imageimage

Some apps even provided standalone applications to manage settings. Also from settings storage perspective every developer had to decide when, where and how those settings are saved and in some cases synchronized between different user’s machines.

With Windows 8 Metro applications most of those questions and hard choices are gone. Every Metro application can use system-wide standard way to present settings and collect them in predictable and system-standard way. Windows 8 provides “Settings” charm which is accessible as a part of system menu.

image

By default, if not customized, application will have only one (system provisioned) settings entry – Permissions.This menu item shows basic application package information and special permissions application uses (as provided by application manifest)

imageimage

Application developer can customize the contents of Settings charm by adding custom menu entries (commands) and handling it in application.

When user opens the settings charm application gets a chance to provide custom commands. To get notified about this request subscribe to CommandsRequested event of SettingsPane class as in the following code snippet:

//Setup settings panel
SettingsPane.GetForCurrentView().CommandsRequested += OnCommandsRequested;

Application can provide customized UI to launch the settings charm. To show this charm invoke the following code as response your application corresponding UI request:

SettingsPane.Show();

Once user invokes this functionality or opens Settings charm by system gesture OnCommandsRequested handler being executed. In order to add new item (Command) to settings pane you have to create new instance of SettignsCommand class providing CommandId, Label and handler function (which will be executed in user invokes the command):

void BlankPage_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
{
    SettingsCommand SyncSettingsCommand = new SettingsCommand("SyncSettings", SyncSettings ? "Disable settings sync" : "Enable settings sync", new UICommandInvokedHandler(onSettingsSync));
    args.Request.ApplicationCommands.Add(SyncSettingsCommand);
    //...
    SettingsCommand helpCommand = new SettingsCommand("HelpPage", "Help", new UICommandInvokedHandler(onHelpCommand));
    args.Request.ApplicationCommands.Add(helpCommand);
}
private void onSettingsSync(IUICommand command)
{
    //Do application logic for setting change
    SyncSettings = !SyncSettings;
    SaveSettings();
}
private void onHelpCommand(IUICommand command)
{
    helpPage.Margin = ThicknessHelper.FromUniformLength(0);
}

Note: Most of the commands were skipped for brevity of the code. SaveSettigs functionality will be discussed in my next post.

Customized settings charm (permissions are provided by OS):

image

Let us see two typical settings commands: direct value change (like Sync settings) and “link” to some additional settings screen. In both cases, taping (clicking) the command on settings charm dismisses the charm. While in case of Sync settings the command is responsible for direct change of the setting, in case of Help we want to present some additional functionality and screen. Unfortunately it is not possible to integrate custom user control into settings charm, so application developer must provide the UI and system-like behavior.

System-like behavior assumes that changes made in custom panel are implemented/saved as they done, panel enables navigating back to system settings pane and supports light dismiss – clicking anywhere else of the panel dismisses the panel and saves the settings.

In my application I created simple user control, which were initially placed off main screen (using negative margin):

<local:HelpPage x:Name="helpPage" Margin="0,0,-346,0" HorizontalAlignment="Right">
    <local:HelpPage.Transitions>
        <TransitionCollection>
            <RepositionThemeTransition/>
        </TransitionCollection>
    </local:HelpPage.Transitions>
</local:HelpPage>

I defined transition effect for my custom control to enable repositioning using system-like animation (RepositionThemeTransition). Using this effect helps mimic “sliding out” effect like real system-provided settings charm does. Final custom settings panel looks like the following:

imageimage

To support light-dismiss I’ve subscribed to PointerPressed event on parent UI element (in my case – Grid):

<Grid PointerPressed="Grid_PointerPressed">

And in handler function simply changed the Margin property of the control:

private void Grid_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerEventArgs e)
{
    //Change margin only if control is visible
    if (helpPage.Margin.Right == 0)
        helpPage.Margin = ThicknessHelper.FromLengths(0, 0, -346, 0);
}

At the control level, I subscribed to back button Click event:

<Button x:Name="backButton" Click="GoBack" Style="{StaticResource BackButtonStyle}" Margin="0"/>

and in event handler I re-opened settings pane and hided the panel:

private void GoBack(object sender, RoutedEventArgs e)
{
    //Reopen settings pane
    SettingsPane.Show();
    //Hide this panel
    this.Margin = ThicknessHelper.FromLengths(0, 0, -346, 0);
}

Small trick here – you control must subscribe and handle PointerPressed event, otherwise parent control (Grid in my case) will dismiss it on any tap/click, even on control itself:

private void UserControl_PointerPressed(object sender, PointerEventArgs e)
{
    e.Handled = true;
}

The control logic (the rest of code you have to write) is absolutely unique for each application.

The working settings pane with custom help panel demonstrated here:

Integrating custom panel in settings panel

Using the techniques described above you application supports standard Windows 8 settings behaviour.

That’s it for now.

 

Stay tuned for next post.

Alex

Windows 8 Consumer Preview and Visual Studio 11 Beta – Data Sharing (Part 3/11)

Windows 8 enables data exchange between applications. This was possible in the past, but to make it happen two applications must have knowledge about each other which was always not possible especially for applications developed by different companies. Windows 8 enables easy data sharing between the application by introducing OS-level sharing mechanism. Metro applications can publish information from certain types (“Share Source” applications) from one hand, operation system is responsible of selecting applications which expressed “interest” in some specific type of info and those applications (“Share Target”) are responsible of processing data received upon activation.

In this post I will show how to share textual and image information from my sample application. When working on this sample I discovered that since my tablet is empty from other applications, I have no share target applications.

image

To work around this problem I wrote simple “Share Target” application which accepts all possible share types and simply presents them.

image

Let’s see how to build simple Sharing Target first.

When application is launched from start screen, it will present static EmptyPage without any real functionality:

image

This done by redirecting application’s root frame to this page:

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    // Create a Frame to act navigation context and navigate to the first page
    var rootFrame = new Frame();
    rootFrame.Navigate(typeof(EmptyPage));
    // Place the frame in the current Window and ensure that it is active
    Window.Current.Content = rootFrame;
    Window.Current.Activate();
}

When application is launched in “Share Target” mode I redirected control over the process to different page, which process the data and present it:

protected override async void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
    SharePage shareTargetPage = new SharePage();
    await shareTargetPage.ActivateAsync(args);
}

To enable sharing functionality application must declare supported share contracts:

image

Note: application must either declare supported format (from known or user-defined types) or support “any” file type.

Also, in application settings specify executable file and entry point:

image

The application code looks pretty simple and self-describing:

ShareOperation shareOperation = null;
const string dataFormatName = "Valentine-Love-Catcher";
//...
//-----------------------------------------------------------------
public async Task ActivateAsync(ShareTargetActivatedEventArgs args)
{
    //If application activated in Share Target mode process further
    if (args.Kind == ActivationKind.ShareTarget)
    {
        //Get share operation object from event args
        shareOperation = args.ShareOperation;
        //Fill UI fields with data
        txtTitle.Text = shareOperation.Data.Properties.Title;
        txtDesc.Text = shareOperation.Data.Properties.Description;
        if (!String.IsNullOrEmpty(shareOperation.QuickLinkId))
            txtLinkID.Text = shareOperation.QuickLinkId;
        else
            stkLink.Visibility = Visibility.Collapsed;
        if (shareOperation.Data.Contains(StandardDataFormats.Uri))
        {
            Uri uri = await shareOperation.Data.GetUriAsync();
            if (uri != null)
                txtURI.Text = uri.AbsoluteUri;
        }
        else
            stkURI.Visibility = Visibility.Collapsed;
        if (shareOperation.Data.Contains(StandardDataFormats.Text))
        {
            string text = await this.shareOperation.Data.GetTextAsync();
            if (text != null)
                txtText.Text = text;
        }
        else
            stkText.Visibility = Visibility.Collapsed;
        if (shareOperation.Data.Contains(StandardDataFormats.StorageItems))
        {
            IReadOnlyList<IStorageItem> storageItems = null;
            storageItems = await this.shareOperation.Data.GetStorageItemsAsync();
            string fileList = String.Empty;
            for (int index = 0; index < storageItems.Count; index++)
            {
                fileList += storageItems[index].Name;
                if (index < storageItems.Count - 1)
                    fileList += ", ";
            }
            txtFiles.Text = "StorageItems: " + fileList;
        }
        else
            stkFiles.Visibility = Visibility.Collapsed;
        if (shareOperation.Data.Contains(dataFormatName))
        {
            string receivedStrings = await shareOperation.Data.GetTextAsync(dataFormatName);
            JsonObject customObject = null;
            if (JsonObject.TryParse(receivedStrings, out customObject))
            {
                if (customObject.ContainsKey("type"))
                {
                    if (customObject["type"].GetString() == "http://schema.org/LoveVibe")
                    {
                        // This sample expects the custom format to be of type http://schema.org/LoveVibe
                        receivedStrings = "Type: " + customObject["type"].Stringify();
                        JsonObject properties = customObject["properties"].GetObject();
                        receivedStrings += Environment.NewLine + "Name: " + properties["name"].Stringify()
                                        + Environment.NewLine + "Soulmate: " + properties["soulmate"].Stringify()
                                        + Environment.NewLine + "Image: " + properties["image"].Stringify();
                    }
                }
            }
            txtCustomData.Text = receivedStrings;
        }
        else
            stkCustomFormat.Visibility = Visibility.Collapsed;
        if (shareOperation.Data.Contains(StandardDataFormats.Html))
        {
            string htmlFormat = await this.shareOperation.Data.GetHtmlFormatAsync();
            string htmlFragment = HtmlFormatHelper.GetStaticFragment(htmlFormat);
            webView.NavigateToString("<html><body>" + htmlFragment + "</body></html>");
        }
        else
            webView.Visibility = Visibility.Collapsed;
        if (this.shareOperation.Data.Contains(StandardDataFormats.Bitmap))
        {
            IRandomAccessStreamReference imageReceived = await this.shareOperation.Data.GetBitmapAsync();
            IRandomAccessStreamWithContentType stream = await imageReceived.OpenReadAsync();
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.SetSource(stream);
            imageHolder.Source = bitmapImage;
        }
        else
            imageHolder.Visibility = Visibility.Visible;                    
    }
    //Activate the window
    Window.Current.Content = this;
    Window.Current.Activate();
}
private void ReportComplete(object sender, RoutedEventArgs e)
{
    shareOperation.ReportStarted();
    //Simulate some sharing process. Different report types supported here, but at the end of the process application must report completed operation
    //...
    shareOperation.ReportCompleted();
}

Sharing simple text from Sharing Source application:

image

Full application source can be downloaded from here.

Now let’s get back to “Share Source”. User initiates sharing process by selecting “Share” charm from right-swipe menu:

image

If application supports sharing it must initialize DataTransferManager class as soon as sharing from application enabled:

public void InitializeShareSource()
{
    DataTransferManager datatransferManager;
    datatransferManager = DataTransferManager.GetForCurrentView();
    datatransferManager.DataRequested += 
            new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(OnDataRequested);
}

From now on application handles data requests by invoking the OnDataRequested function:

private async void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    //Fill basic properties
    args.Request.Data.Properties.Title = "Valentine Love Catcher";
    args.Request.Data.Properties.Description = "Spread the word about this lovely application";
    args.Request.Data.Properties.Thumbnail = 
            RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Images/Love.png"));
    //Share bitmap image
    args.Request.Data.SetBitmap(RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Images/Girl_1.png")));
    //Share simple text
    args.Request.Data.SetText("Just found my true soulmate!");
    //Share multiple files from device's storage
    List<StorageFile> items = new List<StorageFile>();    
    StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Images/Bouquet.png"));
    items.Add(file);
    file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Images/NewYear.png"));
    items.Add(file);
    args.Request.Data.SetStorageItems(items);
}

Code snippet above shares bitmap image, text and list of files from storage.

Note: Each share target app handles shred information differently. In some case not all information is used by shared target.

In some cases application provides UI to start sharing process (instead of scenario when user initiates it by selecting share charm). To support this scenario DataTransferManager class provides special method which opens share UI:

//Force showing share UI
DataTransferManager.ShowShareUI();In some advanced scenarios, application cares about target application (for example share or not share some specific data with some specific apps). To handle this scenario DataTransferManager enables subscribing to TargetApplicationChosen event:
//If application makes distinction between target applications, subscribe to this event
datatransferManager.TargetApplicationChosen += datatransferManager_TargetApplicationChosen;
//...
void datatransferManager_TargetApplicationChosen(DataTransferManager sender, TargetApplicationChosenEventArgs args)
{
    //Application logic based of selected target application. Find select target application name in args.ApplicationName
    //...
}

Pretty simple, yet very powerful and opens many interesting scenarios.

That's all I have to say about that © Forrest Gump

Win8 made sharing really simple…

Stay tuned to next part.

Alex

Windows 8 Consumer Preview and Visual Studio 11 Beta – Working with Proximity Device (Part 2/11)

This post is about proximity devices and scenarios enabled by using this functionality.

WinRT supports Near Field Communication (NFC) scenarios by providing native support using Windows.Networking.Proximity namespace. This namespace enable working with proximity device in two different modes – direct communication using sockets (ProximityStreamSocket class) and publish/subscribe mode (using ProximityMessage class). ProximityDevice class provides gateway to hardware NFC device component (if present in computer/tablet, drivers are installed and user permits using such type of devices).

To use Proximity device it must be manifested in Capabilities section of application manifest:

image

In both cases first you need to acquire the proximity device:

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
ProximityDevice _proximityDevice;
_proximityDevice = ProximityDevice.GetDefault();
 
//Check for proximity device presence
if (null == _proximityDevice)
{
    //Device is not present or not enabled. Have a "backup" plan
    //...
}
else
{
    //Device is presend and working
    //...
}

Using sockets requires using PeerFinder class which helps looking for devices, enables getting requests from arriving device and connection to them. Once two peers completer the connection cycle (PeerFinder.TriggeredConnectionState == TriggeredConnectState.Completed) event arguments provides an instance of ProximitryStreamSocket class which is used to transmit data between devices.

While this technique is relatively advanced and requires subscribing to multiple events and managing socketed connection, ProximityDevice class provides easier way to transmit messages between two PCs/Tablets. Using pub/sub model requires subscribing to known message type from one hand and publishing known message type from other hand. Once connection established and message delivered devices can stop publishing/listening to the messages:

long _subscribedMessageId = -1;
long _publishedMessageId = -1;
 
//...
//-----------------------------------------------------------------------------------------
 
//Subscribing to messages
if (_subscribedMessageId == -1)
{
    //Start listening for messages
    _subscribedMessageId = _proximityDevice.SubscribeForMessage("Windows.TheLoveMessage", (_proximityDevice, message) =>
    {
        //When message arrives proceed with application logic
        if (message.DataAsString.Contains("LOVE;"))
        {
            ///...
        }
 
        //Optionally, stop listening to messgaes if applicaton logic requires so
        _proximityDevice.StopSubscribingForMessage(_subscribedMessageId);
    });
}
else
    //Already subscribed to message...
 
//...
//-----------------------------------------------------------------------------------------
 
//Publish messages
if (_publishedMessageId == -1)
{
    //Start publishing messages
    _publishedMessageId = _proximityDevice.PublishMessage("Windows.TheLoveMessage", "LOVE;" + txtName.Text);
    
    //The rest of applciation logic after starting publishing the message
}
else
    //Already publishing the message...
 
//...
//-----------------------------------------------------------------------------------------
 
//When not needed anymore, stop publishing the message
_proximityDevice.StopPublishingMessage(_publishedMessageId);

In addition, ProximityDevice enables subscribing to DeviceArrived and DeviceDeparted events, which helps to maintain state of connected/disconnected devices and provides information about them:

//Get proximity device
_proximityDevice = ProximityDevice.GetDefault();
 
//If device initialized successfully
if (null != _proximityDevice)
{
    //Subscribe to DeviceArrived event
    _proximityDevice.DeviceArrived += (arrivedProximityDevice) =>
        {
            //...
        };
 
    //Subscribe to DeviceADeparted event
    _proximityDevice.DeviceDeparted += (departedProximityDevice) =>
        {
            //...
        };
}

Note: MessageType parameter in PublishMessage function must begin from “Windows.

That’s it for now.

 

Stay tuned for next part.

Alex

Windows 8 Consumer Preview and Visual Studio 11 Beta – What’s New (Part 1/11)

Long awaited Windows 8 Consumer Preview (Win8 CP) just released!

image

It is not come alone, but accompanied with Visual Studio 11 Beta (VS11)! Both products available for download. The links are:

Win8 CP download is available here.

Visual Studio 11 Express Beta for Windows 8 download available here.

So what’s new for developers?

First think you will meet right after installing VS11 is facelift. Compare – VS2010 & VS11:

image

image

VS11 got Metro-ish face lift! The icons are flat and grey, windows got flat look and feel with grey title, even breakpoint marker (red dot) was flattened. Despite the fact I loved Win8 Metro main screen, my first reaction was “%@X&*@%$#@$@!” and I have to admit I was wrong. Now, after using it for quite some time I absolutely love it!

In next days you will see great many articles and blog posts discussing new features of Win8 CP and the VS11; I will focus on practical part part – how to take your //BUILD DP application run in Win8 CP.

Disclaimer: I love XAML and C#, thus all my posts will be focused only on those technologies of Win8 Metro development. If you are looking for XAML/C++ or HTML/JavaScript stuff, you should refer to some other wonderful blogs.

Let’s see how to migrate you BUILD DP timeframe app to Beta. Just opening the application's solution file with VS11 Beta will not work and you will be getting this kind of experience:

image

With the following problem in Output window:

C:\Users\Alex\Downloads\File access sample\C#\FileAccessSample.csproj : error  : The imported project "C:\Program Files (x86)\MSBuild\Microsoft\WindowsXaml\v1.0\Microsoft.Windows.UI.Xaml.CSharp.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.  C:\Users\Alex\Downloads\File access sample\C#\FileAccessSample.csproj

While it is possible to open csproj file, change the “v1.0” to”v11.0” in file path and reload the project in some cases this will render following some of the following errors:

image or image

The best approach to migrate you existing application would be to start brand new “Blank Application” from Windows Metro Style templates and start pulling in your resources, XAML and code files:

image

After rebuilding your project structure, try to compile you application. It will not compile: the biggest change impacting your work will be accommodating the naming changes in Windows.UI.ViewManagement.ApplicationLayout. All names referencing “layout” were changed to “view”. Enumerations for ApplicationViewState now reflect different orientations, so ApplicationViewState.FullScreen was split up into FullScreenLandscape and FullScreenPortrait which you will have to accommodate in your code.

The event handlers for View state changes have changed and need to be migrated:

Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().ViewStateChanged +=        
    new TypedEventHandler<Windows.UI.ViewManagement.ApplicationView, Windows.UI.ViewManagement.ApplicationViewStateChangedEventArgs>(MainPage_ViewStateChanged);

Also the handler function signature changed:

void MainPage_ViewStateChanged(Windows.UI.ViewManagement.ApplicationView sender, Windows.UI.ViewManagement.ApplicationViewStateChangedEventArgs args)
{
    //...
}

This is a common part for all applications, but this is only the beginning. After dealing with this part you are expected to change/fix breaking changes in WinRT APIs – the amount of those changes and possible solution depends entirely on your application’s functionality and used APIs.

Last, but not least, you must provide new resized graphics for application package such as package logo size is 50x50 (was 56x56), new splash screen size is 620x300 (old was 624x304), badge (if your application uses it) and maybe some others.

For complete list of breaking changes, please refer to the documentation here.

That’s it for now. In next posts I will show few new (or changed) APIs in Win8 CP. Here are the next planned posts in this series (in no specific order):

1. What’s New – this post

2. Proximity – in this post I will show how to use near field communication (NFC) to transmit information between NFC-enabled devices

3. Sharing data – in this post I will overview how to share the data from your application (“Share source”). Also, I will describe how to build simplest “Share target” application which helps to debug you share source app (and was really helpful for me, since at the time I wrote this series the marketplace apps were not available and I had no app to share my data with)

4. Settings – in this post I will overview how to use the settings charm and integrate your app settings into it

5. Local and roaming data – in this post I will overview how to save the data for your application and how to use Win8 CP built-in settings sync functionality to sync your app’s settings

6. Sensors – in this post I will overview working with some sensors. Since may posts will overview using accelerometer, inclinometer and other “orientation-related” sensors (easier to bind their data to input and showcase) I decided to overview using relatively exotic one- the light sensor and how to use its data in application.

7. Licensing (Trial/Full application) and In-App purchases – in this post I will overview the different modes of the application, the products which developer can supply for in-app usage and how to debug those features before your application is published.

8. Location Service – in this post I will overview how to use location information in your app

9. Popups – in this post I will show how to use new kind of “Message Box” in Win8 CP Metro apps.

10. Live tiles, toasts and badges and Push Notifications – in this post I will overview how to keep interacting with your user, even when you application is not running anymore. Also I will touch how to deliver push notifications to your application (which used to update tiles, toasts and badges as described in “Live tiles, toasts and badges” post)

11. PLM and Custom Splash Screen – in this post I will overview product lifecycle management and custom splash screen

If you are still thinking about downloading and trying Win8 CP and VS11 Beta – think no more! Just go and download!

Stay tuned for more posts to come,

Alex

Windows Phone SDK 7.1.1 Update CTP – What’s New?

Windows Phone SDK 7.1.1 update is a minor upgrade with two primary goals: to make it easier for developers to reach more consumers and bugfixes (>500 bugs fixed). To make Windows Phone devices easier to acquire and distribute, Microsoft has permitted device manufacturers to build lower-cost devices. These devices have only 256 MB of memory and weaker CPUs. They may also have other physical limitations. For example, some devices may not have a camera. Making those devices available is vital to helping you reach more potential customers because their lower cost makes them available to more people. In addition, Microsoft added 23 more supported countries to Windows Phone Marketplace.

The speed and responsiveness of the Windows Phone OS has been one of its hallmarks since it first hit the market. Microsoft has always seen performance as critical to ensuring a high-quality user experience. To ensure newly supported devices do not suffer performance degradation due to limited memory and CPU resources, Microsoft has imposed some limitations on these devices.

Supporting low-memory devices may require you to make some changes to your code, however, the development effort required to add support for these new devices are minimized as much as possible. Many apps written for Windows Phone 7.1 will run on Windows Phone SDK 7.1.1. update, and vice versa, with no effort whatsoever. But if your app requires more memory than is available on the low-memory devices, you can choose to make it available only to high-end devices, or you can optimize your app for both types of devices.

Note: This is a pre-release version and does not contains "GO LIVE" license. You are not allowed to publish Windows Phone applications to Marketplace using this update.

Changes Manifest Declaration

If you test your app and determine that it is not supported on low-memory devices, you should also modify your app’s manifest file and specify that it requires a high-end device. This is an explicit way to tell the Windows Phone Marketplace that the app should not be permitted to run on low-memory devices.

To specify that your app is only supported on high-end devices, add a Requirement element to the new Requirements section of the WMAppManifest.xml file specifying the ID_REQ_MEMORY_90 requirement.

<Capabilities>
</Capabilities>
<Requirements>
    <Requirement Name="ID_REQ_MEMORY_90" />
</Requirements>

Note: Requirements element must appear immediately after the Capabilities element. Apps without the ID_REQ_MEMORY_90 requirement declaration will be visible to all devices, low-memory and high-end.

Although enabling support for low-memory devices makes your app available to more users, you should test your app to ensure it works well on devices with 256 MB RAM. Apps that are not optimized for the constrained environment may run out of memory or perform perceptibly slower. This can be detrimental to the user experience and may lead to poor reviews in the Marketplace. If you’re not sure whether your app runs well on a low-memory device, you should probably disable support until you have an opportunity to test and optimize your app.

To test you application behavior on low-memory device, you can use the updated performance tools provided with the Windows Phone Developer Tools update for Windows Phone Tango to emulate a low-memory environment and to analyze your app’s memory consumption. We’ll expand on this shortly.

Targeting Low-Memory Devices

low-memory devices use lower-cost hardware components that impose some physical limitations you should be aware of.

Windows Phone never allocates all the RAM to your app. On a high-end device, the OS allocates at least 90 MB RAM. On low-memory devices, it usually allocates a lot less. To determine whether the device is a low-memory device, you simply have to check whether the OS is allocating less than 90 MB. The following example shows one way to retrieve this information and make it globally available to your app.

using Microsoft.Phone.Info;
//...
public partial class App : Application
{
    public static bool IsLowMemoryDevice { get; private set; }
    public PhoneApplicationFrame RootFrame { get; private set; }
    //…
}
private void Application_Launching(object sender, LaunchingEventArgs e)
{
    try
    {
        var workingSetLimit = Convert.ToInt64(DeviceExtendedProperties.GetValue("ApplicationWorkingSetLimit"));
        IsLowMemoryDevice = workingSetLimit < 94371840; // 90 * 1024 * 1024
    }
    catch(ArgumentOutOfRangeException)
    {
        //API is not supported - Mango device
        IsLowMemoryDevice = false;
    }
}

The code asks the DeviceExtendedProperties class for the new value of the “ApplicationWorkingSetLimit” extended property (which supported starting from in Tango only). This extended property determines how much memory (measured in bytes) is available to your app while it’s active. If your app tries to use more memory than the working set limit, your app will crash with an OutOfMemoryException.

This extended property was introduced in the Windows Phone Tango patch. When you run this code on Windows Phone 7.0 and 7.1, they will not recognize the property and will throw an ArgumentOfRangeException. All the devices manufactured and distributed worldwide before the Windows Phone Tango patch are high-end devices. So if this exception occurs, you can safely assume that your app is running on a high-end device.

Devices running Windows Phone Tango will recognize the property and return the available memory. Because they might be either low-memory or standard devices, we still have to check the size of the memory allocation. If the limit is less than 94371840 bytes (90 MB), the device is a low-memory device.

To optimize the app for low memory conditions, simply call the IsLowMemoryDevice property when you need to choose whether to use an optimization:

if(App.IsLowMemoryDevice)
{
    // low-memory device optimizations
    //...
}
else
{
    // normal memory conditions
    //...
}

If you use MVVM or a similar design pattern, you should consider adding this property to a view model or a globally available service instead of the App class. For more info please refer to MSDN documentation:

http://msdn.microsoft.com/en-us/library/hh855083(v=vs.92).aspx

Background Agents

Windows Phone Tango on low-memory devices does not support periodic and resource-intensive tasks. Support for these background agents was removed to prevent the limited memory from detracting from the user experience.

If your app uses a PeriodicTask or a ResourceIntensiveTask for notifications, to update a Live Tile, or for other purposes, you have two options:

  • You can disable support for low-memory devices in the app manifest and when you submit the app to the Marketplace.
  • Or you can add code to your app that checks whether the device supports background agents, and launch them only on devices that support them.

The following code example demonstrates how to launch background agents selectively. It also accounts for situations where background agents cannot be launched for other reasons.

if(App.IsLowMemoryDevice)
{
    // do not launch general background agents
    //...
}
else
{
    try
    {
        var periodicTask = new PeriodicTask("SomePeriodicTask");
        ScheduledActionService.Add(periodicTask);
    }
    catch(InvalidOperationException)
    {
        // there are too many background agents or they are disabled
    }
}

If you try to schedule a PeriodicTask or a ResourceIntensiveTask on a low-memory device, Windows Phone Tango will throw a SchedulerServiceException. If your app absolutely relies on these agents, do not declare support for low-memory devices when you submit your app to the Marketplace.

Best Practices – Silverlight apps

When you optimize your app for low-memory devices, always keep in mind that less memory is available. In general, any task that requires significant memory or intensive processing should be tested and optimized in a 256 MB environment. The following list provides guidelines on how to avoid common pitfalls. In many cases, these guidelines will improve the performance on high-end devices as well.

  • Avoid long lists. Long lists take a lot of memory. Users usually do not need to see a full list of data. Use filtering and/or paging to optimize long lists. For example, instead of listing 1,000 items all at once, split the list into pages and show only 20 items on a page. If you only retrieve 20 items at a time from your data source, you can dramatically reduce the memory consumption of your application.
  • Use data binding with lightweight objects. When you bind the UI to objects that have a lot of data, Silverlight has to keep all the data in memory even though you might only display one or two properties. A better option is to create light weight objects intended explicitly for binding with the UI, such as view models that only have properties that are displayed. When you retrieve complex data, you can create these lightweight objects and throw away the complex data objects, thereby reducing the overall memory footprint. Using view models with the MVVM design pattern can provide additional benefits as well, such as a loosely coupled UI and better application architecture.
  • Minimize use of the WebBrowser and Bing Maps Silverlight controls on low-memory devices. If you use these controls in your app, make sure to test their use in a low-memory environment. These controls manage a lot of data, and if your app also works with a lot of data, you might reach the memory limit quickly. The best approach is to create, initialize and load the controls only when you really need them, and dispose of them as soon as you don’t need them anymore.
  • Use a smaller cache. Caching is very important, especially for connected applications. However, caching is a memory-intensive operation. You should use caching when appropriate, but limit the size of the cache. For example, if your app uses a cache that stores 200 items, you might want to consider limiting it to 50 items on low-memory devices. If you cache a few pages of results in a list that uses paging, consider caching just one or two pages instead. A smart caching strategy can improve the perceptible performance of your app even on low-memory devices.

Best Practices – XNA apps

Tooling

To get started with the patch, download and install the Windows Phone SDK Update CTP here. The patch is a minor update to the 7.1 version of the tools and can safely be installed over the previous version.

The Windows Phone SDK Update CTP release makes the transition from Windows Phone 7.1 as easy as possible for developers. Apps written for Windows Phone 7.1 will run on Windows Phone Tango, in most cases, without any changes. Also, because the Windows Phone SDK Update CTP is a minor update, most of the tools should be familiar to programmers with any Windows Phone development experience.

In fact, when you create a new Windows Phone application, Visual Studio shows you the New Windows Phone Application dialog that lets you choose the target OS for your app. This is exactly the same dialog as the one introduced with the Windows Phone 7.1 tools, and the target OS options are identical: Windows Phone OS 7.0; and Windows Phone OS 7.1, which now includes Windows Phone SDK 7.1.1 update.

clip_image002

The most significant area where you might have to invest some effort is in testing and profiling your app to ensure it runs well on low-memory devices. You do not have to purchase a low-memory device to test your app. Microsoft has added and improved a couple of tools to help you do this effectively.

The Emulator

The first difference you’ll probably notice after installing the Windows Phone Developer Tools update is that the Target Device dropdown next to the Run button on the Standard toolbar has changed a bit. The “Windows Phone Emulator” option has been renamed to “Windows Phone Emulator - 512 MB” and a new option “Windows Phone Emulator - 256 MB” has been added.

clip_image004

When you test your app on the 256 MB emulator, it mimics the memory constraints of a low-memory device. Run your app and play around with it a bit on this emulator to get a feel for how your app behaves in this environment. If it feels sluggish or freezes often, or if it crashes, it means your app needs more memory than low-memory devices can allocate. You’ll have to invest some effort in optimizing it for low-memory devices.

You can also run both emulators (the 256 MB and 512 MB emulators) side by side. Running both emulators at the same time can help you test apps that have divergent code for low-memory and standard devices by providing quick feedback for both environments.

clip_image006

DebugInfo

To ease on debugging on different device types, I’ve created reusable libraries (for Silverlight, XNA and Silverlight/XNA hybrid type of application).

Those libraries provides easy integration and visible cue for device type, used memory and memory constraints:

Integrating into Silverlight app is as easy as adding number lines of code:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    MemoryInfo mi = new MemoryInfo();
    mi.UpdateInterval = 1;
    mi.IsEnabled = true;
    base.OnNavigatedTo(e);
}

Silverlight:

imageimage

XNA page in hybrid app integration requires to use UIElementRenderer:

DebugInfo.Hybrid.MemoryInfo mi = new DebugInfo.Hybrid.MemoryInfo();
mi.IsEnabled = true;
LayoutRoot.Children.Add(mi);

Note: More info about UIElementRenderer can be found in my older blog post here.

XNA page in hybrid app:

imageimage

XNA game integration – as any usual XNA’s DrawableGameComponent:

MemoryInfo mi;
//...
mi = new MemoryInfo(this);
Components.Add(mi);

XNA game:

imageimage

Note: XNA screenshot taken using 512MB emulator. On 256MB emulator “Low memory device” warning appears also.

General note: All overlay appears only when debugged.

The libraries can be downloaded from here.

 

Stay tuned for more!

Alex

p.s. I owe the word of apology to Noam Kfir, my colleague at Sela and good friend who helped me a lot and actually wrote some parts of initial material I used in this post. Sorry dude – I was in rush publishing stuff out, and forgot to mention your work in first version of this article.

Dear reader…

My dear blog reader! For quite some time my blog being updated pretty slow and new posts are almost not showing up.

I have to apologize for it!

I never forgot about my readers!

Lastly I’ve got few personal mails with the obvious questions arise – why blog is not active as it was? What happened to me? When can we expect new posts?

Let me answer those questions.

Few months ago I’ve been relocated to Redmond, WA (USA) and from my new interesting position hope to serve you, my readers, even better than before. This happened right after //BUILD conference and naturally shifted my attention from blog a little bit.

Also, during this shift I’ve been working on some very interesting projects which were (and currently still are) under very tight NDA. Some of work I am doing will be revealed shortly and I will be able to blog about it and many interesting things I’ve learned during this time; some things I will not be able to reveal soon. Still, I believe that better later than never!

During this time I took a part in launching Seattle chapter of vNext Developer Community – the community driven by the developers and for the developers focusing on new technologies. If you are in the area, you welcome to join our community and take a part in our monthly meetings.

 

Stay tuned to massive blog post series coming soon.

image

Posted by Alex Golesh | with no comments