Cookie Notice

Tuesday 11 September 2012

Upgrading a Windows Phone Database App – Part 2


After discovering all the things I did wrong with FusionWare Swim Tracker and then figuring out what I needed to do to fix them, I went about making the necessary changes.  Just to remind you, here were the five points or epiphanies I came to in deciding what to do in my previous "Part 1" Blog

  1. Keyboards are EVIL and Big is Beautiful!
  2. Include the right data and Spend as much time on design as you can BEFORE you code.
  3. Export of Data and SkyDrive is your friend.  USE IT!
  4. Don’t allow bad data and don’t assume the user will enter the correct data… Trust No One!  The Truth is Out There!
  5. Polish the Apple and Telerik Controls Rock!
So, how did I fix it?  Read on, MacDuff.

Keyboards are Evil

First is the motto that keyboards are evil.  To rid ourselves of the keyboard, especially when entering time, we had to combine a couple of tricks.  The first problem was what to replace the textbox with.  The answer was to use a TimePicker control.  It is included in the Microsoft.Phone.Controls.Toolkit you can download it separately for free from Microsoft (BTW, check out the TiltEffect control…  Really useful).  Description: C:\Users\Marks\Pictures\7picker_thumb_1.pngYou tap it and it loads a time picker page with looping numbers.  This created the second problem.  The looping numbers page for the time picker assumes you want a time of day, complete with AM/PM.  This is not so useful when entering a stopwatch time.  The solution to that is an attribute of the time picker called “PickerPageUri” which allows you to create your own page that will do whatever you want as long as it returns a time.  Notice in the code I have to format the string using another handy attribute “ValueStringFormat” so the time of day that is really stored there looks like a stopwatch time.
<toolkit:TimePicker x:Name="newTime"                    PickerPageUri="/TimePicker.xaml"                    ValueStringFormat="{}{0:HH:mm:ss.ff}"                    UseLayoutRounding="False"                    Value="{Binding TimenewTime, Mode=TwoWay}"/>

Once that was done, all I had to do was create looping controls.  At first I tried to write it from scratch.  That was a bad idea.  I could never quite get the controls right.  Then I realized I had Telerik RadControls that contained a nifty control called the RadLoopingList.  It did all the work for me.  All I needed to do was layout the Looping controls the way I wanted them.  Because the RadLoopingList Control is data aware, I just assigned a datasource containing the numbers from 0 to 9 or 0 to 5 depending on their position and Telerik takes care of the rest.  Piece of cake!
<telerikPrimitives:RadLoopingList                    ItemWidth="70"                           FontSize="64"                    ItemHeight="110"                    x:Name="loopingList1"                    HorizontalContentAlignment="Center"                    IsCentered="True"                    IsExpanded="False"/>

Include the Right Data

