Monday, July 26, 2010

Updated Win Phone 7 Busy Service That Does Not Change RootVisual

Evidently, you are not supposed to change the RootVisual in a Win Phone 7 application.  There was a forum post posed by Matt Hidinger and answered by Peter Torr about Changing the RootVisual that indicates this might cause problems during the approval process.


As a result, I've updated my sample to not require wrapping the root level PhoneApplicationFrame with a Grid.  It turns out, most pages already have a Grid as their LayoutRoot element and you can just pass this in to a new RootPaneBusyService and use it in the same manner.  So instead of passing the App.Current.RootVisual as Grid, we pass the LayoutRoot.

protected override void OnNavigatedTo( System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            
            var busyServ = new RootPanelBusyService(this.LayoutRoot);
            this.DataContext = new MainPageVM(busyServ);            
        }

I've also updated the CreateBusyElement method to detect how many rows and columns are in the passed in root visual and set the ColumnSpan and RowSpan accordingly.

// Set the row and column span's if the root is a grid.
            if (rootVisual is Grid)
            {
                var gridRoot = rootVisual as Grid;

                int rowSpan = 1;
                if (gridRoot.RowDefinitions != null)
                    rowSpan = gridRoot.RowDefinitions.Count + 1;
                int columnSpan = 1;
                if (gridRoot.ColumnDefinitions != null)
                    columnSpan = gridRoot.ColumnDefinitions.Count + 1;

                Grid.SetRowSpan(root, rowSpan);
                Grid.SetColumnSpan(root, columnSpan);
            }

Download the updated sample



Create a Win Phone 7 Busy Service with Loady animation

Update: It looks like there may be a problem with wrapping the RootFrame in a Grid, but I haven't found a good answer to why.  Check this Win Phone 7 Forum Post about Changing the RootVisual.



Update 2: I've updated my example to not require changing the RootVisual.  But you will still need to have a Grid as the Root of your Page.  Check out the Updated Busy Service.



I've been spending a lot of time working on data driven Win Phone 7 apps in the past couple weeks and one of the most useful things I've created is a simple Busy Service for Win Phone 7 apps.


The Busy Service lets you signal that something is busy, like when retrieving data from a web service or doing a long running calculation. Here is a look at it's interface;

/// <summary>
/// An interface for busy services.
/// </summary>
public interface IBusyService
{
bool IsBusyShown { get; }

void ShowBusy(string message);
void ShowBusy(TimeSpan timeToShow, string message);
void ShowBusy(double milliSecondsToShow, string message);

void HideBusy(double millisecondsToWait);
void HideBusy(TimeSpan delay);
void HideBusy();
}



I decided to implement a RootPanelBusyService, which basically means that it expects a "Panel" type of element, a Grid for example, at the RootVisual. The problem with the Win Phone 7 Beta update is that the RootVisual is no longer defined in the App.Xaml, so it would seem like you can't just wrap the Root level Phone Application Frame in a Grid. It turns out, it just takes a little more work, if you open up the App.xaml.cs and take a look at the CompleteInitializePhoneApplication method you will see where the RootVisual is assigned. So with a little code we can add a Grid wrapper around the PhoneApplicationFrame and have a nice place to add our Busy Loady icon.

// Do not add any additional code to this method
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
{
// Add a Grid wrapper around the Root application frame.
var root = new Grid();
root.Children.Add(RootFrame);

RootVisual = root;
}

// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}



Now that we have our Grid at the Root, we can instantiate a RootPanelBusyService and pass it to our ViewModel to start "Getting Busy...".

// From MainPage.xaml.cs
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);

if (App.Current.RootVisual is Grid)
{
var busyServ = new RootPanelBusyService(App.Current.RootVisual as Grid);
this.DataContext = new MainPageVM(busyServ);
}
}

// In our MainPage View Model (MainPageVM.cs)
/// <summary>
/// A view model for the Main Page.
/// </summary>
public class MainPageVM : ViewModelBase
{
/// <summary>
/// The busy service to use...
/// </summary>
private IBusyService busyService;

/// <summary>
/// Gets or sets the do busy command.
/// </summary>
/// <value>The do busy command.</value>
public ICommand DoBusy { get; private set; }

public MainPageVM(IBusyService busyServ)
{
busyService = busyServ;

DoBusy = new DelegateCommand&lt;object>(HandleDoBusy);
}

/// <summary>
/// Handles the do busy command.
/// </summary>
/// <param name="arg">Unused.
private void HandleDoBusy(object arg)
{
busyService.ShowBusy(2000, "Getting Busy...");
}
}


And there you have it, you've got a simple and easy to use busy service for your long running operations. One other trick that I occasionally use it to set a Busy Service static property on my App class, so all ViewModels have easy access to it. To do this, just add a static property to your App class, and assign it after setting the RootVisual, like so:

