Tips and Tricks for building Smartphone applications

Ta da, well I think I've finally finished the next version on my Convert It app targeting the Windows Mobile Smartphone and the Compact Framework V1.

I've developed most of this version using Windows Vista X64 RC1 running on a system identical to this AMD Dual Core System using Possibly The World's Smallest Team Foundation Server and I have to say the dev environment has been an absolute pleasure, actually it's quiet addictive, but hey, whatever rocks ya boatJ

Convert It 2.6 has had near 20000 downloads, so feeling inspired by that, and splashing out on the truly awesome iMate Smartflip SmartPhone, easily the best phone I've ever owned, The Corporate Global Challenge and the pain of expenses I've finally finished "Convert & Track It 3.0". I started version 3 last Christmas but ran out of time and as Andrew keeps reminding me, shipping is a feature too, so I'm done and tested, so watch this spaceJ

Building mobile apps for some of the coolest gadgets on the planet is a stack of fun and you can build compelling connected apps that your users will love you for, that, plus the challenge of building in a resource constrained environment.

To get started you need a PC with the fastest CPU you can lay your hands on (the device emulator runs on a single thread so doesn't benefit directly from multi core CPU systems) and tons of patience, at least initially and see the end of this article some additional references to help get started.

So around 11000 lines later I've learnt a thing or two, these aren't "official" tips and tricks but they worked for me and could save you a lot of time. These tips and tricks are geared towards the Compact Framework V1 and Smartphone, the version that ships on Windows Mobile Smartphone devices 2003 and beyond (note, for great flexibility you can install the Compact Framework V2 on Windows Mobile 5 devices and above).

The areas I cover are:-

Smartphone Forms Navigation

Compact Framework Memory Management

Remember to Close() and Dispose() your forms

Scaling UI Controls for different screen resolutions or DPIs

Scrolling Forms

Issue with Building Menus at Runtime

Designed for Windows Mobile Software for Smartphone Handbook

Smartphone Forms Navigation

Forms navigation on the compact framework is a pain and you have to do some work to make it usable.

Two things happen.

· Firstly, in the task manager, you'll see the name of each form you instantiate.

· Secondly, a user navigates several forms deep in to your application, they then switched to another app, ie the phone rings, they then back to your app and navigate back to the previous form in the app only to find they have been taken back to the Today screen or some other app, very confusing. For a fuller discussion see Bad, Wicked Dialogs.

To solve the first issue you need pass on the form name of the calling form to the destination form and blank out the form name of the calling form and reverse this on the way back.

public static DialogResult ShowDialog(MyForm dialog, MyForm parent) {

string caption = parent.Text;
dialog.Text = caption;
if (dialog != parent) { parent.Text = string.Empty; }
dialog.ShowDialog();
dialog.Text = string.Empty;
parent.Text = caption;
parent.ForeGround();
return dialog.DialogResult;

}

 

To solve the second issue you need to know the Windows Handle of the calling form and pull the calling form forward when closing the destination form – simple ehJ I found the most reliable way to do this on the Smartphone was by creating my own form class inheriting from System.Windows.Forms.Form, and from within the form constructor getting the Windows Handle for later reference.

public class MyForm : System.Windows.Forms.Form {

public MyForm(): base() {

hwnd = GetHandle();

this.Closing += new System.ComponentModel.CancelEventHandler(MyForm_Closing);

}

 

private IntPtr hwnd;

 

private IntPtr GetHandle() {

this.Capture = true;

IntPtr hwnd = GetCapture();

this.Capture = false;

return hwnd;

}

[DllImport("coredll.dll")]

public static extern IntPtr GetCapture();

 

In my form class I have a Foreground method who's role is to pull itself (the form) to the foreground, you see I call it from the ShowDialog helper method above.

public void ForeGround() {

SetForegroundWindow(hwnd);

}

 

[DllImport("coredll.dll")]

public static extern bool SetForegroundWindow(IntPtr hwnd);

 

Compact Framework Memory Management

For a great discussion on the Compact Framework Memory Management check out this article

Like the full .NET Fx the Compact Framework is a managed memory environment providing you the benefits of bounds checking, type checking, garbage collection etc and it does help a lot to build more robust applications.

Unfortunately "Out of Memory" conditions occur with compact framework apps seemingly on a random basis and I've found you need to actively help the memory manager out in places especially when you are about to allocate a large objects such as a forms.

For better or worse the System.GC.WaitForPendingFinalizers() and System.GC.Collect() are your friends and I call these before and after doing anything major. Yup, it goes away from managed memory principles but it works and random failures of forms and other objects are a thing of the past... Btw, calling these two methods is very quick!!

Remember to Close() and Dispose() your forms

Speaking of forms, because you display a form on the Compact Framework using ShowDialog you must manually close and dispose the form object. I normally do this in a try catch finally block, checking the form reference is not null before closing, disposing etc. If you don't do this your application will leak non managed resources. See https://msdn2.microsoft.com/en-us/library/w61zzfwe.aspx    

Scaling UI Controls for different screen resolutions or DPIs

To build a Smartphone app that looks good on different screen resolutions you have to do some additional work or else you'll find your UI gets jammed up in the top left hand corner on higher resolution screens.

When you build a Smartphone app in Visual Studio the designer is geared towards 96dpi, higher resolution devices with QVGA displays run at 131dpi. So at run time you need to determine the DPI of the device. This is easy but you need to call some unmanaged code as follows.

https://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceui40/html/cerefGetDeviceCaps.asp

   

static private int LOGPIXELSX = 88;

static private int LOGPIXELSY = 90;

static private int dpiX = GetDeviceCaps(IntPtr.Zero, LOGPIXELSX);

static private int dpiY = GetDeviceCaps(IntPtr.Zero, LOGPIXELSY);

static private float widthRatio = (float)dpiX / 96.0F;

static private float heightRatio = (float)dpiY / 96.0F;

[DllImport("coredll.dll", EntryPoint = "GetDeviceCaps", SetLastError = false)]

internal static extern int GetDeviceCaps(IntPtr hwndOwner, int id);

 

widthRatio and heigthRatio hold the scaling ratio you now need to apply to all the controls... ScaleControls is re-entrant to deal with nested control containers such as panels.

public void ScaleControls(ControlCollection ctrls) {

if (dpiX == 96) { return; }

foreach (Control ctrl in ctrls) {

if (ctrl.Controls.Count > 0) { ScaleControls(ctrl.Controls); }

ctrl.Size = new Size((int)(ctrl.Size.Width * widthRatio), (int)(ctrl.Size.Height * heightRatio));

ctrl.Location = new Point((int)(ctrl.Location.X * widthRatio), (int)(ctrl.Location.Y * heightRatio));

}

}

 

This works just fine on the Pocket PC as well but it doesn't handle screen orientation changes supported by newer devices. If you targeting the Pocket PC with applications built on the Compact Framework V2 then it's worth checking out the Mobile Client Software Factory.

Btw, and as tempting as it may seem, higher resolutions displays are for better clarity not for crowding more info on.

Scrolling Forms

Scrolling forms are not supported on the Smartphone with Compact Framework V1 (they are in the Compact Fx 2) but you can create the illusion they do scroll by building your form in a panel control. It works as follows, as the user navigates through each control on the form the control fires the GotFocus event, in code you check to see the control is with the visible display area, if not you slide the panel control containing the controls up or down to bring the control in to view – it's magic and it works.

I got the basis of this code from "Crafting Smartphone User Interfaces Using .NET Compact Framework", it worked moderately well but this is my moded code that works pretty well.

Call this code from all the non label controls on your form from their GotFocus event.

protected void SetScrollPosition(object topSender, object bottomSender, VScrollBar scrollbar, Panel panel) {

//get bounds of controls to focus on

int top = ((Control)topSender).Top;

int bottom = ((Control)bottomSender).Bottom + MasterForm.ySpacing;

int panelOffset = 0;

scrollbar.Value = bottom - this.ClientSize.Height;

//check if control is above view

if (bottom < this.ClientSize.Height) {

//scroll up to view, ensuring the topSender is first visible on form

    panelOffset = top;

}

//check if control is below view

if (bottom >= (this.ClientSize.Height)) {

//scroll down to view, ensuring the bottomSender is last visible on form

    panelOffset = bottom - this.ClientSize.Height + 5;

}

// Set the panel position on the form, to redraw the application

panel.Top = -panelOffset;

}

 

Issue with Building Menus at Runtime

I dynamically build menus in my app based on the content of an XML file, when the content of the XML doc changes I change the menus on the fly. So I executed this bit of code to clear the existing menu contents

while (menuItem2.MenuItems.Count > 0) {

this.menuItem2.MenuItems.RemoveAt(0);

}

 

but alas it blows up with a null reference exception. I discovered the issue was with removing the last menu item. So I remove all but the last menu item, add in the new menu items and then remove the menu item I left to stop the null reference exception.

while (menuItem2.MenuItems.Count > 1) {

this.menuItem2.MenuItems.RemoveAt(0);

}

//add in new menu items

...

//remove the menu item I left behind

this.menuItem2.MenuItems.RemoveAt(0);

 

It's a bit ugly but it works and the issue is fixed in Compact Fx V2.

Designed for Windows Mobile Software for Smartphone Handbook

The Designed for Windows Mobile Software for Smartphone Handbook, is a very useful guide if you want to get you application certified, see Mobile2Market for more details, or you are just after general information on UI conventions, where you should install things etc... It's pretty easy doc to digest and worth a quick read.

There are a stack of great Windows Mobile developer resources at https://msdn.microsoft.com/windowsmobile

There is more but this'll do for tonight.

Cheers, Dave