Try to strongly type your data as much as possible in the first go around.  I went and updated all my data types and added a few fields and even a couple of extra tables (associated with foreign keys) in this new version.  Then it occurred to me, “What happens to those that already have Swim Tracker and they update?”  The database installed and configured in version 1 had different data types and version 2 would not run against that database.  Oh Crap!
So I did a little research.  It turns out that on an upgrade you can add new fields, add new tables even, but there was no way at all to change a field type or remove fields.  Oh Snap!
By default a database is version 0 when it is installed (Unless you set DB_VERSION) to something else.  This means that just adding a column is pretty easy with code like this:
        DatabaseSchemaUpdater dbUpdater = db.CreateDatabaseSchemaUpdater();        if (dbUpdater.DatabaseSchemaVersion < DB_VERSION)        {            if (dbUpdater.DatabaseSchemaVersion < 2)            {                //added in version 2                dbUpdater.AddColumn("ThemeId");            }            dbUpdater.DatabaseSchemaVersion = DB_VERSION;            dbUpdater.Execute();        }

You can find a complete walkthrough of this type of update here: http://code.msdn.microsoft.com/Windows-Phone-71-Local-da2fdf80
However, this is NOT useful if you need to change a field from string to DateTime as in my case.  The only option you have is to do the following:
  1. Create Version 2.0 database (if it doesn’t exist)
  2. Copy all records from Version 1.0 database to 2.0 database (If 1.0 exists, maybe this isn’t an upgrate)
  3. Delete Version 1.0 database.
This article explains the theory quite well at MSDN: http://msdn.microsoft.com/en-us/library/hh394018(v=VS.92).aspx
*** If anybody is interested in Sample Code, let me know and I’ll do another blog containing sample code, it’s a little more lengthy than I would prefer for this blog.
The trick is to make sure this only happens once and then never again.  At the same time checking if it IS an update or just a new install.  You will also need to validate the data as you move it across.  Remember, there was no way for those poor users to save their data so if you screw it up or delete it, they are going to be (to quote Marvin the Martian) Veeerrrrrryyyyyy Angry.

Export of Data and SkyDrive is your friend.  USE IT!

This one turned out to be pretty easy.  Just download the Live SDK for Windows Phone here (http://msdn.microsoft.com/en-us/live/ff621310.aspx).  Put a reference in your project and add the  namespace to your XAML and the rest is easy.
xmlns:my="clr-namespace:Microsoft.Live.Controls;assembly=Microsoft.Live.Controls"
All you need to do is add the “SignInButton” control to your page and the rest is handled for you.  When pressed the user gets the Live Login screen and a permission agreement screen.  Once that is done, the “control” remembers next time you go in.  You do need to register your app with Live to get a client ID but all that information is available at the above link.
<my:SignInButton Name="btnLogin"                 Grid.Column="1"                 Grid.Row="0"                 BorderBrush="{Binding Source={StaticResource PhoneAccentBrush}}"                 Scopes="wl.basic wl.skydrive_update"                 SessionChanged="btnLogin_SessionChanged"                 HorizontalAlignment="Center"                 VerticalAlignment="Bottom"                 ClientId="0000000000000000">my:SignInButton>
The sample code executed when the session changes is all included in the Live SDK documentation. Copy and Paste implementation.  When uploading the file, I chose to upload a comma separated file.  However, SkyDrive doesn’t accept CSV files.  Mostly just office and *.txt files.  So I uploaded it as a *.txt file and then just let the user know what I did.

private LiveConnectClient liveClient;
private void btnSkyDrive_Tap(object sender, System.Windows.Input.GestureEventArgs e)    {    string myText = buildSaveFile();    UTF8Encoding enc = new UTF8Encoding();    var stream = new MemoryStream(enc.GetBytes(myText));    filename = "SwimTracker_" + DateTime.Now.Year.ToString("0000")       + DateTime.Now.Month.ToString("00")       + DateTime.Now.Day.ToString("00") + "_"       + DateTime.Now.Hour.ToString("00")       + DateTime.Now.Minute.ToString("00")       + DateTime.Now.Second.ToString("00")       + ".txt";    try    {       liveClient.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(liveClient_UploadCompleted);       liveClient.UploadAsync("/me/skydrive", filename, stream);    }    catch (LiveConnectException ex)    {       MessageBox.Show("Unable to upload" + ex.Message);    }}

Don’t Allow Bad Data

This one actually becomes quite simple once you get all the right controls in place.  By eliminating the keyboard input you make it almost impossible to enter invalid data.  There are events with most controls  that allow validation on input.  For this project I only needed to do two things.
  1. Make sure the data schema was correct for the data to be collected:
    1. Times should be DateTime then just format the output to look like a stopwatch.
    2. Use associated tables to contain the list of options for a particular field.  For example a table containing all the possible strokes linked to a stroke id field in the primary table.
  2. Don’t use the keyboard.  Make all input go through a controlled input method.  For times, I use the “TimePicker” control with a custom XAML form.  For most everything else, use a “DropDown” box that uses a full screen picker list.

Polish the Apple

Making the presentation look nice and conform to “Mango” styles is important.  Your App should  feel like it “fits”.  Things like a decent tile that uses a transparent background so it adopts the theme styles is small but looks nice.  It’s noticed when it’s done wrong and probably not noticed when done right.
Full screen select lists is better than combo drop downs.  This is NOT a desktop system and the user does NOT use a mouse.  Fingers are big.
Keep it simple.  Put too much stuff on a screen and it gets confusing.  Only grab what you need!
In the end, if you make a  useful App that looks nice and is easy to use and you are unique, you very well might get satisfaction!

1 comment:

  1. Here are a couple databases compatible with Windows Phone 7 and Windows Phone 8:
    http://www.kellermansoftware.com/p-43-ninja-net-database-pro.aspx

    ReplyDelete