Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

C# and .NET Tips and Tricks

Quests in programming in .NET

Μάρτιος 2012 - Δημοσιεύσεις

(Windows8) WinJS single page navigation and ViewModels

In the previous post, we have seen simple data-binding scenarios using WinJS and got a glimpse on the way a ViewModel can be created (following the MVVM pattern). In this post, we will build and application where there are more than one views (pages - simple page navigation app) with simple html elements and ListViews with real data. The question we need to answer is:

 

How do the ViewModels and MVVM can be applied in a single page navigation WinJS application?”

(FOR .NET Developers: if you are coming from the .NET world and especially XAML you will also get a chance to see how things like INotifyPropertyChanged and ObservableCollections apply to the WinJS world. The ViewModel presented here is as close as possible to the one that would be applied in a WPF-Silverlight window)

 

We will create a simple app that displays the births and deaths of celebrities in today’s date. Data will come from the RSS feeds provided by IMDB. Clicking on a celebrity should lead us to a new page where we get the IMDB webpage of that celebrity. Within this application, ViewModels will be used for each page. The requirements we have for our ViewModels are as follows:

 

  • There should be one ViewModel for each page.
  • The ViewModel should hold all the variables and methods required to provide the data to the view (html page)
  • The view will connect to the ViewModel through data binding.
  • All events generated by the page should also fire methods of the ViewModel.

 

Open VS 2011 and create a “Navigation Application” project. The “default.html” file that is generated contains the following:

 

<div id="contenthost" data-win-control="BirthsDeaths.PageControlNavigator" 
data-win-options="{home: '/html/homePage.html'}">
</div>

That defines an area that will be loading your pages according to your application’s state. This area is initialized and loads the “homePage.html” page (VS has also generated for you a navigate.js file that handles the navigation. For an introduction to single page navigation you can read this article).

 

You should consider each page that will be loaded in this area as a separate window or view of your application. Therefore, each page should have its own ViewModel. In our case, we have two pages: one (homePage) that displays the births and deaths in two ListViews and one (actorDetails) that displays the actor’s details when the user clicks on an item in the lists.

 

Let’s start with the homePage view that will look like the one in the following figure:

 

image

 

That is, there should be two lists one showing the deaths and one showing the births. Also, just below the main header we need to display the date and time and a downloading message when the data are fetched from IMDB. The html that generates that view is (of course there is the .css that also does the layout magic):

 

<section aria-label="Main content" role="main">
<span id="dateSpan"></span>&nbsp;&nbsp;
<span id="downloadingSpan"></span>
<section class="listsContainer">
<h2 class="birthsHeader">born today</h2>
<h2 class="deathsHeader">died today</h2>
<div id="birthsListElement" data-win-control="WinJS.UI.ListView"></div>
<div id="deathsListElement" data-win-control="WinJS.UI.ListView"></div>
</section>
</section>

So for our ViewModel we will need: A property for the date/time, a property for the “downloading” message, two lists for the births and deaths, two methods to fetch the deaths and births and the event handler that will be invoked when the user clicks on a list item. In the “homepage.js” file, the process for creating this ViewModel is as follows:

Within the self executing function that is generated for us, we define a new class (WinJS.Class.define) that we name HomePageViewModelClass:

 

var HomePageViewModelClass = WinJS.Class.define(
function () {
this.onShowActorDetailsBirths = this.showActorDetailsBirths.bind(this);
this.onShowActorDetailsDeaths = this.showActorDetailsDeaths.bind(this);
},
{
_today: "",
today: {
get: function () {
return this._today;
},
set: function (value) {
this._today = value;
this.notify("today", value);
}
},

_downloading: "",
downloading: (...),

birthsList: new WinJS.Binding.List(),
deathsList: new WinJS.Binding.List(),
getBirths: function () {
this.downloading = "(downloading...)";
var that = this;
var syn = new Windows.Web.Syndication.SyndicationClient();
var url = new Windows.Foundation.Uri("http://rss.imdb.com/daily/born/");
syn.retrieveFeedAsync(url).then(
function (feed) {
for (var i = 0, len = feed.items.length; i < len; i++) {
var item = feed.itemsIdea;
var birth = {
title: item.title.text,
date: item.publishedDate,
content: item.summary.text,
link: item.links[0].nodeValue
}
that.birthsList.push(birth);
}
that.downloading = "";
});
},
getDeaths: (...),
showActorDetailsBirths: function (e) {
this._showActorDetails(this.birthsList.getAt(e.detail.itemIndex));
},
showActorDetailsDeaths: function (e) {
this._showActorDetails(this.deathsList.getAt(e.detail.itemIndex));
},

_showActorDetails: function (item) {
WinJS.Navigation.navigate("/html/actorDetail.html", item);
}

}
);


