Click here to Skip to main content
Click here to Skip to main content

Creating custom controls for Windows Store App (Javascript- WinJS)

, 26 Jun 2014
Rate this:
Please Sign up or sign in to vote.
WinJS custom control to plot/draw a simple donut like chart

Introduction

The Windows Library for JavaScript (WinJS) is a library of CSS and JavaScript files. It contains JavaScript objects, organized into namespaces, designed to make developing Windows Store app using JavaScript easier. WinJS includes objects that help you handle activation, access storage, and define your own classes and namespaces. It also includes a set of controls like AppBar, ListView, Rating, TimePicker etc.

Though there are many useful controls, they don’t cover all needs (for Ex: A generic control for plotting simple graph, tree view, animation etc.) and it is time for us to develop such custom controls, let’s understand how to do it.

Problem Definition

Let’s consider a simple problem where it’s required to plot a donut like graph to show up the percentage (%) values in our Windows Store App and it should be able to bind to the ViewModel property.

This is how our final graph will look like, Donut Graph

Solution

First things first,

Include WinJS references in HTML page, because our control will use APIs from these files.

<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

Few simple steps to create a custom WinJS control:

  • Define a Namespace and a Class for control
  • Initialize the control and define control options(in class constructor)
  • Implement the code to plot donut graph (we will be using HTML5 Canvas)
  • Using the control in HTML/JavaScript.

Define a Namespace and a Class for control:

WinJS provide APIs to create Namespaces and Classes in JavaScript, we will be using the same to create ours.

WinJS.Namespace.define() and WinJS.Class.define() are used to create Namespace and Class respectively.

