WP Simplified: Data Providers for Real vs. Mock Data

This post will bring closure to the framework components of my series WP Simplified. We’ll be covering, at very high level, how to architect data providers in Windows Phone 7.5 and a good pattern for supporting real and mock/design time data. I discuss storage options in a post about Tombstoning. Due to simplicity I’ve chosen to use IsolatedStorage for storage.

Object References

Let’s first discuss what the best approach for developing a data provider is. Understanding that each app is going to require a completely custom build data provider there is one rule that I feel very strongly about. That rule is that if you have a reference to an object that is stored in persistent storage you should not create a second reference to that object inside of the same app. In other words, when you ask a data provider for an object it should always return the same object and not a different instance. We ran into a huge problem with modifying the incorrect copy of an object due to the pattern we were following for navigation. I mentioned this problem in WP7 Simplified: CoreApplicationService (Navigation) under the section titled Lesson #1: Centralize Data.

So, what’s the solution? Cache objects in memory for the lifetime of the app and always return the same reference to an object from the cache if it exists or create a reference to an object and store it in cache. We found that using ReadOnlyObservableCollection<T> allowed us to be pretty flexible. In phone apps users are typically viewing lists of data or data from a list and they want to know immediately if new data is available. By using Observable Collections the list is automatically updated when new data is available. The reason for use ReadOnlyObservableCollections is so the data provider can hand off a collection to a VM and the VM will not be able to add any new items to that collection directly. Instead the VM must utilize the data provider for CRUD actions on the collection.

Data Provider in Lion Heart

We use Initialize rather than the Constructor in our PhoneDataProvider for data setup. This allowed us to think in one pattern for VMs and DataProviders rather than jumping around and forgetting when data is setup and where that logic lives. Until Initialize is called the data provider is not useful and will throw errors.

public class PhoneDataProvider : IDataProvider
{
	private static readonly string CLIENTS_KEY = "CLIENTS_KEY";
	private static readonly string SESSION_NOTES_KEY = "SESSION_NOTES_KEY";
	private static readonly string SESSIONS_KEY = "SESSIONS_KEY";

	private bool _isInitialized;
	private ObservableCollection<Client> _clients;
	private ObservableCollection<SessionNotes> _sessionNotes;
	private ObservableCollection<Session> _sessions;
	private ReadOnlyObservableCollection<Client> _clientsReadOnly;
	private ReadOnlyObservableCollection<SessionNotes> _sessionNotesReadOnly;
	private ReadOnlyObservableCollection<Session> _sessionsReadOnly;

	public void Initialize()
	{
		if (_isInitialized)
		{
			return;
		}

		if (!RetrieveValue(CLIENTS_KEY, out _clients))
		{
			_clients = new ObservableCollection<Client>();
		}
		if (!RetrieveValue(SESSION_NOTES_KEY, out _sessionNotes))
		{
			_sessionNotes = new ObservableCollection<SessionNotes>();
		}
		if (!RetrieveValue(SESSIONS_KEY, out _sessions))
		{
			_sessions = new ObservableCollection<Session>();
		}

		SaveChanges();
		_isInitialized = true;
	}
	
	private bool RetrieveValue<T>(string key, out T value)
	{
		return IsolatedStorageSettings.ApplicationSettings.TryGetValue(key, out value);
	}
	
	public void SaveChanges()
	{
		SaveValue(CLIENTS_KEY, _clients);
		SaveValue(SESSION_NOTES_KEY, _sessionNotes);
		SaveValue(SESSIONS_KEY, _sessions);
	
		Save();
	}
	
	private void SaveValue<T>(string key, T value)
	{
		IsolatedStorageSettings.ApplicationSettings[key] = value;
	}
}

A few important things to note here.

  1. Only Initialize if the data provider is not initialized.
  2. Retrieve collections from IsolatedStorage or create them if they do not exist. These collections are now the cache of objects that should be queried for the rest of the app lifetime.
  3. Call SaveChanges to persist any newly created collections. (This may or may not really be necessary.)

When a VM requires a collection of objects it must request that collection via a method. In LionHeart we will use Client as an example. To get the collection of Clients from the data provider the method GetClients must be used.

public class PhoneDataProvider : IDataProvider
{
	public ReadOnlyObservableCollection<Client> GetClients()
	{
		if (_clientsReadOnly == null)
		{
			_clientsReadOnly = new ReadOnlyObservableCollection<Client>(_clients);
		}
		return _clientsReadOnly;
	}
}

When a new object is created and needs to be added to a collection expose a method to handle this functionality. We do this with a new Client via the AddClient method.

public class PhoneDataProvider : IDataProvider
{
	public void AddClient(Client newClient)
	{
		if (newClient != null)
		{
			_clients.Add(newClient);
			SaveChanges();
		}
	}
}

Deleting an object should be handled the same way as add. LionHeart has no need for deleting so I don’t have any example to show.

Finally, if an existing object is modified the only action required is to call SaveChanges.

public class PhoneDataProvider : IDataProvider
{
	public void SaveChanges()
	{
		SaveValue(CLIENTS_KEY, _clients);
		SaveValue(SESSION_NOTES_KEY, _sessionNotes);
		SaveValue(SESSIONS_KEY, _sessions);
	
		Save();
	}
}

When modifying objects and calling SaveChanges on the data provider one might think of Entity Framework or LINQ to SQL. Those frameworks were actually used as inspiration for the data provider pattern used. It is important to know that LINQ to SQL is supported in Windows Phone 7.5 and I shamefully have had no time to look into using it. I fully admit that the data provider architecture in LionHeart could and probably be improved.

Real (Production) vs. Mock (Design) Data Providers

I had some help from Bryan Coon and Tim Askins coming up with a pattern that would easily support using Blend with mock data without the need for a bunch of crazy garbage code. In the first code snippet shown you’ll notice PhoneDataProvider implements the interface IDataProvider.

public interface IDataProvider
{
	void Initialize();
	void SaveChanges();
	void AddClient(Client newClient);
	void AddSessionNotes(SessionNotes newSessionNotes);
	void AddSession(Session newSession);
	Client GetClient(Guid clientId);
	Session GetSession(Guid sessionId);
	SessionNotes GetSessionNotes(Guid sessionNotesId);
	ReadOnlyObservableCollection<Client> GetClients();
	ReadOnlyObservableCollection<SessionNotes> GetSessionNotes();
	ReadOnlyObservableCollection<Session> GetSessions();
	Session GetSessionForSessionNotes(Guid sessionNotesId);
}

By doing this we open up the opportunity to use the Locator pattern. LionHeart includes DataProviderLocator which simply chooses which IDataProvider to hand out.

public class DataProviderLocator
{
	private static IDataProvider _dataProvider;
	public static IDataProvider DataProvider
	{
		get
		{
			if (_dataProvider == null)
			{
				if (DesignerProperties.IsInDesignTool)
				{
					_dataProvider = MockDataProvider.Instance;
				}
				else
				{
					_dataProvider = PhoneDataProvider.Instance;
				}
				_dataProvider.Initialize();
			}
			return _dataProvider;
		}
	}
}