There are a few things to note here:

 

  • For the properties “today” and “downloading” we use accessors. The accessors get and set their corresponding private fields. Note that after they set their value they call a “strange” (for now) method which is called notify with the name of the property that has changed and the new value. You will see in just a minute where this method is defined but for now remember that this is what makes the data-bound part of the UI update when this property changes (for .NET Developers: the equivalent of PropertyChanged).

 

  • The “showActorDetailsBirths” and “showActorDetailsDeaths” methods are the ones that will be called when the user presses on a list item and therefore will be bound to the “oniteminvoked” event. Due to the javascript’s issues with the “this” keyword”, on the class constructor, we create two new methods from those ones (prefixed with “on”) using the bind method, that have the correct context for “this”, that is the object itself, and those will be the ones that will be bound to the events.

 

  • The two lists are of “WinJS.Binding.List” type in order to provide the necessary events for the UI to be updated when items are added to them (for .NET Developers” the equivalent to ObservableCollection).

 

Now just after the aforementioned class definition, we create from it a new class definition as follows:

 

var BindableHomePageViewModelClass = WinJS.Class.mix(HomePageViewModelClass, WinJS.Binding.mixin);

This actually uses the “WinJS.Class.mix” to add to our HomePageViewModelClass class all methods and properties that are contained in the “WinJS.Binding.mixin” class and guess what, those are the methods needed for providing change notifications of the data-binding (this is the source of the notify method). This also means that we should never use the HomePageViewModelClass but instead always use the BindableHomePageViewModelClass since this is the “complete” one after the application of the mix method.

 

Now we make our ViewModel public in the “ProgwareOrg” namespace as follows creating a new object. This is the object that will be bound to the view:

 

WinJS.Namespace.define("ProgwareOrg", {
HomePageViewModel: new BindableHomePageViewModelClass()
});

In the page’s functions that handle the basic events of our page we can bind the ViewModel to the View as follows (for .NET Developers: this code should be considered as the xaml.cs file and the binding to the ViewModel that occurs there):

 

WinJS.UI.Pages.define("/html/homePage.html", {
init: function init(element, options) {

var calendar = new Windows.Globalization.Calendar();
ProgwareOrg.HomePageViewModel.today = calendar.dayOfWeekAsString() + " " +

calendar.day + " " + calendar.monthAsString() + " " + calendar.yearAsString();
ProgwareOrg.HomePageViewModel.getBirths();
ProgwareOrg.HomePageViewModel.getDeaths();
},
ready: function ready(element, options) {
WinJS.Binding.processAll(home, ProgwareOrg.HomePageViewModel);
}
});


We initialize our ViewModel and in the ready function we bind it to the view via the “WinJS.Binding.ProcessAll” method (for .NET Developers: this would be the this.DataContext=ViewModel in XAML). This method takes as first parameter the root element where the ViewModel is applied and in our case is the one with the id=”home”.

 

The binding in the view is as follows:

 

<div class="homepage" id="home">
<section aria-label="Main content" role="main">
<span id="dateSpan" data-win-bind="innerText:today"></span>&nbsp;&nbsp;
<span id="downloadingSpan" data-win-bind="innerText:downloading"></span>
<section class="listsContainer">
(...)
<div id="birthsListElement"
data-win-control="WinJS.UI.ListView"
data-win-options="{itemDataSource:ProgwareOrg.HomePageViewModel.birthsList.dataSource,
itemTemplate:select('#template'),
layout:{type:WinJS.UI.ListLayout},
oniteminvoked : ProgwareOrg.HomePageViewModel.onShowActorDetailsBirths}"
>
</div>
<div id="deathsListElement" (...)></div>
</section>
</section>
</div>

First note that the top-level element (the one used in processAll) is the id=”home” element. Also note the “data-win-bind”  attributes that provide the binding for the HTML elements.

 

For the two ListViews note the following:

 

