Introduction
MoneyPit was started as a learning exercise. It is nothing earth-shattering, it is not even something that has not been done before. It is, however, something which has given me the opportunity to work on a mobile platform using the power of Visual Studio. It also gave me an opportunity to (finally) learn some XAML and MVVM. I have been an Android user and I have, in fact, tried to do some app development for this platform. For some reason, this never went past some small attempts at actually creating something I could use myself. Since I have switched over to the Windows Phone platform, I decided to target my app development at this platform.
What is MoneyPit?
MoneyPit is a simple fuel log app. It is designed to enable you to keep track of fuel costs of your car. It will allow you to enter one or more cars and then keep track of refueling the car(s) by simply entering the fill up information when you pump the gas.
Background
It wasn't until I got my Nokia Lumia 920 that I got really interested in creating something from start to finish. Microsoft offers great tooling for creating Windows Phone 8 apps with their Windows Phone 8 SDK [^]. It even includes Visual Studio 2012 Express edition which is a great starting point for app development. MoneyPit was developed using Visual Studio 2012/2013 Premium but the solution included in this article should work just fine with the Visual Studio Express edition included in the Windows Phone 8 SDK although I have not tried it.
This article tries to explain the development and release of MoneyPit from concept to design to coding to certification. It has not been smooth sailing all the way.
Functional Requirements
I had set myself a simple set of requirements for the app:
- The app should follow the Windows Phone design guidelines [^]
- The app must support the standard WP8 dark and light themes.
- There must be support for multiple cars.
- The app must be designed/coded using the MVVM pattern.
- The app must support any currency, distance unit and volume unit (no unit conversion).
- There must be support for export to, and import from SkyDrive.
- It must be possible to store the location of a fill up.
- It must be possible to add notes to a fill up.
- The app must enable the user to quickly get a historical overview of the fuel cost and economy of a car.
- The app should protect the user as much as possible against entering false data.
- The app must support multiple languages (English and Dutch to begin with).
- The app should use SQLite because SQL CE is not supported on RT (in case the app will ever be ported).
The Tools
MoneyPit was created using a set of tools which I discovered during development of the app. The following list (in no particular order) contains the links to the tools. During the article, I will explain the reasons for using these tools:
App Design
Microsoft has created a comprehensive set of design rules and guidelines [^] for Windows Phone 8 development. After experimenting with several UI designs for MoneyPit, I have chosen to base the UI design on the "List with details drill-down [^]" and the "App Tabs [^]" designs.
The main page of the UI is a simple list of cars in the database. The user can tap on a car from the list to drill down into the details of that car.
The details of the car consist of a pivot containing four pages of detailed information relating to the selected car. The first pivot contains the fill up log of the car. The second pivot contains charts with fuel costs of the car. The third pivot contains charts with the distances driven with the car and the fuel economy. The fourth and last pivot contains summary information about cost, fuel economy, etc.
The information shown in the details pivots show at maximum a single year of data. The menu in the application bar allows for selecting the year to show. By default, the current year is shown.
The application bar is mostly used throughout the app for accessing functionality that is not needed on a day to day basis. Things like the Settings page, the About page, Import/Export, etc. are accessed using an application bar button.
Adding a fill up for a car is also accessed through an application bar button on the car details page. Since this is what the app is mostly about, logging fill ups for a car, this functionality is also available by "pinning" the car to the start screen. This creates a tile on the start screen which will act as a shortcut for getting to the "add fill up" page for the car.
The Application Bar
The application bar is an important part of a Windows Phone app. It does, however, have its quirks which can make it a bit challenging to work with.
One issue with the application bar is that it will not take the focus of a control when the user clicks on a button or a menu item. Now you may say that this is not a big issue but it can be when using the application bar on a page which allows for user input. The TextBox
and PasswordBox
controls are designed to update their binding source when they lose focus. This means that when a user is editing the control contents and then taps on an application bar button or menu-item, the edited information is not stored in the view model.
To circumvent this problem, MoneyPit uses a class based on the ApplicationBarIconButton
class. This class will handle the Click
event of its base class to find out the object type of the currently focused element. If this is either a TextBox
, a PhoneTextBox
or a PasswordBox
, it will find the object its binding expression and update it.
public class ApplicationBarIconButtonEx : ApplicationBarIconButton
{
public ApplicationBarIconButtonEx()
{
Click += (s, e) =>
{
BindingExpression be = null;
object focused = FocusManager.GetFocusedElement();
if(focused == null)
{
return;
}
var phonetextbox = focused as PhoneTextBox;
if(phonetextbox != null)
{
be = phonetextbox.GetBindingExpression(PhoneTextBox.TextProperty);
}
else
{
var passwordbox = focused as PasswordBox;
if(passwordbox != null)
{
be = passwordbox.GetBindingExpression(PasswordBox.PasswordProperty);
}
else
{
var textbox = focused as TextBox;
if(textbox != null)
{
be = textbox.GetBindingExpression(TextBox.TextProperty);
}
}
}
if(be != null)
{
be.UpdateSource();
}
};
}
}
Another issue with using the application bar and wanting to use an MVVM approach is that the buttons and menus of an application bar do not allow for command binding. AppBarUtils [^] offers a solution for, among other things, this issue. It allows you to use command binding on application bar buttons and menus. One of the great things about it is that it will also enable/disable the application bar button or menu item depending on the CanExecute
state of the bound command.
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True">
<mpctrl:ApplicationBarIconButtonEx IconUri="/Assets/AppBar/add.png"
Text="new" />
...
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
<i:Interaction.Behaviors>
<abu:AppBarItemCommand Id="new"
Text="{Binding Path=LocalizedResources.CreateCarButtonText,
Source={StaticResource LocalizedStrings}}"
IconUri="/Assets/AppBar/add.png"
Command="{Binding NewCarCommand}" />
...
</i:Interaction.Behaviors>
You declare your application bar buttons as per usual. The interesting part happens in the behaviors
section. Here, we use the AppBarUtils
to link a command to an application bar button. As you may have noticed, the Id
property of the AppBarItemCommand
is the same as the Text
property of the ApplicationBarIconButton
. The Text
property of the original ApplicationBarIconButton
is used as a key to find it by AppBarUtils
. In the AppBarItemCommand
, the actual text of the button can be set using its Text
property. Its Command
property can be used to bind it to a command in your view model.
MVVM Light
As mentioned earlier, MoneyPit uses MVVM Light [^] as MVVM framework. It is, as the name implies, a lightweight framework which makes implementing an MVVM architecture a lot easier. It offers among other things, features like a simple IoC container, a messenger service and design time data.
ViewModels
All view models in MoneyPit are derived from a class called BaseViewModel
. This class is derived from the MVVM Light class ViewModelBase
. The BaseViewModel
class contains methods for setting properties and handling property related events.
public class BaseViewModel : ViewModelBase
{
[Ignore]
public bool IsDirty { get; set; }
protected void SetProperty<t>(T value, [CallerMemberName] string propertyName = null)
{
RaisePropertyChanged(propertyName);
}
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
RaisePropertyChanging(propertyName);
storage = value;
RaisePropertyChanged(propertyName);
IsDirty = true;
return true;
}
}</t>
The view models are created and injected into the views using the MVVM Light SimpleIoc
container. The ViewModelLocator
class creates all view models and registers them into the SimpleIoc
container. The ViewModelLocator
class will then expose the instances as properties which will in turn return the instances from the SimpleIoc
container.
Normally, the SimpleIoc
container will create one instance of the view model and return this same instance each time the view model property is referenced. However, sometimes, we want to create a new instance each time a view model property is referenced. In this case, SimpleIoc
offers the possibility to get a instance using a key. However, the instances created with a key are internally cached by SimpleIoc
. What I wanted was to create a new instance each time the view model property is referenced but to destroy each previously created instance when a new one is created. To do this, a view model is created using a key. However, before it is created, a check is made to see if SimpleIoc
contains an instance created with the previous key. If so, this one is unregistered before the new instance is created.
public class ViewModelLocator
{
static readonly string _editModelKey = "798829F7-0753-4450-AC72-F3D0013D048E";
...
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
...
SimpleIoc.Default.Register<EditCarViewModel>();
...
}
...
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public EditCarViewModel EditCar
{
get
{
if (!String.IsNullOrEmpty(_editModelKey))
{
SimpleIoc.Default.Unregister<EditCarViewModel>(_editModelKey);
}
return SimpleIoc.Default.GetInstance<EditCarViewModel>(_editModelKey);
}
}
...
}
Design Time Data
MVVM Light has support for design time data. In the ViewModelLocator
class constructor, a check is made to see whether or not it is running in the designer. If it is running under the designer, an instance of the DesignMoneyPitRepository
class is registered instead of an instance of the MoneyPitRepository
class. This DesignMoneyPitRepository
class takes care of providing the design time data.
static ViewModelLocator()
{
...
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IMoneyPitRepository, Design.DesignMoneyPitRepository>();
}
else
{
SimpleIoc.Default.Register<IMoneyPitRepository, MoneyPitRepository>();
}
...
}
This will result in something like you see below in the Visual Studio/Blend designer.
This can be a real big help with designing your UI.
One word of caution when using design time data is that you cannot do much of anything in your view model when running under the designer. It is not possible to access a database or something similar when running under the designer. You can use MVVM Light's ViewModelBase.IsInDesignModeStatic
property to check if you are running under the designer.
Problems doing things that are not allowed under the designer manifest themselves by designer data not showing up in the designer. That's about it. There are no more clues as to why the data is not showing. This can be a real pain to track down. A great help with issues concerning design time data is running Blend attaching the Visual Studio debugger to this instance of Blend and then opening the project and XAML page giving you trouble. With a little luck, the Visual Studio will break at the code that is causing the issue.
EventToCommand
When using MVVM to design your application, you want to be able to link any type of event on a UI control to a command in your view model. Some controls offer a Command
property but that is not nearly enough. Luckily, MVVM Light has EventToCommand
. This is a TriggerAction
derived class which will allow for mapping any type of event to a command in a view model.
There is an issue with MVVM Light's implementation of EventToCommand
and that is that it will not invoke the command when the UI element the trigger is attached to is disabled. There is a lot to be said for this behavior but wouldn't you know, I needed it to invoke the command also when the attached UI element is disabled.
For this reason, I created my own version of EventToCommand
based on the original MVVM Light source code (long live open source). It has an added DependancyProperty
called InvokeWhenSourceDisabled
which will, when set to true
, tell the trigger to also call the command when the source UI element is disabled.
The Messenger
In MoneyPit, the MVVM Light Messenger
is used for communication between view models and for displaying dialogs from the view model. The MVVM Light Messenger
is a simple register and send system which allows for registering for a message type at one point and sending the message at another point without having any knowledge of each other other than the interface the message defines. This makes it possible to keep things loosely coupled.
Messenger.Default.Register<BitmapImage>(this, "PictureTaken", (image) => { CarPhoto = image; });
Messenger.Default.Send<BitmapImage>(Util.ScaleImage(e.ImageStream), "PictureTaken");
As mentioned, the Messenger is also used to open dialogs. The method I used for this is not my own. Searching for a good way to display dialog boxes from the view model I came along this blog [^] which shows a nice way to do just that. Basically, it consists of a DialogBehaviour
class which can be instantiated in your XAML like this:
<i:Interaction.Behaviors>
<mpb:DialogBehavior Caption="{Binding LocalizedResources.MessageDialogTitle, Mode=OneWay,
Source={StaticResource LocalizedStrings}}"
Text="{Binding LocalizedResources.OdometerFault,
Source={StaticResource LocalizedStrings}}"
Buttons="Ok"
Identifier="odometerFault" />
</i:Interaction.Behaviors>
Then, in your view model, you can show the dialog simply as follows:
Messenger.Default.Send(new DialogMessage(String.Empty, null), "odometerFault");
In the DialogMessage
, you can pass in an Action<MessageBoxResult>
to handle the button clicks in the dialog if necessary.
The Database
I started off developing MoneyPit with SQL CE as database engine. Soon, however, I came to realize that was not a good choice if ever I wanted to port the app to RT. SQL CE is not supported on Windows RT. Luckily SQLite [^] is supported on both platforms so a switch was made to using this embedded database engine.
SQLite for Windows Phone can simply be installed as a Visual Studio Extension. To use SQLite, I have also used the SQLite-net [^] wrapper. This is a .NET wrapper for the SQLite native DLL which enables to use SQLite in a managed environment and also enables some Linq as well. There is a problem with this wrapper however and that is the fact that it is not in sync with the official SQLite releases. As of this writing, the wrapper supports version 3.8.0.2 of SQLite for Windows Phone whereas SQLite for Windows Phone is up to version 3.8.2
Because the SQLite-net wrapper is a C++ project that compiles against a specific version of the SQLite runtime, upgrading the Visual Studio SQLite Extension will get you into trouble. I have so far managed to keep using the SQLite-net wrapper against the latest SQLite version by ... manually editing the Sqlite.vcxproj project file. Now this is far from ideal, but so far it has enabled me to use the latest SQLite version.
To "update" the Sqlite.vcxproj, you should change the SQLite version number in the ImportGroup
sections of the project file. If for example, you want to update the version from 3.8.0.2 to 3.8.2, you need to make the following changes:
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(MSBuildProgramFiles32)\Microsoft SDKs\Windows Phone\v8.0\ExtensionSDKs\
SQLite.WP80\3.8.0.2\DesignTime\CommonConfiguration\Neutral\SQLite.WP80.props" />
</ImportGroup>
Should be changed to:
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(MSBuildProgramFiles32)\Microsoft SDKs\Windows Phone\v8.0\ExtensionSDKs\
SQLite.WP80\3.8.2\DesignTime\CommonConfiguration\Neutral\SQLite.WP80.props" />
</ImportGroup>
Simply replacing all occurrences of "3.8.0.2
" to "3.8.2
" should suffice. This method will of course go "poof" when the native API of SQLite changes, but that is not likely to happen.
Another issue with the SQLite-net wrapper is that it does not support foreign keys and cascading deletes using the "normal" CreateTable<Type>
method of creating tables. Since I needed both foreign keys and cascading deletes in my data model tables are created using the Execute
method of the Connection
class. Using the Execute
method, you can run standard SQL statements for table creation allowing you to have more control over the created data model.
Connection.Execute("CREATE TABLE IF NOT EXISTS [SchemaVersion] ( [Version] INT );");
Connection.Execute("INSERT INTO [SchemaVersion] ([Version]) VALUES(?);", 1);
Connection.Execute("CREATE TABLE IF NOT EXISTS [Car] ( " +
"[Id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
"[Make] NTEXT(50) NOT NULL, " +
"[Model] NTEXT(50) NOT NULL, " +
"[Picture] IMAGE, " +
"[LicensePlate] NTEXT(15) NOT NULL, " +
"[BuildYear] INT, " +
"[Currency] NTEXT(3) NOT NULL, " +
"[VolumeUnit] SMALLINT NOT NULL DEFAULT 1, " +
"[DistanceUnit] SMALLINT NOT NULL DEFAULT 1);");
Connection.Execute("CREATE TABLE IF NOT EXISTS [Fillup] ( " +
"[Id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
"[CarId] INTEGER NOT NULL CONSTRAINT [FillupCar]
REFERENCES [Car]([Id]) ON DELETE CASCADE, " +
"[Date] DATETIME NOT NULL, " +
"[Odometer] DOUBLE NOT NULL, " +
"[Volume] DOUBLE NOT NULL, " +
"[Price] DOUBLE NOT NULL, " +
"[FullTank] BOOL NOT NULL, " +
"[Note] NTEXT, " +
"[Longitude] DOUBLE, " +
"[Latitude] DOUBLE);");
Connection.Execute("CREATE TABLE IF NOT EXISTS [CrashLog] ( " +
"[Id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
"[Message] NTEXT NOT NULL, " +
"[Stacktrace] NTEXT NOT NULL, " +
"[Stamp] DATETIME NOT NULL);");
As you can see, the data model of MoneyPit is very simple. There is a Car
table for storing information about the cars. There is a Fillup
table for storing fill up information which has a foreign key constraint to the Car
table. It also defines a cascading delete so that fill ups that are linked to a car are automatically deleted when the car is deleted. The SchemaVersion
table will be used when updating an existing model is necessary. Lastly, there is the CrashLog
table. In that table, unhanded exceptions are stored. If this happens and the user restarts the app, (s)he will be given the option to email the exception to the developer. A feature which hopefully is not needed too often ...
The UI
Windows Phone 8 offers a broad range of standard controls, however more is needed to really create a rich user experience. For this reason, Microsoft has created the Windows Phone Toolkit [^] which offers a truck load of new controls and effects which makes it so much easier to create a good user experience. MoneyPit also uses the Windows Phone Toolkit to enhance the user interface of the app.
Page Transitions
MoneyPit uses SlideTransition
for page navigation. When navigation to a page, it slides in the screen right to left while the page you navigate from slides out of the screen right to left. Creating these transitions is very simple. I simply added the following code to App.xaml:
<Style x:Key="TransitionPageStyle"
TargetType="phone:PhoneApplicationPage">
<Setter Property="toolkit:TransitionService.NavigationInTransition">
<Setter.Value>
<toolkit:NavigationInTransition>
<toolkit:NavigationInTransition.Backward>
<toolkit:SlideTransition Mode="SlideRightFadeIn" />
</toolkit:NavigationInTransition.Backward>
<toolkit:NavigationInTransition.Forward>
<toolkit:SlideTransition Mode="SlideLeftFadeIn" />
</toolkit:NavigationInTransition.Forward>
</toolkit:NavigationInTransition>
</Setter.Value>
</Setter>
<Setter Property="toolkit:TransitionService.NavigationOutTransition">
<Setter.Value>
<toolkit:NavigationOutTransition>
<toolkit:NavigationOutTransition.Backward>
<toolkit:SlideTransition Mode="SlideRightFadeOut" />
</toolkit:NavigationOutTransition.Backward>
<toolkit:NavigationOutTransition.Forward>
<toolkit:SlideTransition Mode="SlideLeftFadeOut" />
</toolkit:NavigationOutTransition.Forward>
</toolkit:NavigationOutTransition>
</Setter.Value>
</Setter>
</Style>
After that, it is a simple matter of adding the following code to the initialization of the XAML page:
<phone:PhoneApplicationPage ...
Style="{StaticResource TransitionPageStyle}"
toolkit:TiltEffect.IsTiltEnabled="True">
The toolkit:TiltEffect.IsTiltEnabled="True"
is there to enable the tilting effect of controls like the ListPicker
or Button
when the user taps on it. Another nice feature of the Windows Phone Toolkit.
Charts
Unfortunately, neither the default Windows Phone 8 controls or the Windows Phone Toolkit includes charting controls. Because I did need to have charting capabilities in the app, I had a search for a free solution. There are not too many choices (amCharts [^] and Sparrow Toolkit [^] are the ones I found). I chose to go with the Sparrow Toolkit simply because it gave me quick results without too much effort. I need to admit I never gave amCharts a fair go simply because Sparrow Toolkit did everything I needed it to and I tried that one first.
I did, however, run into one very annoying "feature" when using the Sparrow Toolkit. In the app, I create lists of CategoryPoint
data. The CategoryPoint
object is a simple structure that defines a value and the category the value belongs to. Nothing shocking you would think if it weren't for the fact that the CategoryPoint
class depends on some static fields in the SparrowChart
class being setup properly. These fields are setup when creating a chart for the first time. Not a very nice design. The problem is that I create the list of CategoryPoint
objects before any chart is created resulting in an exception. I have worked around this as follows:
class MoneyPitRepository
{
private SparrowChart _stupidWorkAround;
...
private List<CategoryPoint> GetFullYearAsMonthCategory(List<MonthlyChartData> data)
{
if (_stupidWorkAround == null)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
_stupidWorkAround = new SparrowChart();
});
}
var yearData = new List<CategoryPoint>();
for (int i = 1; i <= 12; i++)
{
var dataPoint = new CategoryPoint
{
Value = data.Where(m => m.Month == i).Select(m => m.Value).SingleOrDefault(),
Category = CultureInfo.CurrentUICulture.DateTimeFormat.GetAbbreviatedMonthName(i)
};
if (Double.IsInfinity(dataPoint.Value))
{
dataPoint.Value = 0.0;
}
yearData.Add(dataPoint);
}
return yearData;
}
}
This will not win any prizes, but it did solve my problem. Maybe, someday, I will revisit this issue and come up with a nicer solution, but for now, I'll leave it as it is.
The Map Control
MoneyPit has the possibility for the end user to use the location services to log the position where a fill up has taken place. When the Phone location services are activated and the user consented in MoneyPit using the location services, the location for a new fill up is logged. When a location is logged, the edit fill up page will get an extra pivot item containing a Map
control which shows the location with a Pin
.
Now the view model for the edit fill up page contains two GeoCoordinate
properties which will be set to the same position initially. One position for the center coordinate of the Map
and one for the position of the Pin
. Now normally, you would say well it is the same position so just use one property and bind to that OneWay
from the Map
and the Pin
and be done with it. That unfortunately does not work. You must bind the Center
property of the Map
control TwoWay
or else binding will not work. That means that when the user scrolls the map, the CenterPosition
property in the view model will also be updated. It is after all TwoWay
binding that must be used. Now if this property was being used for both the Map
center position and the Pin
position, it would mean that the pin would be moved when the user scrolls the map. We do not want that and that is why we have a CenterPosition
property for the Map
and a PinPosition
property for the Pin.
The App Icons
MoneyPit was started using the standard app icons the Windows Phone SDK offers. This soon turned out to be insufficient so I went on the prowl to find icons that I could use for the app. Soon, I found the Modern UI Icons [^] site from which you can download an icon package which should be more than enough for any kind of app. It even includes XAML path and Blend designer files. A very comprehensive piece of work.
Simply read the license (it is included in the Assets folder of the source code download of this article) to understand how you can use these icons.
SkyDrive
MoneyPit has the possibility to export information to your SkyDrive account and to import that information again. Microsoft has created the Live SDK [^] which allows, among other things, communication with SkyDrive. While working on the SkyDrive code, I found that when the app is deactivated while a call to SkyDrive is running sometimes, not always, will throw a TaskCanceledException
when the app is resumed again. To make the behavior consistent, I have created a solution which will cancel any running SkyDrive task before the app is deactivated. That way, I can be sure that when the app is resumed, I get a TaskCanceledException
.
I have done this by creating a CancellationTokenSource
each time a SkyDrive function is executed. This CancellationTokenSource
is passed to all async Live SDK methods used for the SkyDrive function. The CancellationTokenSource
is also stored in a list while it is being used.
public static class SkyDrive
{
...
private static CancellationTokenSource GetCancellationTokenSource()
{
var result = new CancellationTokenSource();
lock (_tokenLock)
{
_tokens.Add(result);
}
return result;
}
public static async Task<SkyDriveData> GetFilesInMoneyPitFolder()
{
var result = new SkyDriveData { Result = SkyDriveResult.NotConnected, Data = null };
var cts = GetCancellationTokenSource();
if (cts != null)
{
try
{
result.Data = await GetFilesFromFolder(cts.Token);
result.Result = SkyDriveResult.ImportOk;
}
catch (LiveConnectException)
{
result.Result = SkyDriveResult.LiveConnectError;
}
catch (TaskCanceledException)
{
result.Result = SkyDriveResult.TaskCanceledError;
}
finally
{
ClearCancellationTokenSource(cts);
}
}
return result;
}
public static void CancelAllRunningTasks()
{
lock (_tokenLock)
{
foreach (var cts in _tokens)
{
cts.Cancel();
}
_tokens.Clear();
}
}
...
}
When the app gets deactivated while one of the SkyDrive functions is still running, the CancellationTokenSource
associated with the SkyDrive function is canceled before the app is deactivated. This way, I can be sure I get a TaskCanceledException
after app resume and I can act accordingly.
public partial class App : Application
{
...
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
SkyDrive.CancelAllRunningTasks();
IsolatedStorageSettings.ApplicationSettings.Save();
}
...
}
The Visual Studio Project
I have enabled NuGet package restore in the solution which should download the necessary packages for you when you first build the solution. It is, however, possible that you need to install the SQLite extension before the solution will compile. Also make sure you make the necessary changes to the SQLite.vcxproj project file to reflect the version of SQLite you are using. The solution included in this article uses version 3.8.2 of SQLite.
The application uses SkyDrive and Maps. This means it uses a Live Connect Client ID and a Map application ID and authentication token. These are located in the Tokens
class in the Helpers source code folder. The posted source does not include the application ids and tokens used by MoneyPit. You will need to get these yourself for the functionality to work properly on your phone.
To deploy the app to a Phone, it needs to be Developer Unlocked [^]. It will not deploy to a locked Phone.
If you are going to deploy to a phone, remember that you should switch to an ARM build for both projects in the solution (are there x86 based phones out there?). When you are going to deploy to the emulator, use a x86 build.
I will do my best to keep this article updated but it is possible that the article is out of sync with the version published in the store. Depending on how long it takes to get updates to either the article or the app published, the CodeProject article can contain newer or older source code than that was used to build the latest published app in the store.
App Certification
There really is not much to say about this. MoneyPit passed the certification process first time. The most important thing is to read through the App Certification Requirements [^] and make sure you adhere to those requirements that apply for your app. The list is long but reading through it makes sure your app does not fail certification because you missed one of the requirements.
For MoneyPit, the certification process took four days. I do not know if this a normal amount of time but I can imagine that it may take longer than that depending on how many apps are in the certfication queue and how complex the app is. The more features you have in your app, the more work has to be done to actually check if it meets all of the requirements.
References
MoneyPit was not created in a vacuum. I have used many, many resources from the web to create it so it would be quite impossible to list them all. However, the following lists the most important ones:
- Windows Phone Dev Center [^]
This is the starting point for any Windows Phone Development I guess. Especially, the Jumpstart videos offer a tremendous amount of information to get started. - Live Connect Development Center [^]
The site for Live Connect resources. The SkyDrive functionality of MoneyPit was created using the Live SDK which can be downloaded from this site. - DialogBehaviour [^]
This is where I got the idea from for the behavior driver dialogs. - NumericInputBox [^]
A culture neutral numeric input box which is used in MoneyPit. - Navigation and MVVM [^]
A blog post explaining the idea of a navigation service to control page navigation from a view model. The MoneyPit navigation service is build around this idea.
In Conclusion
As I have mentioned at the start of this article, I did this project as a learning exercise. I did it to get into mobile development but also (and maybe even more so) to get into XAML, MVVM and the latest .NET Framework. I know that MVVM and XAML have been around for quite some time now, but I never before had the opportunity to actually do something with it.
I am sure that for the seasoned Windows Phone/.NET developer, there are a lot of things that could have been or should have been done better/easier/different. To those people, I would like to say "Teach Me." Please let me know what could have been done better or different. Let me know what I did wrong and also why what I did is wrong.
History
Version 1.3
- Bug fix: Database restore did not always work
- Some cosmetic changes
- Removed screenshots from the source download. They should not have been in there in the first place. My apologies.
- Added a summary overview for the total history of a car
Version 1.2
- Spell checked the resources
- Added database backup and restore (on SkyDrive)
- Added location and note markers to the fill up list
- Fixed a few design time data issues
- Enhanced the UI a bit
Version 1.1
Version 1.0