In this case only two IDataProvider implementers exist: PhoneDataProvider and MockDataProvider. If the app is in a design tool then the MockDataProvider is used otherwise the PhoneDataProvider is used.

Now the only weird part, and I’m still trying to find a better way to do this, is setting up VMs for mock data. Usually the Initialize method of a VM is called to set it up with data, however Initialized will never be called in design tool. In this case we still must modify the VM we desire to use in Blend by adding a query to the DataProvider in the Constructor of the VM.

public class ClientPageVM : PageViewModel
{
	public ClientPageVM()
	{
		if (DesignerProperties.IsInDesignTool)
		{
			InitializeData(DataProviderLocator.DataProvider.GetClients().First().Id);
		}
	}
	
	private void InitializeData(Guid clientId)
	{
		Client = ViewModelFactory.CreateClientVM(DataProviderLocator.DataProvider.GetClient(clientId));
		...
	}
}

This code will only be run when the app is in a design tool so it’s safe to leave in. One could argue that it should be optimized out in release mode and I think that would not be a bad idea.

Something interesting to note here is that the InitializeData method is used as a single point of entry whether or not the app is in a design tool or not. There is a huge benefit to this. Reduced duplicated code. If the way the VM handles the client id changes, the updates only need to be applied in one location. DONE!

Conclusion

This post ends coverage of Framework Elements in LionHeart. The other cool part about finishing this post is the next step is to provide a project template that will provide all the Framework Elements discussed in this series automatically.

Make Portable Libraries Your Go-To Project Template

With the upcoming release of Visual Studio 2012, Portable Libraries will finally be built into the product. The 2012 iteration of Portable Libraries is much improved from the prior versions, adding not only new platforms (Windows 8 Metro) but greatly increasing the list of available framework library classes that can be used across the various platforms.

Continue reading

WP7 Simplified: PageViewModel, ViewBase, and Using CoreApplicationService

In this post continuing my series WP7 Simplified we’ll be covering the PageViewModel class. This ViewModel (VM) hides all of the complicated wiring up that is necessary to work with ViewBase as mentioned in my post WP7 Simplified: CoreApplicationService (Navigation). PageViewModel is an abstract class that acts as the base class for all VMs backing Views (aka Pages). The three key parts to this class are: Initialize, Uninitialize, and hooking into the CoreApplicationService (CAS).

Initialize

ViewModels need to be built up based on navigation parameters in Windows Phone. Because of this it is more flexible to use an Initialize and Uninitialize vs. Constructor and Dispose pattern. Derived VMs can be defined as the DataContext in the XAML of Views that inherit from ViewBase.

<Views:ViewBase x:Class="LionHeart.UI.Phone.Views.HomeView"
				… >
	<Views:ViewBase.DataContext>
		<vms:HomeVM />
	</Views:ViewBase.DataContext>
</Views:ViewBase>

When the view is navigated to the OnNavigatedTo method is called. If this view has a constructed PageViewModel as the DataContext it is initialized here.

public class ViewBase : PhoneApplicationPage
{
	public PageViewModel PageViewModel { get { return DataContext as PageViewModel; } }

	protected override void OnNavigatedTo(NavigationEventArgs e)
	{
		if (PageViewModel != null)
		{
			var parameters = e.NavigationMode == NavigationMode.Back ? null : NavigationContext.QueryString;
			PageViewModel.Initialize(parameters);
		}
		base.OnNavigatedTo(e);
	}
}

Notice that the NavigationContext.QueryString is only passed into Initialize if this navigation is NavigationMode.New. This is important. When the user navigates forward in the app the NavigationMode will be New. In this case the NavigationContext.QueryString is required because the VM has never been initialized before and has no data. However, if the NavigationMode is Back then this VM has already been initialized with data so we only need to initialize the VM without data.

public abstract class PageViewModel : ViewModelBase
{
	public void Initialize(IDictionary<string, string> parameters = null)
	{
		InitilizeAlways();
		if (!IsInitialized)
		{
			bool isLaunching;
			CoreApplicationService.TryRetrieveTombstoningValue(
				CoreApplicationService.IS_LAUNCHING_KEY, out isLaunching);
			if (parameters != null &&
				(parameters.ContainsKey(CoreApplicationService.IS_RUNNING_KEY) || isLaunching))
			{
				InitializeFromNavigation(parameters);
			}
			else if (!HasState)
			{
				InitializeFromActivation();
			}
			HasState = true;
			IsInitialized = true;
		}
	}
	
	protected virtual void InitilizeAlways()
	{
		SubscribeGlobalHandlers();
	}

	protected virtual void InitializeFromNavigation(IDictionary<string, string> parameters) { }

	protected virtual void InitializeFromActivation() { }
	
	protected virtual void InitializeWithState() { }
	
	protected virtual void SubscribeGlobalHandlers()
	{
		if (!IsSubscribed)
		{
			CoreApplicationService.Deactivated += DeactivatedHandler;
			CoreApplicationService.Closing += ClosingHandler;
			IsSubscribed = true;
		}
	}
}

Inside of Initialize there are a few actions to perform. The first is to initialize anything that needs to always be initialized via the virtual method InitializeAlways. Generally nothing should be done here except hookup Global event handlers with the virtual method SubscribeGlobalHandlers. SubscribeGlobalHandlers is always called inside of InitializeAlways however, the code inside is only run if the property IsSubscribed is false. IsSubscribed is false if the VM has not been initialized or if it has been torn down.

After InitializeAlways a check is performed to know if the VM is Initialized or not. If it is not then execution continues. The next check is to discover if the VM is being initialized because it the app was launched or from some other action while the app was running. The launched indicator is set in the CAS when it is initialized. The first VM to be initialized is responsible for removing this key from the Tombstoning values.

public class CoreApplicationService
{
	public void Initialize(PhoneApplicationFrame frame, bool isLaunching)
	{
		…
		StoreTombstoningValue(IS_LAUNCHING_KEY, isLaunching);
	}
}

The is running key is set each time the CoreApplicationService.Navigate method is called. If the VM is being initialized from launching or running then the parameters dictionary is passed to the virtual method InitializeFromNavigation. This method would be overridden by VMs that need data from the parameters, which is the NavigationContext.QueryString dictionary. If this method was called a few things can be inferred. The first is that this is a new, or forward navigation and this VM has never been initialized before meaning it has no state.

If the VM is not initializing from launching or running then it is initializing from deactivation. When coming from deactivation there are two possibilities: State is preserved or state was disposed. The VM uses the HasState property to know if state exists. This property is set to true at the end of Initialize and is never set to false. The only time the property would be false is if the VM was newly constructed and not yet initialized. If HasState is true then the virtual method InitializeWithState is called. Here is where we would do very very minimal code to refresh data that could have changed since deactivation. Usually, no work should be done in this method unless absolutely necessary. Fast Application Switching relies on as little code being run as possible when state has been preserved. If HasState is false then the virtual method InitializeFromActivation is called. When this method is called we know that no state exists and we need to access Tombstoning values through the CAS.