// Easy access to the busy service.
public static IBusyService BusyService { get; private set; }

// Rest of this class omitted for brevity....

// Do not add any additional code to this method
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
{
// Add a Grid wrapper around the Root application frame.
var root = new Grid();
root.Children.Add(RootFrame);

RootVisual = root;

// Set our easy access busy service property.
BusyService = new RootPanelBusyService(root);
}

// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}


If you don't want to download the full example project to get the RootPanelBusyService implementation, here is the source, but you'll need to create your own Loady graphic / control.

/// &lt;summary>
/// An interface for busy services.
/// </summary>
public interface IBusyService
{
bool IsBusyShown { get; }

void ShowBusy(string message);
void ShowBusy(TimeSpan timeToShow, string message);
void ShowBusy(double milliSecondsToShow, string message);

void HideBusy(double millisecondsToWait);
void HideBusy(TimeSpan delay);
void HideBusy();
}

/// <summary>
/// A busy service that expects a Panel / Grid as the root visual.
/// </summary>
public class RootPanelBusyService : IBusyService
{
private UIElement currentBusyElement;
private Panel rootVisual;

/// <summary>
/// Initializes a new instance of the &lt;see cref="RootPanelBusyService"> class.
/// </see></summary>
/// The root visual element.
public RootPanelBusyService(Panel rootVisualElement)
{
rootVisual = rootVisualElement;
}

#region IBusyService Members

private bool _busyShown = false;
public bool IsBusyShown
{
get { return _busyShown; }
}

public void ShowBusy(double milliSecondsToShow, string message = "Loading...")
{
ShowBusy(TimeSpan.FromMilliseconds(milliSecondsToShow), message);
}

public void ShowBusy(TimeSpan timeToShow, string message = "Loading...")
{
ShowBusy(message);

ThreadPool.QueueUserWorkItem(new WaitCallback(time =>
{
Thread.Sleep((TimeSpan)time);

// Hide the busy after a sleep.
rootVisual.Dispatcher.BeginInvoke(() => HideBusy());
}), timeToShow);
}

public void ShowBusy(string message = "Loading...")
{
_busyShown = true;
var busyEl = CreateBusyElement(message);
busyEl.Opacity = 0;

try
{
if (rootVisual is Panel)
{
var root = rootVisual;
root.Children.Add(busyEl);

var anim = CreateAnimation(busyEl, Grid.OpacityProperty, 0.8, 0.00, 900, EasingMode.EaseOut);

var story = new Storyboard();
story.Children.Add(anim);
story.Begin();
}
}
catch
{
Debug.WriteLine("Problem showing busy.");
}

currentBusyElement = busyEl;
}

public void HideBusy(double milliseconds)
{
HideBusy(TimeSpan.FromMilliseconds(milliseconds));
}

public void HideBusy(TimeSpan delay)
{
var root = rootVisual;
ThreadPool.QueueUserWorkItem(new WaitCallback((arg) =>
{
Thread.Sleep(delay);

((Panel)arg).Dispatcher.BeginInvoke(() =>
{
HideBusy();
});
}), root);
}

public void HideBusy()
{
try
{
if (rootVisual is Panel)
{
var root = rootVisual;
var anim = CreateAnimation(currentBusyElement, Grid.OpacityProperty, 0.00, .80, 1000, EasingMode.EaseOut);

var story = new Storyboard();
story.Children.Add(anim);
story.Begin();

// Remove the element a second later; I do it this way because StoryCompleted is flaky.
ThreadPool.QueueUserWorkItem(new WaitCallback(obj =>
{
Thread.Sleep(900);

root.Dispatcher.BeginInvoke(() =>
{
root.Children.Remove(currentBusyElement);

currentBusyElement = null;
});
}), null);                  
}
}
catch
{
Debug.WriteLine("Problem hiding busy.");
}

_busyShown = false;
}

#endregion


/// <summary>
/// Creates the busy element; A Loady with a text block underneath.
/// </summary>
/// The message to show below the loady.      
private UIElement CreateBusyElement(string message = "Loading...")
{
// create a grid to hold the loady and swallow the entire screen.
var root = new Grid()
{
Background = new SolidColorBrush(Colors.Black)
};

// The spinny loady thingy...
root.Children.Add(new Loady()
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
});

// The text below the loady...
root.Children.Add(new TextBlock()
{
Text = message,
Margin = new Thickness(0, 150, 0, 0),
FontSize = 30,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = System.Windows.VerticalAlignment.Center,
Foreground = new SolidColorBrush(Colors.White)
});

return root;          
}