The itemDataSource is bound to our ViewModel but it is using the full qualified name. This means that it does not take into consideration the processAll method. I do not know why this happens and I think that this is something that needs to be fixed but for now the hack I found is that for the controls of WinJS for binding with the ViewModel we need to use the full access paths of our ViewModel. This also applies to the binding of the oniteminvoked event handler. The template set is defined in the page just above this code and not displayed here for simplicity.

 

And this summarizes the way the ViewModel can be bound to our view. The ViewModel for the actorDetails page is simpler:

 

(function () {
"use strict";

var ActoDetailViewModelClass = WinJS.Class.define(
function () {},
{
_pageToShow: "",
pageToShow: {
get: function () {
return this._pageToShow;
},
set: function (value) {
this._pageToShow = value;
this.notify("pageToShow", value);
}
}
}
);

var BindableActoDetailViewModelClass = WinJS.Class.mix(ActoDetailViewModelClass, WinJS.Binding.mixin);

WinJS.Namespace.define("ProgwareOrg", {
ActoDetailViewModel: new BindableActoDetailViewModelClass()
});

WinJS.UI.Pages.define("/html/actorDetail.html", {
ready: function ready(element, options) {
ProgwareOrg.ActoDetailViewModel.pageToShow = options.link;
WinJS.Binding.processAll(mainSection, ProgwareOrg.ActoDetailViewModel);
},
updateLayout: function updateLayout(element, viewState) { }
});
})();

And the view:

 

<div id="mainSection" class="actorDetail fragment">
...
<section aria-label="Main content" role="main">
<iframe id="actorsPage" data-win-bind="src:pageToShow" style="width: 100%;height: 100%" />
</section>
</div>

Data are passed between pages as the second parameter of the WinJS.Navigation.navigate("/html/actorDetail.html", item);. They can be retrieved from the navigated page through the options parameter of the ready function.

The “actorDetail” page uses an iframe to display the webpage. Because the page has ActiveX controls when you run the project from within VS you will be getting the error “JavaScript runtime error: Access is denied”.If you look at the Javascript Console you will see that the error is “Cannot load the ActiveX plug-in that has (…)” and that “Apps can't load ActiveX controls”. But if you compile and deploy in “Release” go to Windows8 start page and run the app this will not appear. This does not mean that the error disappears. It just means that in release mode the ActiveX will just don’t load which is fine with us.

 

So in this post we have seen an approach on how to create ViewModels in a single navigation application in Windows8.

Posted: Παρασκευή, 23 Μαρτίου 2012 6:43 πμ από iwannis | 2 σχόλια
Δημοσίευση στην κατηγορία: , ,
(Windows8) WinJS Basic Javascript Objects and the ViewModel pattern

When someone starts implementing small programs in Visual Studio 2011 and starts playing with the VS2011 templates for Metro applications in Javascript, he soon wonders about the differences that WinJS brings in Javascript object creation and what is the best practice of connecting the view (HTML) with the model (Javascript). he second relates to the functionality that WinJS brings and is similar to other data binding libraries like for example Knockoutjs.In this blog we will see a general overview of the object and ViewModel creation patterns using only WinJS via the implementation of a simple “Metro Style” application.

 

Please note that due to the lack of complete documentation for the WinJS library and its workings, some of the statements below are my assumptions and may not reflect 100% the reality. Of course every single line of code below is tested and works but bear in mind that there may be a better way of doing this and I will definitely keep researching for it.

 

How do I try the examples in this post?

If you do not have Windows8 – VS 2011 installed go ahead and download the Consumer Preview from here, then follow the steps given either here for running Windows8 on a VM or here (amazing and totally recommended) for running Windows8 on a virtual partition but directly on your “metal”. Then you may install VS 2011 Express which comes along with Expression Blend from the files downloaded from here.

 

How do I define classes with properties and fields in WinJS?

Properties are object variables that have setters and getters. Fields are object variables that do not have getters and setter. It is also preferable to define classes within namespaces to avoid naming collisions with other variables of other libraries. Let us define within the namespace “Progware”, a class named “DemoClass” with two public fields “string1”, “string2”, a public property “number” and a private field “_number”. We will be using WinJS.Namespace.define for the namespace definition and WinJS.Class.define for the class definition. To implement this class with its namespace, within a file named “DemoClass.js” we define a self executing function as follows:

 