Just before exiting Initialize the HasState and IsInitialized properties are both set to true. At this point the VM is initialized and ready to go. If data needs to be pulled from web servers or other long running tasks during initialization make sure that the work is done async and that a busy indicator and message is used in place of the missing data. This is better than showing a blank page because most users will think a blank page is a broken page. Also, please cache data when possible. No need to store the entire web on the phone, but don’t make the user wait to see data every time the app is loaded. This is a huge complaint of mine when I use apps that display RSS feeds and each time I load the app the entire feed must be pulled from the server before I can see the articles I was reading a few minutes prior.

Uninitialize

There are three different time when the VM should be uninitialized: Navigating forward to a new view or out of the app, navigating backward, and when the view is removed from the navigation back stack. The VM is notified about all three of these cases by the ViewBase class.

public class ViewBase : PhoneApplicationPage
{
	protected override void OnNavigatedFrom(NavigationEventArgs e)
	{
		if (PageViewModel != null)
		{
			PageViewModel.Uninitialize(e.NavigationMode == NavigationMode.Back);
		}
		base.OnNavigatedFrom(e);
	}
	
	protected override void OnRemovedFromJournal(JournalEntryRemovedEventArgs e)
	{
		if (PageViewModel != null)
		{
			PageViewModel.Uninitialize(true);
		}
		base.OnRemovedFromJournal(e);
	}
}

The OnNavigatedFrom method is used to indicate navigation forward or backward events from this view. If the navigation is a back navigation then indicate to Uninitialize that a tear down is requested. The OnRemovedFromJournal method indicates that the view has been removed from the back stack and will not be used again. Uninitialize is immediately called with the tear down flag.

A question to ask here is should the VM always be uninitialized when it’s not in use? Or should it just have a pause setting? In this approach uninitializing the VM is always desired when it is not in use, but tearing down the VM is not. Tear down means the VM will never be used again, essentially it’s the VM dispose method without actually using the dispose pattern.

public abstract class PageViewModel : ViewModelBase
{
	public void Uninitialize(bool isTearDown = false)
	{
		UninitializeAlways();
		if (isTearDown)
		{
			UninitializeTearDown();
		}
		IsInitialized = false;
	}
	
	protected virtual void UninitializeAlways() { }
	
	protected virtual void UninitializeTearDown()
	{
		UnsubscribeGlobalHandlers();
	}

	protected virtual void UnsubscribeGlobalHandlers()
	{
		if (IsSubscribed)
		{
			CoreApplicationService.Deactivated -= DeactivatedHandler;
			CoreApplicationService.Closing -= ClosingHandler;
			IsSubscribed = false;
		}
	}
}

Uninitialize first calls the UnitializeAlways virtual method. Work done in UninitializeAlways should be things like pausing media elements, timers, and web service queries. Also, unhooking any handlers that should not be handled while the view is not in use. It’s important to know that views that are in the back stack still run code. They are not dormant or prevented from executing code, therefore any handlers that could cause code to run should generally be disabled. A possible candidate to ignore this might be a live feed that is expected to continue updating even while not on that view. Performance and battery life and the two reasons for this rule.

After UninitializeAlways if the isTearDown argument is true then the UninitializeTearDown virtual method is called. This method should be used to clean up anything on the page that needs to be disposed of, closed, etc. UninitializeTearDown is responsible for calling the virtual method UnsubscribeGlobalHandlers which simply unsubscribes from events such as app deactivating, closing, and error notifications. Nice and straight forward.

At the end of Uninitialize the property IsInitialized is set to false. There is no need to if the VM was torn down or not because a torn down VM will never be used again.

Integrating with the CoreApplicationService

The CAS is available as a singleton anywhere in the app, but we have found it nice to have a simple property in the VM to call. This also aids in replacing the CAS if needed with something else. As you saw above in the SubscribeGlobalHandlers method the VM subscribes to the Closing and Deactivated events on the CAS. These events are also not unsubscribed to unless the VM is being torn down. When either of these events are raised by the app the CAS receives then and passes them on to whom ever desires them, in this case the VM cares. The PageViewModel then calls a virtual method related to the event that was raised. If the Closing event is raised the PageViewModel calls the related virtual method Close. If the Deactivated event is raised the PageViewModel calls the virtual method Deactivate. This just aids in exposing methods and events that should be used in almost every VM that inherits from PageViewModel. The argument was proposed to make the methods abstract forcing derived types to implement the methods, but was eventually lost to keep them virtual and hope the developer is responsible and thinks through the flow of data and the app.

public abstract class PageViewModel : ViewModelBase
{
	protected CoreApplicationService CoreApplicationService
	{
		[DebuggerStepThrough]
		get { return CoreApplicationService.Instance; }
	}
	
	protected virtual void Deactivate()
	{
	}
	
	private void DeactivatedHandler(object sender, EventArgs e)
	{
		Deactivate();
	}
	
	protected virtual void Close()
	{
	}
	
	private void ClosingHandler(object sender, EventArgs e)
	{
		Close();
	}
}

The Close method is called when the user uses the back button of the phone to leave the app. In this method all data that needs to be persisted for future runs of the application need to be saved. Data that is transient like form data can be ignored.

The Deactivated method is called when the user hits the Windows logo button, or touches a notification that opens another app such as an SMS notification opens the Messaging app. This happens a lot more often than Close and needs special attention in each VM that is in the back stack or in use when called. In this method all persistent data needs to be saved in persistent storage and all transient data such as form data needs to be stored in transient storage. More information about the difference between persistent and transient storage and what data goes where read my post Windows Phone 7 – Tombstoning. Use the CAS to store transient data with the StoreTombstoningValue method.

public class CoreApplicationService
{
	public void StoreTombstoningValue(string key, object value)
	{
		PhoneApplicationService.Current.State[key] = value;
	}
}

In my experience with apps in the market place this is one area where most of them are only OK. I could spend hours discussing proper ways to handle app lifecycle and data flow, but I will spare you in this post.

Conclusion

Armed with everything you’ve learned here and in previous posts about the CAS you’re ready to start your very own MVVM Windows Phone application. App ideas, of course, must be supplied by your own creative juices, but there’s no shame in copying an idea for the purposes of learning. This post also brings us one post closer to having complete coverage of the Framework Components introduced in LionHeart.

Code Camp: Windows Phone 7.5 Simplified

First off thank you to everyone who attended my session at code camp. I hope you learned something new! I’d love to get feedback on how you felt the presentation went. Please leave me some comments here or shoot me an email or whatever.

In this session I used an app codenamed LionHeart to help explain the concepts and framework I presented. To learn more about the framework I have a blog series named Simplifying the Windows Phone Development Experience! Codename: LionHeart. In this series I go in much more depth about the same concepts I presented on.

Please note the source code release does not have fully completed app however, the framework used in the app is stable and the API’s are almost locked down.