WinJS.Namespace.define("Demo.UI", {
    DonutChart: WinJS.Class.define(function (element, options) {
       // Constructor: here we will validate the DOM element, define options and more.
       this.element = element || document.createElement("div");
    },{
       // Properties: here we will declare and define the properties, private methods and core logic lies here i.e. code to create graph in our case.
       donutValue: {
                    get: function () {
                        return this._donutValue;
                    },
                    set: function (value) {                       
                        this._donutValue = value;
                    }
    }// End of Properties
) // End of Control class definition
}); // End of Namespace definition
 

The constructor will take two parameters element and options where element is Element object and options we can configure according to our control let’s say radius in our case. Now we have our Namespace and Class ready to use.

Initialize the control and define control options:

In the constructor, we have to validate the element is existing or not if element doesn’t exist create an element (“div” in this example) and add a set of declaratively specified options (properties and events) to the control this is achieved using WinJS.UI.setOptions() method, this method call overrides default values for configurable options on the control.

function MyControl(element, options) {
                this.element = element || document.createElement("div");
                this.element.winControl = this;
                this.options = options;
                // Set option defaults
                this._donutValue = 0;
                // Set user-defined options
                WinJS.UI.setOptions(this, options);
 
                element.textContent = options.donutValue;// If it fails to plot graph then display only text
            }

Implement the code to plot donut graph:

We will be using one of HTML5’s great features Canvas to plot the graph, will create the donut chart in _createDonut() method [Note: _createDonut is made as private method so that this method should not be used outside the control]. We can create any number of HMTL element and logic inside this method.

Here is the code snippet where we used Canvas’s arc method to draw circles i.e donut chart

_createDonut: function () {
                    var settings = $.extend({
                        // These are the defaults.
                        color1: '#808080',
                        color2: '#107c10',
                        textColor: '#808080',
                        backgroundColor: "transparent",
                        radius: 20,
                        lineWidth1: 1,
                        lineWidth2: 5,
                        donutValue: 0
                    }, this.options);
                    if (this.donutValue > 0) {
                        try{
                            settings.donutValue = this.donutValue;
                            var percentage = settings.donutValue / 100;
                            var id = this.element.getAttribute("id") + "_canvas";
                            var width = this.element.offsetWidth;
                            var height = this.element.offsetHeight;
                            this.element.innerHTML = "";
                            this.element.innerHTML = "<canvas id='" + id + "' width='" + width + "' height='" + height + "'></canvas>";
                            var c = document.getElementById(id);
                            var ctx = c.getContext("2d");
 
                            var centerX = c.width / 2;
                            var centerY = c.height / 2;
                            radius = width > height ? height * 0.455 : width * 0.455;
                            //Inner circle
                            ctx.beginPath();
                            ctx.strokeStyle = settings.color1;
                            ctx.lineWidth = settings.lineWidth1;
                            ctx.arc(centerX, centerY, radius, 0 * Math.PI, 2 * Math.PI);
                            ctx.stroke();
                            //Outer Circle
                            ctx.beginPath();
                            ctx.lineWidth = settings.lineWidth2;
                            ctx.strokeStyle = settings.color2;
                            ctx.arc(centerX, centerY, radius, 1.5 * Math.PI, (1.5 + (2 * percentage)) * Math.PI) //calculating the end angle for arc
 
                            ctx.stroke();
                            //Display text
                            ctx.font = "15px Segoe UI";
                            ctx.fillStyle = settings.textColor;
                            ctx.fillText(settings.donutValue + "%", centerX - 14, centerY + 5);
 
                            clearInterval(this.terminateInterval);
                        }
                        catch (ex) {
                            clearInterval(this.terminateInterval);
                        }
                    }
                    else {
                        this.element.innerHTML = "<span style='color:black; text-align:center;'>---</span>";
                    }
                }

Assume that the control is given with an empty div element, in our code we will create a Canvas element dynamically and append it to the given div element. We are using JQuery $.extend() method to map the default options with the options configured from UI.

Putting the bits altogether:

WinJS.Namespace.define("Demo.UI", {
    DonutChart: WinJS.Class.define(function (element, options) {
       // Constructor: here we will validate the DOM element, define options and more.
       this.element = element || document.createElement("div");
       this.element.winControl = this;
       this.options = options;
       // Set option defaults
       this._donutValue = 0;
       // Set user-defined options
this.terminateInterval = 0;
       WinJS.UI.setOptions(this, options);
element.textContent = options.donutValue;   
    },{
       // Properties: here we will declare and define the properties, private methods and core logic lies here i.e. code to create graph in our case.
_notZero: 0,
       donutValue: {
                    get: function () {
                        return this._donutValue;
                    },
                    set: function (value) {                        
                           if (this._donutValue > 0) {
                                  clearInterval(this._notZero);
                        }
                           this._donutValue = value;
                           this._notZero = setInterval(this._createDonut.bind(this), 1000);
//I'm using setInterval here because the data for this control is from a ajax request and since WinJS will process the UI even before ajax request is completed so we will call _createDonut() methos in a interval and once data is available clea the Interval.
                       this.terminateInterval = this._notZero;
                        }
},
_createDonut: function () {
              //Copy from above
}
    }// End of Properties
) // End of Control class definition
}); // End of Namespace definition
 

Using the control in HTML/JavaScript:

Now we have created all necessary code and have it in say demo.ui.donutchart.js, let’s see how to use it in our HTML.

Include a reference to demo.ui.donutchart.js script file

<script src="../../js/customcontrols/demo.ui.donutchart.js"></script>

In HTML use the data-win-control attribute to set the control name Demo.UI.DonutChart, use the data-win-options attribute to set the options (Ex. { radius: 20,  color2: '#107c10' }) provided to the constructor and use data-win-bind to bind the data from ViewModel property.

So our html will be,

<div id="donut" data-win-bind="winControl.donutValue:AvgPercent " data-win-control="Demo.UI.DonutChart" data-win-options="{ radius: 20,  color2: '#107c10' }"></div>

Now question is how this will call the control constructor and draw the graph.

Answer is, when WinJS.UI.processAll() method is called from our code [usually this method will be called in default.js of the Windows Store App Solution ] the WinJS will parse the DOM, if the control is found mapping between control and the html done automatically by WinJS i.e. WinJS will instantiate the control and calls the constructor with element where the chart will be created  and options provided in data-win-options as parameters.

Final thing, we will use WinJS.Binding.processAll(document.getElementById("container"), ViewModel); to bind the data to the page or element.

Now debug/deploy the app and we can see the required graph on the screen.

References:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Murali Gowda
Software Developer (Senior)
India India
Yet to be written
Follow on   Twitter

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140821.2 | Last Updated 26 Jun 2014
Article Copyright 2014 by Murali Gowda
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid