This article is the first part of a potential series, which will describe the process of creating an application for convenient management of the home budget for WP7 from the very beginning. At the same time, various APIs and platform capabilities will be used in the development in order to maximize their coverage. At the same time, the program will be absolutely usable (it will have at least one permanent user), and there will not be garbage used exclusively for academic purposes.
The choice of the type of application is due to the fact that the first program I bought in the marketplace was exactly a budget but some useful functions (for example, automatic backup on SkyDrive, etc.) are not in it and are not planned.
Principles of development
The functionality will be increased gradually, by small iterations of 10-14 days, of which 3-5 days are allocated for running into the previous version, 1 day for brainstorming and the rest for the implementation of the conceived, testing and design of the article.
')
Since I plan to use the created application on a daily basis instead of a similar utility, the functionality will have to be exactly what is needed, and not what is easier to write.
First iteration: plan
In the first case, we should be able to add transactions and have at least basic categories, because otherwise, after a week of use, there will be a small cesspool that you don’t even want to open.
In this article we will in detail (perhaps even too much) consider the process of creating a database and the process of adding categories. We will not consider the process of adding transactions since it is almost 100% similar to working with categories and its consideration would inflate an article to too large. Also, we will not consider the process of editing categories, but given the model of work used - it will take + - 10 minutes.
If necessary, these topics can be glimpsed in the next article or skip.
Accordingly, we will consider the following questions:
- Basic principles of development for WP7
Some Metro Basics [1]- Working with SQLCE database and creating a model based on the code-first principle
Prerequisites
Development: database creation
In case someone has a desire to use this article as a lesson - after each logically completed and more or less important stage, a link to the archive with the results will be given. All sources on SkyDrive . Separate files in the case of abbreviations will be presented on pastebin
.
Choosing the structure of the application, I focus primarily on the logical separation of the functions performed by projects. Based on these considerations, to create a database and all data types, we create a separate project, Entities, do not forget to add System.Data.Linq and Microsoft.Practices.Prism to References.
Solution here .
Immediately make a reservation - by the concept of a transaction I mean a financial transaction, not a transaction in the database.When creating the database, we will use the code-first approach
[3] . For today's task, we will be satisfied with as many as two tables - Transactions and Categories. Create two empty tables and add them to the database.
Transaction.cs[Table(Name = "Transactions")] public class Transaction : NotificationObject
Category.cs [Table(Name = "Categories")] public class Category : NotificationObject
Database.cs public class Database : DataContext { private static string DBConnectionString = "Data Source=isostore:/Database.sdf"; public Database() : base(Database.DBConnectionString) { this.DeferredLoadingEnabled = true; } public Table<Bick.Budget.Entities.Categories.Category> Categories; public Table<Bick.Budget.Entities.Transactions.Transaction> Transactions; }
Do not even think of making tables in the database properties and not fields. I, because of the stylistic habit of using properties for public, killed about an hour trying to understand why the database is not working at all.Here, transactions and categories inherit the NotificationObject defined in Prism for normal interaction with the UI in the future. By the way, we use the MVVM pattern when developing.
In the database constructor, set the DefferedLoadingEnabled flag to disable the automatic loading of related objects from the database. It will be necessary - we will separately specify.
We proceed to the formation of the contents of the tables. As a result, we should have something like this:

