Windows Mobile Widget 101 – Handling Form Factors

So far in this series I’ve covered Getting Started, generating Start Menu Icons and a Gadget Side Note, all with a single static page that will generally display ok on all devices. However, when you get into building more complex widgets and testing on different devices you will notice that what works on higher resolution devices (Eg the HTC Diamond 2 or the HTC HD2) won’t look good on a smaller device (Eg HTC Snap). Whilst you may be thinking “Oh goodness, please don’t tell me we have to handle all the different resolutions differently,” the good news is that for the most part you only really need to worry about two classifications “high res” and “low res” devices.  Devices are typically divided into those with a width >=480 (high res) and those with a width<480 (low res).

The easiest way to change the way your widget displays for different resolutions is to use different css files to determine the style of each element. Our widget doesn’t currently use a css file, so lets add two css files in order to handle high and low resolution displays. Actually, while we’re at it we’ll include a gadget.css file which we will use in order to specify the height/width properties that were manually added to the Main.htm file in the previous post. Put all of these css files into a css folder to keep your project tidy.

In order to dynamically set the correct css file we’ll need to write some javascript. We’ll actually add two javascript files, one called main.js which will do most of the user interaction logic and one called widget.js which will contain most of the widget specific logic (the idea being that this file can be reused amongst your widgets). Again, put these files into a js folder within your project.

The last files we’re going to include are two background images, one for high res and one for low res.  In this case the files are background.png (480×328) and background_lowres.png (240×164) in the images folder. With all these files added the build files list should look like:

—————–
files.txt
—————–

config.xml
Main.htm
icon.ico
icon.png
js*.js
css*.css
images*.png

—————–
gadget_files.txt
—————–

gadget.xml
Main.htm
icon.png
js*.js
css*.css
images*.png

In this case the only difference between the high and low resolution layouts is that it’s going to use a different background image. This makes the css files relatively simple:

—————– 
highres.css
—————–

body
{
    background: transparent url(‘../images/background.png’) no-repeat center top;
}

—————– 
lowres.css
—————–

body
{
    background: transparent url(‘../images/background_lowres.png’) no-repeat center top;
}

—————– 
gadget.css
—————–

body
{
    height:200px;
    width:200px;
    border:1px solid #000000;
}

Note that the gadget.css doesn’t specify the background – instead of having a third layout we are going to combine the highres.css and the gadget.css values. This means that the gadget.css only needs to contain values that are unique to the gadget layout. You could take this approach with the low res display as well but given that most elements/class styles will change between high and low res it’s probably easier to manage as two unique layouts.

Ideally we want to specify the style sheet as early as possible in the initial loading/rendering of the widget.  This is done by hooking into the onLoad event of the body element in Main.htm. Note that you also need to include references to the javascript files:

—————– 
Main.htm
—————–

<html >http://www.w3.org/1999/xhtml">
    <head>
        <title>Hello Widget</title>
        <!– Javascript –>
        <script type="text/javascript" src="js/widget.js"></script>
        <script type="text/javascript" src="js/main.js"></script>
    </head>
    <body onload="onLoad();">
        Hello Widget World!
    </body>
</html>

In the onLoad event we simply need to determine whether the widget is running on a high or low res device and load the appropriate css files:

—————– 
Main.js
—————–

/****** Constants ******/
var HighResCss = "css\highres.css";
var LowResCss = "css\lowres.css";
var GadgetCss = "css\gadget.css";

/****** Variables ******/
var isGadget = false;

/********************************************************************
This is the entry point to Widget code.
*********************************************************************/
function onLoad() {
    // Determine if this is running as a gadget (true if System.Gadget is defined)
    if (typeof System != "undefined" && typeof System.Gadget != "undefined") {
        isGadget = true;
    }

    if (WidgetAPI.isHighResolution() == true) {
        WidgetAPI.applyCssFile(HighResCss);
    }
    else {
        WidgetAPI.applyCssFile(LowResCss);
    }

    if (isGadget == true) {
        WidgetAPI.applyCssFile(GadgetCss);
    }
}

And we of course need to implement the WidgetAPI functions in the widget.js file.  Note that to avoid confusion we create a WidgetAPI object rather than the existing window.widget object.

—————– 
Widget.js
—————–

var WidgetAPI = function () {
    return {
        // returns true or false to indicate if widget is running on a high resolution device or not
        isHighResolution: function () {
            return this.getClientWidth() >= 480;
        },

        // gets the widget’s client width
        getClientWidth: function () {
            return (document.documentElement.clientWidth==0) ? document.body.clientWidth : document.documentElement.clientWidth;
        },

        // apply css file
        applyCssFile: function (cssFileName) {
            if (typeof(cssFileName)!="undefined" && cssFileName!=null) {
                var cssNode = document.createElement(‘link’);
                if (cssNode) {
                    cssNode.setAttribute(‘rel’, ‘stylesheet’);
                    cssNode.setAttribute(‘type’, ‘text/css’);
                    cssNode.setAttribute(‘href’, cssFileName);
                    document.getElementsByTagName(‘head’)[0].appendChild(cssNode);
                }
            }
        }
    };
} ();

Running this gives the following displays: Widget (high res), Widget (low res) and Gadget

 image image

image

Note: After you have installed your widget on a device you don’t need to reinstall or upgrade your widget every time you want to see your updates.  Instead you can locate where your widget has been extracted to on the device and simply copy across the files that have changed.  For example the widget is currently extracted to Program FilesWidgetsUser17 so when I make changes, say to Main.htm, I just copy that file across to this folder. Then I just need to exit and restart the widget to see the changes take effect.

Leave a comment