In my previous posts on Binding in WinJS (see http://www.skimedic.com/blog/post/2012/11/27/MVVM-in-WinJS-Part-1-Observable-Models.aspx, http://www.skimedic.com/blog/post/2012/12/20/MVVM-in-WinJS-Part-2-e28093-Observable-Collections.aspx, and http://www.skimedic.com/blog/post/2013/02/22/MVVM-in-WinJS-Part-3-e28093-Binding-Initializers-and-Two-Way-Binding.aspx) we talked a lot about binding data for display in Windows 8 HTML apps.
Did you know that you can also bind to properties and attributes on markup, such as styles? Binding to styles can ensure that your application is consistent, and that your user can define aspects of you application according to their wishes. Of course, this doesn’t replace using CSS Style Sheets as the core for defining your application’s user interface, but does allow a series of customizations.
This is part one of a series:
To get started, let’s build a very simple app that will help to illustrate how the binding to styles works. The app is shown in Figure 1. By changing the toggle switches, the style for the fonts on the page will be updated to reflect the users wishes (as shown in Figure 2).
Figure 2 – Sample app with styles updated
To get started, create a new WinJS Navigation project in Visual Studio 2013 (File->New Project…->Templates->Other Languages->JavaScript->Windows Store->Navigation App, shown in Figure 3). I named mine ApplicationSettings because this sample will also be used for working with Application Settings and the Settings Charm. Although we aren’t going to creating multiple pages, the Navigation app contains all of the layout to be consistent with the Layout Guidelines from Microsoft, and is my goto App Template.
NOTE: You can use Visual Studio 2012 for this sample, as we won’t be using anything that is only in Windows 8.1, but I do all of my work in VS 2013 for the Windows 8.1 support.
Figure 3 – Creating a new WinJS Navigation App
Finally, change the style to the light style by changing the ui-dark.css reference to ui-light.css in both the default.html and home.html pages.
Open home.html, and paste in the following markup:
<div id="contentContainer"><div id="menuTarget" contenteditable="true">This is some sample text to demonstrate the RadRadialMenu</div><div>Another Test</div><textarea id="MyTextArea" rows="6" cols="100" width="600px"></textarea></div><div id="fontSettings" class="win-settings-section"><div id="boldSwitch"data-win-control="WinJS.UI.ToggleSwitch"data-win-options="{title:'Bold'}"></div><div id="italicSwitch"data-win-control="WinJS.UI.ToggleSwitch"data-win-options="{title:'Italic'}"></div><div id="underlineSwitch"data-win-control="WinJS.UI.ToggleSwitch"data-win-options="{title:'Underline'}"></div></div>
Listing 1 – Markup for Sample App
The two divs and the TextArea are just sample content items that we will use databinding to update the style. The ToggleSwitch controls will be used to change the properties on the model so we can change the properties on the model and see the changes in real time.
To fill the TextArea with some sample text, open up home.js and add the following line to the pages ready event:
Listing 2 – Adding Lorem ipsum to the TextArea HTML controlMyTextArea.value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Pellentesque volutpat sodales rhoncus. In a rhoncus risus. Maecenas neque ante,pharetra sollicitudin ante sit amet, venenatis consequat enim. Vivamus laciniahendrerit nibh, in consequat elit commodo vitae. Aliquam adipiscing rhoncus nisl,
non ultricies dolor lobortis eu. Nulla ac felis at mauris tristique tristique.Etiam eget tincidunt velit. Cras vitae augue non purus ultricies facilisis et in nulla.
Mauris adipiscing dui quis felis lacinia ornare. Nulla vitae congue odio, sedvestibulum nulla. Donec dignissim egestas metus, sit amet vestibulum nunc pretiumgravida. ";
There isn’t a solid reason to move this to the JavaScript file through programmatic assignment, except that it makes it easier to format for this post.
The model is very simple. Add a new file to the js directory and add a self executing function. In the function, set up some convenience variables to hold the style values (to help reduce the number of “magic strings” in our code). Add a NameSpace called Model, and use the WinJS.Binding.as() statement to hold the textSettings object. Remember that WinJS.Binding.as creates Observable models for us, so anytime the value changes, the UI will be notified and can act accordingly.
/// <reference path="//Microsoft.WinJS.1.0/js/ui.js" />
/// <reference path="//Microsoft.WinJS.1.0/js/base.js" />
(function () {
"use strict";
var normal = "normal";var none = "none";var bold = "bold";var italic = "italic";var underline = "underline";WinJS.Namespace.define("UISettings", WinJS.Binding.as({
textSettings: {textWeight: normal,textFontStyle: normal,textDecoration: none,},}));})();
Listing 2 – Initial Model for Text Settings
Add the uiSettings file to the home.html as a script tag.
Remember that the data-win-bind takes two arguments (three if using a binding initializer). The first is the property on the markup element or WinJS control, the second the property on the model that is to be used to set the value. For example, if we wanted to bind the textContent of a Span to the firstName property of the model, we would use the following code:
<span data-win-bind="textContent:firstName"></span>
Listing 3 – Sample binding statement
In the previous example, the binding statement bound to a single property on the root of the control and the model. If you need to bind deeper into the object (or element) graph, either side of the binding statement can be a path, or a “dotted” notation of the properties, starting at the root. For example, to change the color of the text, use the following code:
data-win-bind="style.color:textColor;">
Listing 4 – Binding the Style Color to the textColor property
If we apply this pattern to the font-weight property of the style, we will get an error through by WinJS – it doesn’t like special characters in the binding statements. Fortunately there is another syntax that we can use when the property names contain special characters to reserved words. Instead of prop1.prop2, we use prop1[‘prop2’]. To bind the Font Weight, Text Decoration, and Font Style properties, we use the following code in the Div named contentContainer:
Listing 5 – Binding the model to an HTML controldata-win-bind="style['font-weight']:textWeight;style['text-decoration']:textDecoration;style['font-style']:textFontStyle"
The full markup for the contentContainer Div is as follows:
<div id="contentContainer"data-win-bind="style.color:textColor;style['font-weight']:textWeight;style['text-decoration']:textDecoration;style['font-style']:textFontStyle">
<div id="menuTarget" contenteditable="true">This is some sample text to demonstrate the RadRadialMenu</div><div>Another Test</div><textarea id="MyTextArea" rows="6" cols="100" width="600px"data-win-bind="style.color:textColor;style['font-weight']:textWeight;style['text-decoration']:textDecoration;style['font-style']:textFontStyle"></textarea></div>
Listing 6 – Full HTML Markup
The final step that we need to do in this example is set the datacontext for the binding statements. Open up home.js and after the line setting the text for the TextArea, add the following line:
Listing 7 – Setting the DataContext for the DataBinding statementsWinJS.Binding.processAll(element, Model.textSettings);
And that’s it! Now you can change the properties in the model, run the app, and see the affects on the styles. For example, if you change the model to this:
Listing 8 – Setting the bold/italic/underline styles in the modeltextSettings: {textWeight: bold,textFontStyle: italic,textDecoration: underline,},
Running the app presents the following UI:
Figure 4 – Updated styles using Data Binding
While this works nice, it isn’t very different from hard coding the styles into the page. To wire up the ToggleSwitch controls to the model takes just a few lines of JavaScript in a few convenience methods on the model, and setting the click event on the toggle controls.
Start by opening uiSettings.js and adding a new NameSpace within the self executing function. We want the new NameSpace in the same function so we can take advantage of the variables we already defined.
Listing 9 – Namespace for Toggle FunctionsWinJS.Namespace.define("UISettings", {
});
Create three functions, one for each style property, marking them as supported for processing. This allows them to be used in markup in the data-win-options attribute. Each event will get the checked value from the Toggle control and adjust the model accordingly.
Listing 10 – Functions for changing the modelsetTextIsBold: WinJS.Utilities.markSupportedForProcessing(function (event) {
if (event.currentTarget.winControl.checked) {
Model.textSettings.textWeight = bold;}else {
Model.textSettings.textWeight = normal;}}),setTextIsUnderlined: WinJS.Utilities.markSupportedForProcessing(function (event) {
if (event.currentTarget.winControl.checked) {
Model.textSettings.textDecoration = underline;}else {
Model.textSettings.textDecoration = normal;}}),setTextIsItalic: WinJS.Utilities.markSupportedForProcessing(function (event) {
if (event.currentTarget.winControl.checked) {
Model.textSettings.textFontStyle = italic;}else {
Model.textSettings.textFontStyle = none;}}),
Finally, open hone.html and connect the onclick events for the WinJS controls to our new functions, as shown in Listing 11.
<div id="fontSettings" class="win-settings-section">
<div id="boldSwitch"
data-win-control="WinJS.UI.ToggleSwitch"
data-win-options="{onclick:UISettings.setTextIsBold,title:'Bold'}"></div>
<div id="italicSwitch"
data-win-control="WinJS.UI.ToggleSwitch"
data-win-options="{onclick:UISettings.setTextIsItalic,title:'Italic'}"></div>
<div id="underlineSwitch"
data-win-control="WinJS.UI.ToggleSwitch"
data-win-options="{onclick:UISettings.setTextIsUnderlined,title:'Underline'}"></div>
</div>
Listing 11 – Connecting the event handlers to the controls
Run the app, and when you toggle the controls from off to on, the text on the page within the contentContainer div as well as the text in the TextArea are updated to Bold, Italic, and Underlined (as in Figure 2 above). There’s only one problem – when you toggle the Underline and Italic toggle switches, nothing changes. The Bold works, but the others remain the same. This is an issue with changing CSS through JavaScript. My next post will show how to handle the issue with Application level events.
Data binding works with not only traditional models and display elements, but also styles and any other property on HTML or WinJS controls.
[Cross posted from Skimedic.com]Philip Japikse is an international speaker, a Microsoft MVP, ASPInsider, INETA Community Champion, MCSD, CSM/ CSP, and a passionate member of the developer community. Phil has been working with .Net since the first betas, developing software for over 20 years, and heavily involved in the agile community since 2005. Phil also hosts the Hallway Conversations podcast (www.hallwayconversations.com) and serves as the Lead Director for the Cincinnati .Net User’s Group (http://www.cinnug.org). You can follow Phil on twitter via www.twitter.com/skimedic, or read his personal blog at www.skimedic.com/blog.