Highlights in
Transaction.cs 
:
[Column(IsPrimaryKey = true)] public Guid ID { ... } ... private EntityRef<Categories.Category> category; [Association(Name = "FK_Transactions_Category", Storage = "category", ThisKey = "CategoryID", IsForeignKey = true)] public Categories.Category Category { get { return this.category.Entity; } set { Categories.Category previousValue = this.category.Entity; if (((previousValue != value) || (this.category.HasLoadedOrAssignedValue == false))) { if ((previousValue != null)) { this.category.Entity = null; previousValue.Transactions.Remove(this); } this.category.Entity = value; if ((value != null)) { if ((value.AddedTransactions == null) || (!value.AddedTransactions.Contains(this))) { value.Transactions.Add(this); } this.CategoryID = value.ID; } else { this.category = new EntityRef<Categories.Category>(); } this.RaisePropertyChanged(() => this.Category); } } }
The ID parameter is a table column and primary key. The remaining columns are also specified by the Column attribute. More details about Attribute-based mapping can be read on
msdn .
Category together with CategoryID are responsible for linking transactions to categories, and in this example we created the foreign key FK_Transactions_Category. The reason for the bloated setter is that when assigning a transaction to the parent category, we must delete the transaction from the previous parent category, and add the new one to the new one. Roughly speaking - Navigation Property from
EF . In turn, Category uses minimum code to implement this.
Category.cs 
:
public EntitySet<Transactions.Transaction> Transactions { get { if (this.transactions == null) { this.transactions = new EntitySet<Transactions.Transaction>(); this.transactions.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(this.OnTransactionsChanged); } return this.transactions; } ... } ... private void OnTransactionsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { this.AddedTransactions = e.NewItems; foreach (Transactions.Transaction t in e.NewItems) { t.Category = this; } this.AddedTransactions = null; this.RaisePropertyChanged(() => this.Transactions); }
As a matter of fact, in Category we catch the situation when no category is assigned to a transaction, and a new item is added to the category transaction list.
The base is ready.
Solution on SkyDrive .
Development: we create UI
The Shell project was created as a Windows Phone Application, that is, at this stage we will not use such controls as Pivot / Panorama. User interaction with the application will be approximately as follows:
Expression Blend with SketchFlow (not included in the free SDK) and the sketchflow template for WP7 ( CodePlex ) were used for creation.
We will divide these screens into the following View: New / Edit transaction, New / Edit category, Categories list, Transactions list, and the part responsible for working with transactions is transferred to a separate project.
Solution on SkyDrive .
First of all, we need to implement the functionality of viewing the list of categories and adding categories. There is nothing special in this, but as we try to focus on performance, we will need to refine our Database a bit. The fact is that when viewing the list of categories, we are not going to edit anything - we just need to quickly get the list of categories as quickly as possible. To do this, we will make this edit in
Database.cs :
public Database(bool isReadOnly = false) : base(Database.DBConnectionString) { if (!this.DatabaseExists()) { this.CreateDatabase(); } this.DeferredLoadingEnabled = true; this.ObjectTrackingEnabled = !isReadOnly; }
Thus, with isReadOnly == true, we disable tracking of context objects for changes, which on average increases the speed of simple reading by more than 10 times.
When creating a UI, one of the problems we face is the inability to attach any Behavior to the ApplicationBarButton (we need it for binding to the team). In Prism.Interactions there is a DependencyProperty ApplicationBarButtonCommand but for some reason it did not work for me. Therefore, I had to use the quite convenient
AppBarUtils library.
Interesting moments from
CategoriesView.xaml 
:
<phone:PhoneApplicationPage xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:appbar="clr-namespace:AppBarUtils;assembly=AppBarUtils"> … <phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="False" Mode="Default"> <shell:ApplicationBarIconButton IconUri="/icons/appbar.add.rest.png" Text="add new"/> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar> <i:Interaction.Behaviors> <appbar:AppBarItemCommand Id="add new" Command="{Binding Path=AddCategoryCommand}"/> </i:Interaction.Behaviors>
Most often, the actions of the buttons will be transitions to other pages of the application and we need to make a convenient mechanism for working with navigation from the ViewModel. A convenient and relatively familiar (I once worked with the desktop MVVM on a similar principle) the method is described
here . A similar principle we implement in our Common project by creating the ApplicationController class. Also all our View's will be defined in the static KnownPages class:
public static class KnownPages { public const string AddCategory = "/Views/CategoryAddEditView.xaml?mode=Add"; public const string EditCategory = "/Views/CategoryAddEditView.xaml?mode=Edit&id={0}"; public const string ListCategories = "/Views/CategoriesView.xaml"; public const string CategoryDetails = "/Views/CategoryDetailsView.xaml?id={0}"; }
, a NavigateTo () from ApplicationController (still a little left of the original) will look like this
public void NavigateTo(string url, params object[] data) { Uri address = new Uri(String.Format(url, data), UriKind.Relative); PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame; root.Navigate(address); }
Now, since we are passing the
mode = Add parameter to the AddEdit page, we need to catch the navigation event in the ViewModel and get the data from the string. Unfortunately, at the moment I stopped at the option of overriding the OnNavigatedTo method in CodeBehind and calling the corresponding method in the ViewModel.
CategoryAddEditView.xaml.cs :
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); ((CategoryAddEditViewModel)this.DataContext).OnNavigatedTo(this.NavigationContext, this.NavigationService.BackStack.First()); }
As can be seen from the code, we transfer not only the navigation context (from where it is convenient to pull the parameters from the page address), but also the page from which we switched to the current one.
Now it's time to implement the process of adding a category. Normal View and Normal ViewModel. But there are two but. The first is that we will use the same MV-VM for editing categories (will be homework), respectively, we receive and process the parameter mode from the NavigationContext. The second is that in WP7 for TextBox, a change in the value of a property occurs only when the element loses focus. This is not implemented in native ways, so for this we use Prism (File CategoryAddEditView.xaml):
xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Interactivity;assembly=Microsoft.Practices.Prism.Interactivity" ... <TextBox TextWrapping="Wrap" Text="{Binding Path=Category.Name, Mode=TwoWay}"> <i:Interaction.Behaviors> <prism:UpdateTextBindingOnPropertyChanged /> </i:Interaction.Behaviors> </TextBox>
The process of category preservation looks like this:
CategoryAddEditViewMode.cs public void SaveCategory() { if (!this.isEditMode) { this.model.AddCategory(this.Category); ApplicationController.Default.GoBack(); } }
What you should pay attention to - we do not go to the CategoriesView page, but return to the previous page. It is worth paying attention to similar transitions within the application so that the user is not at a loss where he suggested after clicking on the Back button.
In
CategoryAddEditModel.cs, saving looks like this:
public void AddCategory(Category cat) { using (Database db = new Database()) { db.Categories.InsertOnSubmit(cat); db.SubmitChanges(); } }
It can be seen that there are no checks and validations - and this is bad. But for the first article there is already quite a lot of material, and now it is more important for us to finish the main functionality and start using the program - we'll do the rest between the articles or in the following.
When returning to the list of categories, View and ViewModel are not re-created, so when going from the list page to the adding page we will set the flag IsReloadPending and after return we will process and reset it.
CategoriesViewModel.cs :
private void AddCategory() { this.isReloadPending = true; ApplicationController.Default.NavigateTo(KnownPages.AddCategory); } public void OnNavigatedTo(NavigationContext context, JournalEntry lastPage) { if (this.isReloadPending) { this.isReloadPending = false; this.Categories = this.model.GetCategoriesList(); } }

Results
Today we received all the necessary tools for working with WP7, tried working with the database, prepared the ground for the further development of the program and learning the programming techniques for Windows Phone. We also ran into a couple of jambs (ApplicationBar, TextBox) and overcame them.
Yes - we did not receive a program ready for use (just as a data collector), but we are separated from this stage in about 1-2 hours. Who cares - try it yourself.
Solution on SkyDrive .
In parallel, those familiar with C # should have realized that Microsoft’s mobile platform is quite easy to learn and can easily be mastered by yourself.
At the same time, I realized that writing such an article takes quite a long time. The article was written in the diary format in parallel with the writing of the application.Future plans
In the next article I would like to consider:
- process of creating secondary tile
- application launch optimization
- prototyping an application in SketchFlow
Literature
- WP7 UI / UX Notes
- Developer's Guide to Microsoft Prism
- Programming Guide (LINQ to SQL)