When the framework is complete it will be available in Project Template form. This will allow you to easily start a new Windows Phone project with all of the boring boiler plate work completed for you.

Presentation Slide Deck

Source Code: LionHeart (codecamp release)

Cheers! And Happy Coding!

WP7 Simplified: CoreApplicationService (Tombstoning)

This post brings closure to coverage of the CoreApplicationService (CAS) in this series titled WP7 Simplified. The final topic to cover is how the CAS handles tombstoning. Before getting into that let’s discuss what tombstoning is first. There are a couple of different terms to be familiar with that all refer to the tombstoning process: App Deactivation, Fast Application Switching, Tombstoning.

App Deactivation

When ever a user navigates leaving the app, except when they use the back button to close the app, the app is deactivated. All of the application state is stored in memory and app execution has 10 seconds to pause (Read the very last caution in the documentation). During deactivation it is important to store any transient and persistent state/data. From this state the application can be activated without losing any state in which case little if any state needs to be restored because the OS didn’t dispose of any part of the app state. The app could also be tombstoned which means that all state will need to be restored, but the application can still be activated. Finally the app might be completely closed by the OS thus requiring a fresh start.

Fast Application Switching

When an app is activating and the OS preserved state, the app should not attempt to do a full state restore, but instead should leverage the fact that the OS preserved the app’s previous state.

Tombstoning

If an app is tombstoned it means that all app state has been removed from the OS memory. This is why it is important for developers to store app state in transient or persistent data stores during app deactivation. When an app is activated from being tombstoned it should appear in the exact same state it was in before the user deactivated the app. It should also look the same as if the app had utilized Fast Application Switching.

For a much better explanation of these topics and some coverage of the difference between what persistent and transient data stores are available in Windows Phone please read my post Windows Phone 7 – Tombstoning.

Handling Tombstoning in CoreApplicationService

In the CAS only transient data is handled. We use DataProviders for persistent data. When deciding what transient data needs to be stored for tombstoning remember that we need to be able to restore the app during activation to the exact same state it was left in. Think of Leave No Trace from Boy Scouts and camping, we don’t want to leave any trace that the app was ever deactivated or tombstoned. Also, keep in mind when storing full objects that the object reference after retrieving a tombstoned value will be different than the object reference that was stored. If the reference needs to be the same the object should be stored in a persistent data store that should hand out the same reference for that object each time it is requested. We store values for tombstoning with the CAS using the following API.

public void StoreTombstoningValue(string key, object value)
{
	PhoneApplicationService.Current.State[key] = value;
}

public bool TryRetrieveTombstoningValue<T>(string key, out T value, bool clearKey = false)
{
	object tempObj;
	var exists = PhoneApplicationService.Current.State.TryGetValue(key, out tempObj) && tempObj is T;
	value = exists ? (T)tempObj : default(T);

	if (clearKey)
	{
		ClearTombstoningValue(key);
	}

	return exists;
}

public void RemoveTombstoningValue(string key)
{
	PhoneApplicationService.Current.State.Remove(key);
}

Store Tombstoning Value

Windows Phone provides developers with the PhoneApplication.Current.State dictionary which will survive as long as the app is not closed. Here is where we store all transient data.

Try Retrieve Tombstoning Value<T>

When retrieving a value we wanted to streamline the experience. Thus, this method is generic and does all the checks and casts each time a value is requested. It reports weather the value was found and cast correctly. It optionally takes in a parameter indicating to clear the value out of the State dictionary.

Remove Tombstoning Value

After retrieving a tombstoning value for the last time it should be removed to decrease memory usage.

Quick Tip of What to Store for Tombstoning

As much as possible only store object Id’s instead of the full objects. Even if at the moment the reference to that object doesn’t matter it may matter down the road. The naming of these methods is also very important. It is strongly suggested to not use the State dictionary to store objects during navigation. While it is true that a full object can be stored in the State dictionary before navigation for the next page to access after navigation, the object references are different because the State dictionary serializes and then deserializes all objects being stored then retrieved. This fact hurt one of our last projects. Once we had a grip on this principle we were OK, but until then bug after bug would manifest and they were difficult to track down.

Conclusion

Nothing super fancy this time. The motivation to include this functionality in the CAS was the constant repetition of the code in TryRetrieveTombstoningValue<T>(). It has helped clean up our code base quite a bit. This post also concludes code coverage of the CAS. The CAS is a very key component to any Windows Phone development. We’re excited to hear how you use it or how the principles discussed around it have helped you. Leave some comments to let us know. Next in the series we will be covering PageViewModel which works closely with ViewBase which we discussed in the previous post of this series WP7 Simplified: CoreApplicationService (Navigation).

WP7 Simplified: CoreApplicationService (Navigation)

My last post in this series covered the CoreApplicationService (CAS) and how it handles app lifecycle which is part of a broad series discussing how to simplify Windows Phone development. In the lifecycle post navigation was discussed only lightly. This post aims to cover the CAS and how it handles navigation. It will also, cover a few open questions about lifecycle from my previous post. The CAS was originally created and used for navigation exclusively. Desires for centralized lifecycle, tombstoning, and navigation management drove us to buff out the former NavigationService and rename it as CoreApplicationService. Since the NavigationService, we have optimized, simplified, and fundamentally improved navigation on Windows Phone applications.

Lesson #1: Centralize Data

The most important of lessons that we learned was to make sure data is centralized in the app. Any object reference that needs to be passed to the next page can only do so if the reference to that object is available from a centralized data provider. We won’t cover data providers here, but I wanted to bring this lesson to the forefront in order to help bring clarity to the thought process behind the navigation paradigm used in the CAS. We had unknowingly painted ourselves into a corner during our development one of the phone apps we developed. We desired to modify the same object reference in two related pages. We made the mistake of storing the object in the PhoneApplicationService.Current.State dictionary. What we didn’t realized until the final weeks of development and testing was that State dictionary serializes the objects stored in it. That means when obtaining an object stored in the State dictionary it was being deserialized as a new object. We would then modify that object and return back to the first page. The first page would then assume the object was updated when in reality a different object reference had been used and updated. This was a huge bummer to realize so late and taught us to never pass objects via the State dictionary if we need the original object reference.

With that said lets get into some navigation topics.

Navigation Infrastructure

Those familiar with Windows Phone development know that in order to perform any kind of navigation you must use the PhoneApplicationFrame. When initializing the CAS in App.xaml we pass in the PhoneApplicationFrame. This gives us the ability to navigate without requiring a PhoneApplicationPage which is the built in intermediary to the PhoneApplicationFrame. The navigation framework in the CAS is essentially a more developer friendly API wrapper for the PhoneApplicationFrame navigation framework. Our goal was to expose all of the PhoneApplicationFrame navigation framework while buffing out the points where it was lacking.

public class CoreApplicationService
{
	private PhoneApplicationFrame _frame;

	public event EventHandler<NavigatingCancelEventArgs> Navigating;
	private void OnNavigating(NavigatingCancelEventArgs e)
	{
		var handler = Navigating;
		if (handler != null) handler(this, e);
	}