/// <summary>
/// A helper method for creating double animations.
/// </summary>
public DoubleAnimation CreateAnimation(DependencyObject obj, DependencyProperty prop, double value, double milliseconds, EasingMode easing = EasingMode.EaseOut)
{
var from = Convert.ToDouble(obj.GetValue(prop));
return CreateAnimation(obj, prop, value, from, milliseconds, easing);
}

/// <summary>
/// A helper method for creating double animations.
/// </summary>
public DoubleAnimation CreateAnimation(DependencyObject obj, DependencyProperty prop, double value, double from, double milliseconds, EasingMode easing = EasingMode.EaseOut)
{
CubicEase ease = new CubicEase() { EasingMode = easing };
DoubleAnimation animation = new DoubleAnimation
{
Duration = new Duration(TimeSpan.FromMilliseconds(milliseconds)),
From = from,
To = value,
FillBehavior = FillBehavior.HoldEnd,
EasingFunction = ease
};
Storyboard.SetTarget(animation, obj);
Storyboard.SetTargetProperty(animation, new PropertyPath(prop));

return animation;
}
}




Now Playing: The Roots - Get Busy

Monday, July 19, 2010

Authenticating MVC 2 JSON Services from Win Phone 7 with OAuth Lite


One of the challenges I've been facing with using an ASP.Net MVC 2 site to return JSON data to my Win Phone 7 app is authenticating my requests. I don't want other people to be able to sniff on the data between my app and the server and start making malicious requests.


I started looking into a custom AuthorizeAttribute after spending most of last week looking into OAuth authentication methods for the FatSecret.com API service wrappers. If you're not familiar with OAuth, take a minute to read up on it.


I decided that I wasn't going to use the full OAuth authentication strategy, specifically, I boiled it down to three parameters and an HMACSH1 Hashed Signature. Basically, my signature format is this {HTTPMethod}&{ServiceURL}&{LimitedParmString}. Where my LimitedParmString consists of a standard OAuth "Nonce", a Timestamp (seconds from Jan 1, 1970 UTC), and your consumer key. My client (a Win Phone 7 app in this case) includes this signature as a request header called "oauthlite" when they make their service requests to the MVC Website.


On the server side, my custom AuthorizeAttribute named OAuthLiteAuthenticate takes the HTTPContextBase and constructs a signature based on a request header called "oauthlite". The custom request header is just a url encoded parameter string with the 4 parameters; the "Nonce", Timestamp, Consumer Key and Signature Hash computed by the requestor.


Here is an example "oauthlite" header:



You can get the gist of the authentication process by taking a look at this in-memory implementation of an OAuthLiteAuthenticator:




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace OAuthLite.Server
{
/// <summary>
/// An in memory OAuthLite Authenticator example.
/// </summary>
public class OAuthLiteDictionaryAuthenticator : IOAuthLiteAuthenticator
{
// An "in-memory" key repository.
private Dictionary<string, string> _keyRepository = new Dictionary<string, string>()
{
// We only have one client, but you could add more.
{ "8b84444fbc09448da68ca14f000695cb", "83fbf4ea05df427bb3390d6d9a0fc626" }
};

private static string UrlFormat = "{0}://{1}";

#region IOAuthLiteAuthenticator Members

public bool Authenticate(HttpContextBase context)
{
if (context == null
|| context.Request == null)
return false;

var requestUrl = context.Request.Url;

var apiUrl = String.Format(UrlFormat, requestUrl.Scheme, requestUrl.Host);
var method = (HttpMethod)Enum.Parse(typeof(HttpMethod), context.Request.HttpMethod.ToUpper());

var header = context.Request.Headers["oauthlite"];

// We expect a parmstring including the signature.
if (String.IsNullOrEmpty(header)
|| !header.Contains("&"))
return false;

Dictionary<string, string> parms = new Dictionary<string, string>();
foreach (var parm in header.Split('&'))
{
string[] s = parm.Split('=');
if (s.Length < 2)
continue;

parms.Add(s[0], s[1]);
}

if (!parms.ContainsKey("signature") || !parms.ContainsKey("key"))
return false;

var signature = HttpUtility.UrlDecode(parms["signature"]);
parms.Remove("signature");

var key = parms["key"];

var secret = GetSecretByKey(key);

if ( string.IsNullOrEmpty(secret) )
return false;

return OAuthLiteHelper.ValidateSignature(signature, method, apiUrl, secret, parms);
}

private string GetSecretByKey(string key)
{
if (!_keyRepository.ContainsKey(key))
return null;

return _keyRepository[key];
}

#endregion
}
}



I've updated the Win Phone 7 JSON Services code example to use the new OAuthLiteAuthenticate attribute on an action called "Secrets" on the Product Controller. You can download the example below.




Download the code





Now Playing: Foals - Spanish Sahara