(function () {
"use strict";

WinJS.Namespace.define("Progware", {
DemoClass: WinJS.Class.define(
function (string1, string2,number) {


this.string1 = string1;
this.string2 = string2;
this.number = number;
}
, {

string1: "",
string2: undefined,


number: {
set: function (value) {
if (isNaN(value) || value < 0)
throw new Error("Invalid number");
this._number = value;
},
get: function () {
return this._number;
}
},
incrementCool: function () {
this._privateIncrementCool();
},
getCoolValue: function () {
return Progware.DemoClass.staticPropertyNumber;
},


_privateIncrementCool:function(){
Progware.DemoClass.staticPropertyNumber++;
}

},


{
staticPropertyNumber: 1
}),
namespaceNumberVariable: 55
});
})();

WinJS.Namespace.define requires two parameters: The namespace name (Progware) and a list of the namespace’s classes, methods and/or variables. In this namespace a “namespaceNumberVariable” variable is declared (ie a global variable that can be referenced from anywhere in your code using the namepsace’s prefix – “Progware.namespaceNumberVariable”) and also a class is declared using the WinJS.Class.define method.

 

The WinJS.Class.define method requires 3 parameters: a function to act as the constructor of the class, a list of instance members of the class (those are the normal properties, fields, methods of each object of this class ) and a list of static members which are the equivalent of the properties, fields and methods declared as static in .NET and can be referenced from everywhere with the prefix of the namespace and the name of the class (in this example “staticPropertyNumber” is a static field and can be referenced from everywhere in the application using the “Progware.DemoClass.staticPropertyNumber” name).

 

The constructor of the class initializes the properties and fields. Note here that just the process of initializing a property on the constructor adds it to the list of properties of the class without having to declare it later. Those are the merits (and fallacies) of the loosely typed world! It is generally though good practice to declare them also in the appropriate place (second parameter) for “code consistency”.  Also note the number property and the way its setter and getter is defined. They both use a “private” field “_number” that is implicitly defined just by its use.  Of course there is no such thing as privates in Javascript but in our case just prefixing a field with the underscore “_” makes the field not enumerable and it also hides it from VS2011 intellisense (this is the IDE actually enforces a missing feature of the language which is also pretty handy). The same applies for the private method “_privateIncrementCool”. Also note how the classe’s methods manipulate the static field “staticPropertyNumber” and the naming convention they use to reference it.

 

In general, in the code above you have a full blown namespace with its global variables and its classes (with public, private properties, fields and methods). This I dare to say is the equivalent .cs file we would write for a class in C# apart from the namespace variables which cannot exist in .NET.

 

How do I create a ViewModel following the MVVM pattern in WinJS?

Now suppose you want to obey the MVVM principle in WinJS. That is you want to create a JavaScript class that implements all the view’s  functionality and data and bind that class to the view (that is the html page that displays the data and reacts to user events). The requirements for our simple example are as follows: We need to create a “DemoClass” object and display its contents. When a user click a button named “Add Prefix” we want to prefix the current value of the “string1” property of the class with the letter “A”. Here is a snapshot of the result:

 

image

 

Create a new Javascript/Blank Application project in VS 2011. You get a blank application with some primitive structure. In the “js” folder create a “DemoClass.js” file with the code mentioned above. This file defines your “DemoClass” class. Now go to your “default.html” page and write your DIVs for displaying the data and also implement a BUTTON so that your view reflects the image above.

 

Where do I put the JavaScript code to display the “DemoClass” object data to the view?

The first answer is everywhere you like! You can use the “default.html” itself if you like. It is pure plain JavaScript and the HTML world! Of course you need to make sure that the DOM is ready using well-known techniques. There is though a better suited space in Metro style application you can put your code where you are sure that your DOM will be ready. In the “default.js” file created for you you have the “app.onactivated” event that will be called when your app initializes. At that place the initial if checks to see whether your application is activated from a “terminated” state, that is it was not running before (more of this in a future post). Within that you can be sure that the DOM is ready and the “DemoClass" has been initialized. You first approach to display the data can be something like the following:

 

var class1 = new Progware.DemoClass("STRING1", "STRING2", 14);
document.querySelector(".string1Direct").innerText = class1.string1;
document.querySelector(".string2Direct").innerText = class1.string2;
document.getElementsByClassName("numberDirect")[0].innerText = class1.number;

 