	public event EventHandler<NavigationEventArgs> Navigated;
	private void OnNavigated(NavigationEventArgs e)
	{
		var handler = Navigated;
		if (handler != null) handler(this, e);
	}

	public Dictionary<object, string> Mappings { get; private set; }

	public void Initialize(PhoneApplicationFrame frame, bool isLaunching)
	{
		LittleWatson.CheckForPreviousException();
		_frame = frame;
		_frame.Navigated += FrameNavigatedHandler;
		_frame.Navigating += FrameNavigatingHandler;
		PersistTombstoningValue(IS_LAUNCHING_KEY, isLaunching);
	}
	
	private void FrameNavigatedHandler(object sender, NavigationEventArgs e)
	{
		OnNavigated(e);
	}

	private void FrameNavigatingHandler(object sender, NavigatingCancelEventArgs e)
	{
		OnNavigating(e);
	}
}

Navigation Mappings

This is one are of functionality where we’re trying to optimize still. Currently we have a dictionary of object to string mappings. The string represents the Uri to a page while the object is some arbitrary key that maps to that page. Managing the keys is the difficult part that we haven’t quite figured out yet. Here is how we register the mappings in LionHeart.

public partial class App : Application
{
	private void InitializeNavigationMappings()
	{
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.HOME_VIEW_KEY, "/Views/HomeView.xaml");
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.MY_VIEW_KEY, "/Views/MyView.xaml");
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.ALL_CLIENTS_VIEW_KEY, "/Views/AllClientsView.xaml");
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.CLIENT_VIEW_KEY, "/Views/ClientView.xaml");
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.MY_REPORTS_VIEW_KEY, "/Views/MyReportsView.xaml");
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.REPORT_VIEW_KEY, "/Views/ReportView.xaml");
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.ALL_SESSIONS_VIEW_KEY, "/Views/AllSessionsView.xaml");
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.MY_SESSIONS_VIEW_KEY, "/Views/MySessionsView.xaml");
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.SESSION_VIEW_KEY, "/Views/SessionView.xaml");
		CoreApplicationService.Instance.Mappings.Add(ViewKeys.SETTINGS_VIEW_KEY, "/Views/SettingsView.xaml");
	}
}

By using this dictionary of mappings we gain a huge amount of flexibility. This allows the developer to expose and manage navigation keys in lower tiers of the app if desired. In the sample code we use a struct named ViewKeys to manage or contain all view keys. We’ve tried using object types as the key, but we always seemed to come to a point where we were mixing types into ViewModels that shouldn’t know about certain types and tightly coupling types to views. That doesn’t sound all bad, but from experience a struct of string keys has been one of the cleanest and readable approaches.

Note on Managing Views and ViewModels in the CAS: The PhoneApplicationFrame has a BackStack property that manages all pages that are alive in the app. We didn’t want to manage Views or ViewModels in the CAS because that work was already being done by the PhoneApplicationFrame. We also didn’t want to tightly couple the CAS the UI tier, in fact that’s exactly why we need the CAS is that the PhoneApplicationFrame is only available in the UI tier unless passed into a different tier. The CAS is immediately available to ViewModels and any other tier above the very base tier.

Here’s a look at ViewKeys:

public struct ViewKeys
{
	public static readonly string HOME_VIEW_KEY = "HOME_VIEW_KEY";
	public static readonly string MY_VIEW_KEY = "MY_VIEW_KEY";
	public static readonly string ALL_CLIENTS_VIEW_KEY = "ALL_CLIENTS_VIEW_KEY";
	public static readonly string CLIENT_VIEW_KEY = "CLIENT_VIEW_KEY";
	public static readonly string MY_REPORTS_VIEW_KEY = "MY_REPORTS_VIEW_KEY";
	public static readonly string REPORT_VIEW_KEY = "REPORT_VIEW_KEY";
	public static readonly string ALL_SESSIONS_VIEW_KEY = "ALL_SESSIONS_VIEW_KEY";
	public static readonly string MY_SESSIONS_VIEW_KEY = "MY_SESSIONS_VIEW_KEY";
	public static readonly string SESSION_VIEW_KEY = "SESSION_VIEW_KEY";
	public static readonly string SETTINGS_VIEW_KEY = "SETTINGS_VIEW_KEY";
}

Not very exciting… And moving on.

Performing Navigation

Now we get into the exciting stuff. How we perform a navigation. Here is some code from LionHeart in HomeVM and helper class NavigationDefinition to help illustrate how to do this.

<ListBox x:Name="Menu"
	ItemsSource="{Binding NavigationOptions}"
	SelectedItem="{Binding NavigationTarget, Mode=TwoWay}"
	DisplayMemberPath="Name"
	toolkit:TiltEffect.IsTiltEnabled="True"/>

public class HomeVM : PageViewModel
{
	private List<NavigationDefinition> _navigationOptions;
	public List<NavigationDefinition> NavigationOptions
	{
		[DebuggerStepThrough]
		get { return _navigationOptions; }
		set
		{
			if (value != _navigationOptions)
			{
				_navigationOptions = value;
				OnPropertyChanged("NavigationOptions");
			}
		}
	}
	
	private NavigationDefinition _navigationTarget;
	public NavigationDefinition NavigationTarget
	{
		[DebuggerStepThrough]
		get { return _navigationTarget; }
		set
		{
			if (value != _navigationTarget)
			{
				_navigationTarget = value;
				OnPropertyChanged("NavigationTarget");
				Navigate(_navigationTarget);
			}
		}
	}
	
	private void InitializeMenu()
	{
		if (NavigationOptions == null)
		{
			NavigationOptions = new List<NavigationDefinition>(new[]
				{
					new NavigationDefinition(MY_SESSIONS_MENU_ITEM_NAME,
						ViewKeys.MY_VIEW_KEY,
						new Dictionary<string,string>{{NavigationKeys.PIVOT_ITEM_KEY, MyPivotItem.Sessions.ToString()}}),
					new NavigationDefinition(MY_CLIENTS_MENU_ITEM_NAME,
						ViewKeys.MY_VIEW_KEY,
						new Dictionary<string,string>{{NavigationKeys.PIVOT_ITEM_KEY, MyPivotItem.Clients.ToString()}}),
					new NavigationDefinition(MY_REPORTS_MENU_ITEM_NAME,
						ViewKeys.MY_VIEW_KEY,
						new Dictionary<string,string>{{NavigationKeys.PIVOT_ITEM_KEY, MyPivotItem.Reports.ToString()}}),
					new NavigationDefinition(CREATE_REPORT_MENU_ITEM_NAME,
						ViewKeys.REPORT_VIEW_KEY),
					new NavigationDefinition(SETTINGS_MENU_ITEM_NAME,
						ViewKeys.SETTINGS_VIEW_KEY),
				});
		}
	}
	
	private void Navigate(NavigationDefinition definition)
	{
		if (definition != null)
		{
			NavigationTarget = null; // reset selection
			CoreApplicationService.Navigate(definition.Key, definition.Parameters);
		}
	}
}

