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 example: 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:
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 are listed below:
- 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 Namespace
s and Class
es 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) {
this.element = element || document.createElement("div");
},{
donutValue: {
get: function () {
return this._donutValue;
},
set: function (value) {
this._donutValue = value;
}
}
)
});
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;
this._donutValue = 0;
WinJS.UI.setOptions(this, options);
element.textContent = options.donutValue;
}
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 elements 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({
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;
ctx.beginPath();
ctx.strokeStyle = settings.color1;
ctx.lineWidth = settings.lineWidth1;
ctx.arc(centerX, centerY, radius, 0 * Math.PI, 2 * Math.PI);
ctx.stroke();
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)
ctx.stroke();
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) {
this.element = element || document.createElement("div");
this.element.winControl = this;
this.options = options;
this._donutValue = 0;
this.terminateInterval = 0;
WinJS.UI.setOptions(this, options);
element.textContent = options.donutValue;
},{
_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);
this.terminateInterval = this._notZero;
}
},
_createDonut: function () {
}
}
)
});
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 (Example: { 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, the question is how will this call the control constructor and draw the graph.
The 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