WinRT JavaScript - Custom Controls
Written by Ian Elliot   
Article Index
WinRT JavaScript - Custom Controls
Standard configuration
Events

We have looked at WinJS controls and how to make use of them, but what do you do when the control you need doesn't exist? One solution is to create a custom control. As most of WinJS is just simple JavaScript with very little added, this is easier than you might expect.

 

This article is Chapter 8 of  Creating JavaScript/HTML5 Windows 8 Applications.

 

 

WinJS controls are chunks of JavaScript that follow some simple rules that allow the system to run them when it encounters a tag with a data-win-control attribute. That is, when you call processAll it scans the HTML looking for tags with data-win-control attributes. It uses the value of the data-win-control attribute to work out what JavaScript function to call. The JavaScript function constructs the control usually by manipulating the DOM.

For example, if you use a tag like:

<div data-win-control="Mycontrols.MyCustom">

then processAll will call

Mycontrols.MyCustom(element, params)

and this function acts as the constructor for the control.

Sounds easy - and it is, but there are a few things that we have to get right if we want our custom control to work like the supplied controls - but first let's see how easy it can be.

A Simple Custom Control

We first need some JavaScript that creates something that we can regard as the "workings" of a custom control. We could use a range of other HTML controls to build a composite control but an interesting alternative is to demonstrate how a canvas element can be used to create a completely customizable control - what in other Windows contexts might be called a "self draw" custom control. The idea is that by using a canvas element you can draw anything you like to represent your control and arrange for it to respond in any way that you like - it is completely flexible.

First we need to create a canvas element using nothing but JavaScript and draw something on it so that it is visible.  if you don't know about using the canvas element then see: A Programmer's Guide to Canvas.

First we create a canvas DOM object in the usual way and set its size:

var c = document.createElement("canvas");
c.width = "500";
c.height = "500";

Next we get its drawing context and draw a red filled rectangle:

var ctx = c.getContext("2d");
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(10, 10, 100, 50);

At this point we have a canvas element stored in c with a red rectangle drawn on it. If we were to append c to the DOM using say the usual appendchild function you would see a red rectangle appear at the appropriate location. This is going to be the basis for our demonstration custom control. It has to be admitted that this is not much of a really useful custom control, but it does have all of the properties and potential needed to do almost anything. You can draw what you like and respond to clicks and other events according to the location within the canvas.

Name Spaces

To turn this chunk of JavaScript into a custom control we first need to set up a namespace. This isn't exactly essential but adding custom control constructors to the global name space is a recipe for chaos. In practice you are free to do what you like but it is worth trying to work withing the guidelines. If you recall a JavaScript name space is just an object that you use as a container for everything else you want to create and work with. WinJS provides a utility function

WinJS.Namespace.define

the first parameter is the name of the name space i.e. the object that everything else is defined within and the second is the object containing properties that you want added to the name space. For example:

WinJS.Namespace.define("MyNameSpace", {});

adds nothing to the name space.

WinJS.Namespace.define("MyNameSpace", {total:0});

adds the variable total to the name space. Following this you can use

MyNameSpace.total=100;

All of the properties contained within the object are added to the name space.

If the name space doesn't exist then it is created. If it already exists the object is added which means you can use the define method to build up a namespace one object at a time.

So let's call our name space MyControls and the particular control MyCanvasControl. We need to define a constructor within the name space :

WinJS.Namespace.define("MyControls", {
            MyCanvasControl:

we could define the constructor function somewhere else and just refer to it in the call to define but that would mean inventing yet more names. Better to define it within the function call. In this case the body for the function is just the code given earlier to create the canvas object:

function (element, options) {
var c = document.createElement("canvas");
            c.width = "500";
            c.height = "500";
            var ctx = c.getContext("2d");
            ctx.fillStyle = "rgb(200,0,0)";
            ctx.fillRect(10, 10, 100, 50);
            element.appendChild(c);
        }
    });

Notice that the only thing that is new is that we append the canvas object to the element passed into the constructor.

Putting all this together gives:

WinJS.Namespace.define("MyControls",
{
 MyCanvasControl: function (element, options){
   var c = document.createElement("canvas");
   c.width = "500";
   c.height = "500";
   var ctx = c.getContext("2d");
   ctx.fillStyle = "rgb(200,0,0)";
   ctx.fillRect(10, 10, 100, 50);
   element.appendChild(c);
 }
});

 

If you now modify the HTML to read:

<body>
 <p>Content goes here</p>
 <div data-win-control=
           "MyControls.MyCanvasControl">
 </div>
</body>

and run the app you will see the canvas custom control appear below the "Content goes here" message.

 

canvas1

 

You can of course include as many instances for the custom control on a page as you need. In this case take note of the fact that it is 500x500 pixels and so on the large size.