public class NavigationDefinition
{
	public NavigationDefinition(string name, object key, Dictionary<string, string> parameters = null)
	{
		Name = name;
		Key = key;
		Parameters = parameters;
	}
	
	public string Name { get; private set; }
	public object Key { get; private set; }
	public Dictionary<string, string> Parameters { get; private set; }

}

NavigationDefinition

This simple helper class is just a wrapper for the desired ViewKey, a dictionary of parameter keys and values, and a display friendly name. This class helps to with creating a menu of navigation options in the UI such as the one created in HomeVM.InitializeMenu() in the sample code above.

When we navigate we can append parameters to the page Uri when we pass it to the PhoneApplicationFrame. In HomeVM.InitializeMenu() we create parameters for each NavigationDefinition with a key of ViewKeys.MY_VIEW_KEY. In this case the parameters will be used to determine which pivot item is in view when the user navigates to MyView. How this is done will be covered in a future post.

In HomeView there is a ListBox which displays each NavigationDefinition. The selection of the ListBox is bound to HomeVM.NavigationTarget. When the user selects a definition and NavigationTarget is set we call Navigate() from it’s setter. Navigate() in turn calls CoreApplicationService.Navigate() passing it the view key and the dictionary of parameters.

Processing a Navigation Request

public class CoreApplicationService
{
	/// <summary>
	/// Navigates the specified viewKey.
	/// </summary>
	/// <param name="viewKey">The viewKey.</param>
	/// <param name="parameters">The parameters.</param>
	/// <param name="countToRemoveFromBackStackAfterNavigation">The count to remove from back stack. (ignored if clearBackStack == <c>true</c>)</param>
	/// <param name="clearBackStack">if set to <c>true</c> [clear back stack].</param>
	public void Navigate(object viewKey, Dictionary<string, string> parameters = null, 
		int countToRemoveFromBackStackAfterNavigation = 0, bool clearBackStack = false)
	{
		string target;
		if (viewKey != null && Mappings.TryGetValue(viewKey, out target))
		{
			parameters = parameters ?? new Dictionary<string, string>();
			parameters.Add(IS_RUNNING_KEY, true.ToString());
	
			ICollection<string> parameterKeyValueStrings =
				parameters.Keys.Select(key => string.Format("{0}={1}", key, parameters[key])).ToArray();
	
			target += string.Format("?{0}", string.Join("&", parameterKeyValueStrings));
	
			if (clearBackStack)
			{
				_frame.Navigated += NavigationReadyToClearBackStackHandler;
			}
			else if (countToRemoveFromBackStackAfterNavigation > 0)
			{
				_countToRemoveFromBackStackAfterNavigation = countToRemoveFromBackStackAfterNavigation;
				_frame.Navigated += NavigationReadyToRemoveCountFromBackStack;
			}
	
			_frame.Navigate(new Uri(target, UriKind.RelativeOrAbsolute));
		}
		else
		{
			throw new ArgumentException(string.Format("Cannot navigate to target view. Driver={0}", viewKey));
		}
	}
}

Here we find the meat of navigation processing. The first thing done here is a check to validate the viewKey is a valid key to a page Uri then acquires the target page Uri. If no parameters were passed an empty parameters dictionary is created and immediately we add a parameter for IS_RUNNING_KEY which is always set to true if this method is called. The IS_RUNNING_KEY key is used by ViewModels in order to know how to initialize. Again, another topic that will be covered in a future blog post. After the parameters collection is finalized the CAS then appends each parameter to the target Uri forming the finalized target Uri.

The next step is to hook up handlers for the PhoneApplicationFrame.Navigated event when necessary. Sometimes it is desirable to perform a navigation that clears the entire PhoneApplicationFrame.BackStack. Other times, it is desirable to remove a specified number of pages from the PhoneApplicationFrame.BackStack. If clearBackStack is true then after navigation is complete the CAS clears the PhoneApplicationFrame.BackStack. If clearBackStack is false and countToRemoveFromBackStackAfterNavigation is greater than 0 then after navigation completes the CAS removes the specified number of page entries from the PhoneApplicationFrame.BackStack. This functionality is helpful for navigating to menu pages and in app navigation resets.

Finally, the CAS calls PhoneApplicationFrame.Navigate() allowing Windows Phone to take over.

ViewBase and Handling Navigation in Pages

First, there is currently a disconnect in terminology. Windows Phone calls each View a Page, and here at Interknowlogy we’re in the habit of calling each Page a View. So each term means exactly the same thing and we’re working on simplifying our terminology. With that aside lets look at ViewBase.

ViewBase becomes the new base class of each View in the app. This cuts down on duplicating this code for each view. Because of that maintainability goes up, which is always a good thing.

public partial class HomeView : ViewBase
{
	public HomeView()
	{
		InitializeComponent();
	}
}

public class ViewBase : PhoneApplicationPage
{
	public PageViewModel PageViewModel { get { return DataContext as PageViewModel; } }

	protected override void OnNavigatedTo(NavigationEventArgs e)
	{
		if (PageViewModel != null)
		{
			PageViewModel.Initialize(NavigationContext.QueryString);
		}
		base.OnNavigatedTo(e);
	}

	protected override void OnNavigatedFrom(NavigationEventArgs e)
	{
		if (PageViewModel != null)
		{
			PageViewModel.Uninitialize(e.NavigationMode == NavigationMode.Back);
		}
		base.OnNavigatedFrom(e);
	}

	protected override void OnRemovedFromJournal(JournalEntryRemovedEventArgs e)
	{
		if (PageViewModel != null)
		{
			PageViewModel.Uninitialize(true);
		}
		base.OnRemovedFromJournal(e);
	}
}

ViewBase is by no means required, but it is a nice piece of reusable code. Each page desiring to leverage the navigation framework must have a ViewModel derived from PageViewModel as its DataContext. ViewBase is responsible for knowing what to do with its ViewModel and when to do it. It knows that in OnNavigatedTo the ViewModel needs to be Initialized with the specified parameters, and that in OnNavigatedFrom and OnRemovedFromJournal the ViewModel needs to be Uninitialized and how. OnRemovedFromJournal is called on a Page when it is removed from the PhoneApplicationFrame.BackStack.

Conclusion

The CAS makes navigation more powerful and flexible. It’s simple to request navigation via the CAS and it’s reliable. The CAS does not replace existing navigation options, instead it builds upon the existing framework and extends possibilities. The CAS also makes navigation more reusable and readable inside ViewModels. I hope this post helps inspire innovation with Windows Phone.

WP7 Simplified: CoreApplicationService (Lifecycle)

In my post Simplyfying the Windows Phone Development Experience! Codename: LionHeart I explain that LionHeart is a demo app that I’m using to prove out a few lessons we’ve learned here at IK from our last Windows Phone project. In this post I will start to get into the meat of the app starting in a class named CoreApplicationService (CAS).

What is CoreApplicationService’s Purpose?

Event Forwarding

One of the purposes of the CoreApplicationService (CAS) is to expose events from across the app to ViewModels (VMs) or any other class that is not instantiated by App.xaml. This class acts, in a very simplified way, as an event aggregator. It is not, however, an event aggregator as found in Prism. Instead it’s more of a one to one event forwarding class. For example, in App.xaml there are two events named Deactivated and Closing. Therefore, in the CAS there are two identical events named Deactivated and Closing. When the event is handled in App.xaml.cs we simply forward the handling onto the CAS as show in this example:

public partial class App : Application
{
   private void ApplicationDeactivated(object sender, DeactivatedEventArgs e)
   {
       CoreApplicationService.Instance.OnDeactivated(e);
   }
}

Currently the events handled in the CAS are:

From App.xaml

  • Deactivated
  • Closing
  • Obscured
  • Unobscured
  • NavigationFailed
  • UnhandledException

From PhoneApplicationFrame

  • Navigating
  • Navigated

In all cases, the CAS acts as a forwarding system for any object the desires to subscribe to these events. In LionHeart those objects are usually VMs. This pattern allows the VM to be responsible for knowing when and what to do during these events. This pattern has made more and more sense as we’ve discussed possible solutions to our problem of telling VMs when to Deactivate (prepare for tombstoning) etc.

Error Reporting

Another responsibility of CAS is to prompt the consumer of the app to send error data to the developer. We found Little Watson created by Andy Pennell at Microsoft which simply collects unhandled exception information and stores it in a file in IsolatedStorage before the app closes. The next time the consumer launches the app they are prompted to send the error file to the developer, which they can choose to not send it if they desire. The error file is then deleted and the application will not prompt again until an error file exists again. This is so helpful I cannot even begin to express how many bugs we have been able to track down because of this tool. I want to start buffing this helper class out with logging since the error information we receive on the phone is not as helpful as it could be. Adding a logging feature that began each time the application was launched would be of even greater benefit.

Navigation

This purpose is what originally drove us to create the CAS. In fact we had originally named the CAS as NavigationService, but as it’s functionality increased and purpose morphed we decided that name was to specific and did not convey what we wanted. Navigation will be covered in greater detail in another post. In simple terms we use a dictionary of object to string mappings. Each string represents the Uri to a page. These mappings are set immediately after initializing the CAS as shown in the section below “How to use CoreApplicationService.”

public partial class App : Application
{
   private void InitializeNavigationMappings()
   {
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.HOME_VIEW_KEY, "/Views/HomeView.xaml");
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.MY_VIEW_KEY, "/Views/MyView.xaml");
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.ALL_CLIENTS_VIEW_KEY, "/Views/AllClientsView.xaml");
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.CLIENT_VIEW_KEY, "/Views/ClientView.xaml");
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.MY_REPORTS_VIEW_KEY, "/Views/MyReportsView.xaml");
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.REPORT_VIEW_KEY, "/Views/ReportView.xaml");
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.ALL_SESSIONS_VIEW_KEY, "/Views/AllSessionsView.xaml");
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.MY_SESSIONS_VIEW_KEY, "/Views/MySessionsView.xaml");
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.SESSION_VIEW_KEY, "/Views/SessionView.xaml");
       CoreApplicationService.Instance.Mappings.Add(ViewKeys.SETTINGS_VIEW_KEY, "/Views/SettingsView.xaml");
   }
}

In the example code above you will notice the use of ViewKeys which is a struct that contains string values that we use as keys. The key does not need to be a string instead it can be any object. Sometimes we will use typeof(object) for views that represent specific types. We found that strings were required for some views and decided to go with all strings for consistency. Remembering that VMs will be requesting navigation most of the time we did not use typeof(Page) as the key, because VMs should not have access to those types.

How to use CoreApplicationService

The CAS is provided via a static property of itself named Instance as a singleton. There are two times the CAS will need to be instantiated. The first is when the application is Launching. The second case is when the application is being Activated, but only if there is no application state. In both cases no VMs exist yet, therefore the CAS will not forward the Launching and Activating events because no object would or could receive them. Instead we use the events to initialize the CAS.

public class CoreApplicationService
{
   private static CoreApplicationService _instance;
   public static CoreApplicationService Instance
   {
       get { return _instance ?? (_instance = new CoreApplicationService()); }
   }
}

public partial class App : Application
{
   private void Initialize(bool isLaunching)
   {
       CoreApplicationService.Instance.Initialize(RootFrame, isLaunching);
       InitializeNavigationMappings(); //As shown in above section “What is CoreApplicationService’s Purpose?” and subsection “Navigation”.
   }

   private void ApplicationLaunching(object sender, LaunchingEventArgs e)
   {
       Initialize(true);
   }

   private void ApplicationActivated(object sender, ActivatedEventArgs e)
   {
       if (!e.IsApplicationInstancePreserved)
       {
           Initialize(false);
       }
   }
}

Initialize accepts two parameters: the PhoneApplicationFrame which is used for navigation, and a boolean indicating if the application is launching.

public class CoreApplicationService
{
   public void Initialize(PhoneApplicationFrame frame, bool isLaunching)
   {
       LittleWatson.CheckForPreviousException();

       _frame = frame;
       _frame.Navigated += FrameNavigatedHandler;
       _frame.Navigating += FrameNavigatingHandler;

       PersistTombstoningValue(IS_LAUNCHING_KEY, isLaunching);
   }
}

The reason for the boolean is for VM initialization. This will be explained in much greater detail later on, but for those curious minds out there during Activating (without state) when VMs initialize there are no navigation parameters. This is used as an indication that the application was tombstoned. Because this same state exists during Launching we must differentiate the two somehow. We got around this by adding a boolean to the tombstoning values that is checked for when no navigation parameters exist and indicates the application is Launching not Activating. As I stated before I will cover this in later posts in greater detail.

Conclusion

This concludes the introduction into the CoreApplicationService and how it plays into the lifecycle of the phone app. This service is the backbone to LionHeart and all phone apps that use it. As we cover navigation and how VMs integrate with this service you’ll understand why it is so important and why it is so helpful. As always if you have any suggestions for improvement, comments, or questions please post them. This is not meant to be a one man show, but rather a starting point for phone app architecture.

WP7 Simplified: Wireframes and Information Map

UPDATE 7/12: This post is part of a broad series helping to simplify the development experience of Windows Phone apps. The beginning of the series can be found here.

In architecture a blueprint must exist before a house or office complex can be built. Without the blueprint there would be mass confusion and little if anything would ever happen. The final product would be disaster. The same concept applies to software especially on the phone. If you just start coding the resulting app would struggle to be coherent or usable to the consumer. In order to prevent this we need some kind of software blueprint to help the developer stay focused and deliver what is desired and intended. Over the last 3 years here at InterKnowlogy I’ve come across a variety of ways to represent the flow and design of an application. I have gone from paper to whiteboards to Visio to combinations of all of the above and more. What I’ve discovered is the digital formats take too long to accomplish anything so they are more of a final design asset rather than the means to get to the desired result. Whiteboards are my preferred medium for sketching wireframes and information flow. Microsoft has one of the coolest examples of wireframes participating in the flow of information called an Information Map. The example image on the aforementioned site is not the highest quality, but it demonstrates the intended purpose.