That is, you create your “class1” object and then you either use WinJS’s querySelector (a structure that resembles the way you would query for DOM elements using JQuery) or the more traditional getElementsByClassName or getElementById to assign to the appropriate DOM elements the “class1”’s values.

 

All is good with that but you need to know that with this approach you make your code aware of the view (it has to know the id and class names of the HTML Page) which violates the separation we are trying to achieve with the MVVM pattern. The second approach described below solves this issue and brings you closer to the ViewModel pattern.

 

You initialize again the “class1” object but this time your view has the “data-win-bind” properties pointing the properties of the “class1” object:

 

<div id="boundDiv" style="margin-bottom:10px">
string1: <span data-win-bind="innerText: string1" style="color:green"></span><br/>
string2: <span data-win-bind="innerText: string2" style="color:green"></span><br />
number: <span data-win-bind="innerText: number" style="color:green"></span><br />
</div>

Now in the same place as before (the app.onactivated event) we initialize our object and then call WinJS.Binding.processAllfor the binding:

 

var class1 = new Progware.DemoClass("STRING1", "STRING2", 14);
var boundDiv = document.getElementById("boundDiv");
WinJS.Binding.processAll(boundDiv, class1);

 

Note that we specify the root element for our binding. All children of this element “see” the same data to which they bind (in our case class1). All is great apart from the  fact that if we change in code any of the values of string1,string2 or number, this change will not be reflected to the UI. To be able to achieve this we need o make a little change to the class1 we bind as follows:

 

var class1VM = WinJS.Binding.as(class1);
WinJS.Binding.processAll(boundDiv2Way, class1VM);

Thus we use WinJS.Binding.as to get a new “observable” class named “class1VM” and the new class is the one used for the binding. Note that this new class is just a wrapper. Changes to the properties of this class will affect the properties of “class1”. The opposite does not hold.

 

The only thing left to do is provide the functionality for the button. Again a way to go is to bind something to the click event of the button element as follows:

 

document.getElementById("prefixString1NoBinding").onclick = function () {
class1VM.string1 = "A" + class1VM.string1;
}
 
This will both change the value of “class1.string1” and reflect the change in the UI. But again to use the ViewModel pattern we need a way to bind the “click event” of the button to a method in the ViewModel. Actually why won’t we implement a general ViewModel class that will has this method but it will also have the “class1” for the data:
 
(function () {
"use strict";
WinJS.Namespace.define("Progware", {
ViewModel: WinJS.Class.define(
function () {
this.class1 = new Progware.DemoClass("", "", 0);
this.onAddPrefix = this.addPrefix.bind(this);
},
{
class1: undefined,

addPrefix: function (eventObject) {
this.class1.string1 = "A" + this.class1.string1;
}
}, {}
)
});
})();

The HTML now will look like this:
 

<div id="boundDivVM" style="margin-bottom:10px">
string1: <span data-win-bind="innerText: class1.string1" style="color:green"></span><br/>
string2: <span data-win-bind="innerText: class1.string2" style="color:green"></span><br />
number: <span data-win-bind="innerText: class1.number" style="color:green"></span><br />
    <button id="prefixString1BindingVM" data-win-bind="onclick:onAddPrefix" /><br/>

</div>

 
We can actually now initialize the ViewModel in app.onactivated handler and bind it to the UI:
 
var defaultVM=new Progware.ViewModel();
defaultVM.class1 = WinJS.Binding.as(class1);
var javascriptViewModelDiv = document.getElementById("boundDivVM");
WinJS.Binding.processAll(javascriptViewModelDiv, defaultVM);

But wait a second!
 
Why the data-win-bind (onclick) refers to the onAddPrefix method and not on the addPrefix one? And what the hell is this.addPrefix.bind(this)?
 
Well, those of you who are in Javascript for some time you will know that having the handler directly reference the addPrefix method will make the “this” keyword used in it to refer to the whole window and not the object itself. The this.<function>.bind(this) sets the correct reference for the “this” keyword  order to be used from within the function and this is exactly what we need to do here (quite easy and actually really helpful). So instead of binding to “addPrefix” we bind to the method returned by “this.addPrefinx.bind(this)”.
 
This was a first approach to the ViewModel pattern in WinJS. If you need the source code please feel free to write me a message.
Posted: Κυριακή, 11 Μαρτίου 2012 4:03 μμ από iwannis | 1 σχόλια
Δημοσίευση στην κατηγορία: ,