Information Map

As I mentioned earlier I don’t like going to a digital medium because I feel it takes too long to make it worth while. But I am a huge fan of the whiteboard so I’m including am image of the information map of LionHeart that I created on a whiteboard. Thus, digitizing my whiteboard information map. It’s not beautiful but it conveys the idea clear enough that I feel little to no need to rework it using Visio or some other pretty digital designer.

There are just a few important items to mention here. First, the Windows Phone platform has some strict navigation rules namely the back button always goes back to the previous page. There are times this rule can be ignored such as when going to a settings screen and selecting save. Hitting the back button after selecting save should not return to the settings screen in most cases. Essentially any screen that is treated more like a modal dialog should not be returned to via the back button. These cases should be called out in the information map. Second, certain UI aspects should be called out such as multi-select lists, pivot or panorama control, or other controls using none intuitive interactions (based on a static drawing). Finally, remember that these information maps only take the UI aspect of the application into account. Do show the layout to some of your colleges or potential consumers and make sure the flow of the app cuts down on as many touches as possible to accomplish what is important. Once the information map is complete then it’s time to move on to the project structure and start coding!

Code Architecture

This is an entire different beast. I will not be covering this topic now. Perhaps at a future time. Admittedly, code architecture is one of those religious wars that I absolutely LOVE to discuss. For now I will only say that after you have an information map completed decide on what models exist and what VMs exist. Remember if commands will be used for buttons such as with the BindableApplicationBar that VMs should be used. Also, take into account if you will be using IsolatedStorage or the local application database. LionHeart is designed around IsolatedStorage for purpose of its demo.

Simplifying the Windows Phone development experience! Codename: LionHeart

Each time you start a new project one of the first things most of us developers do is pull in code we want to reuse. If we don’t have any existing code then we try to figure out how to leverage concepts and theories from past experiences and platforms. The latter is exactly what we did during the development of my last project, a Windows Phone project. We have a ton of XAML and MVVM experience here at Interknowlogy, and a few of us had done small RECESS phone apps. None of us had done a full blown app from scratch on the phone before, but today I can say we did an awesome job! What I want to focus on are the lessons we learned from that project and how to apply them to future phone projects to help simplify the process. My coworker Tim Askins and I spent a few RECESS discussing what we could improve on and and what we thought went well. The result is what I call LionHeart.

Introduction to LionHeart

LionHeart is a demo phone app that I am working on that is applying the code lessons learned. These lessons include application lifecycle, navigation, tombstoning, MVVM on the phone, how to provide mock data vs. real data, styling, and many more. The project it self is a demo based on work my wife does as a Behavior Interventionist (BI) for children with autism. We got talking about what could potentially simplify her line of work as well as provide a compelling demo app for the phone. The resulting app will allow for BIs to create reports that are currently done on paper, then transferred to another paper and placed in a binder, then that binder is driven to the main office once a month, and then driven back to the client’s home where BIs go when then work with the children. Wow, that was a mouth full. The app will also allow BIs to view their schedule of sessions for the day/week/month/etc. from within the application. The schedule will also be integrated into the normal phone calendar from Windows Live as well. Individual client information will also be available including previous reports, charts showing progress, and whether there will be a supervisor attending the session as well. Sound like a full blown app? I’m really excited to bring this app to life. I just wish it wasn’t just a demo app.

Components of LionHeart

Let’s remind ourselves again that the reason we are am creating LionHeart is to help solidify patterns and practices on the phone that will help simplify and speed up the development process of future phone apps. Since there are so many components to LionHeart I wanted to give a brief overview of what I think some of those components will be over the next few weeks.

Framework Components

Application Components

  • HomeVM
  • Navigating to Pages with a Pivot Control and to a Specific PivotItem
  • Calendar Integration
  • Styling

With that said as I complete each post I’ll update the list with links.

Final Thoughts

When all is said and done these components should provide a solid foundation for developing on the Windows Phone platform. If you have any questions or thoughts about how to improve on our design please let me know. Happy Coding!

Responding to Incoming Phone Calls on WP7

While working on a Windows Phone 7 application recently, I discovered some limitations of the platform that were rather frustrating.  The application allowed audio to be recorded and played back, and it was expected that recording would pause if an incoming phone call was received on the phone.  The app was already designed to pause recording when the app was deactivated, and initially I thought an incoming phone call would cause deactivation.  I was surprised to discover that it did not.  Instead, the app continued to run uninterrupted while a phone call was taken.  If the app was recording at the time, recording would continue throughout the phone call.  Interestingly, the XNA Microphone class appeared to be muted during the phone call, with that portion of the recording being blank, neither side of the conversation captured.

A little searching confirmed that an incoming phone call does not deactivate a WP7 app.  Instead, the app is “obscured”, and the only way to detect the phone call is by subscribing to the Obscured event of the PhoneApplicationFrame class.  Unfortunately, the Obscured event simply indicates that something from the OS is obscuring the visual of your app (“the shell chrome is covering the frame”, in the words of the MSDN docs), without providing any detail about the source cause.  As a result, many things can cause Obscured to fire, including an incoming phone call, locking of the screen, an incoming text message, a calendar event notification, and a MessageBox.  There are no events fired specific to the different types of OS interruptions that can occur, nor any properties in the ObscuredEventArgs to indicate the source cause.  There is an IsLocked property within ObscuredEventArgs that can be used to detect if locking of the screen caused Obscured to fire, but otherwise no other useful information is given.

This presented a problem for our application, and I could imagine it being a problem for others as well.  In our app we wanted to handle incoming phone calls differently than other types of OS interruptions, such as incoming text messages or calendar event notifications.  The former should pause recording, while the latter should not.  (Pausing recording in response to something as innocuous as a calendar notification would be an awful experience within our app, as the user could easily be unaware recording had stopped, causing potentially important audio to be missed.)

Even the ability to discriminate a screen lock as the source of the Obscured event is of limited value.  We’ve set up our app to continue running (and recording) under lock screen, and at first glance it seems like it would be helpful to ignore the Obscured event if a screen lock was the cause.  Unfortunately, once the screen is obscured, no other OS interruptions fire the Obscured event until the screen is once again unobscured.  Although we did not want to pause recording in response to the lock screen, we did want to pause recording in response to an incoming phone call, including when the screen was locked.  The Obscured event is not fired in response to the incoming phone call if the screen is locked, however.  It would be impossible for us to have consistent behavior both while the screen is locked and unlocked.

As a result of these limitations, we decided to ignore the Obscured event altogether, and simply allow recording to continue while a phone call was taken.  Most users will likely dismiss incoming phone calls while recording, but if they take the call, at least their conversations will not be recorded.  (Instead a blank gap appears in the recorded audio.)  Hopefully future versions of WP7 offer more information about OS interruptions, so apps can respond in an intelligent manner dependent upon the source of the interruption.