0% found this document useful (0 votes)
183 views108 pages

Magazine: The Microsoft Journal For Developers

Uploaded by

openid_JFMYmfqs
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
183 views108 pages

Magazine: The Microsoft Journal For Developers

Uploaded by

openid_JFMYmfqs
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 108

THE MICROSOFT JOURNAL FOR DEVELOPERS MARCH 2014 VOL 29 NO 3

magazine

Asynchronous
Programming................18, 26

0314msdn_CoverTip.indd 1 2/20/14 12:12 PM


0314msdn_CoverTip.indd 2 2/6/14 10:52 AM
THE MICROSOFT JOURNAL FOR DEVELOPERS MARCH 2014 VOL 29 NO 3

magazine

Asynchronous
Programming................18, 26

Patterns for Asynchronous COLUMNS


MVVM Applications: Data Binding CUTTING EDGE
Stephen Cleary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 A First Look at ASP.NET Identity
Dino Esposito, page 6
Asynchronous TCP Sockets WINDOWS AZURE INSIDER
as an Alternative to WCF The Windows Azure Service Bus
James McCaffrey . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 and the Internet of Things, Part 2
Bruno Terkaly and

A .NET Developer Primer for Ricardo Villalobos, page 12

Single-Page Applications THE WORKING


PROGRAMMER
Long Le . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Getting Started with Oak:
Data Validation and
Building a Netduino-Based Wrapping Up
HID Sensor for WinRT Ted Neward, page 62

Donn Morse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 MODERN APPS


A Look at the Hub Project and
Control in Windows Store Apps
Rachel Appel, page 66

DIRECTX FACTOR
Triangles and Tessellation
Charles Petzold, page 74

DON’T GET ME STARTED


The Peasants Are Revolting!
David Platt, page 80
Untitled-11 2 2/5/14 4:05 PM
Untitled-11 3 2/5/14 4:05 PM
®

MARCH 2014 VOLUME 29 NUMBER 3 magazine


Instantly Search MOHAMMAD AL-SABT Editorial Director/[email protected]

Terabytes of Text
KENT SHARKEY Site Manager

MICHAEL DESMOND Editor in Chief/[email protected]


DAVID RAMEL Technical Editor
25+ fielded and full-text search types SHARON TERDEMAN Features Editor
WENDY HERNANDEZ Group Managing Editor

dtSearch’s own document filters SCOTT SHULTZ Creative Director

support “Office,” PDF, HTML, XML, ZIP, JOSHUA GOULD Art Director

emails (with nested attachments), and


SENIOR CONTRIBUTING EDITOR Dr. James McCaffrey
CONTRIBUTING EDITORS Rachel Appel, Dino Esposito, Kenny Kerr,

many other file types


Julie Lerman, Ted Neward, Charles Petzold, David S. Platt,
Bruno Terkaly, Ricardo Villalobos

Supports databases as well as static


and dynamic websites Henry Allain President, Redmond Media Group
Michele Imgrund Sr. Director of Marketing & Audience Engagement
Highlights hits in all of the above Tracy Cook Director of Online Marketing

.
Irene Fincher Audience Development Manager

APIs for NET, Java, C++, SQL, etc. ADVERTISING SALES: 818-674-3416/[email protected]
Dan LaBianca Vice President, Group Publisher

64-bit and 32-bit; Win and Linux


Chris Kourtoglou Regional Sales Manager
Danna Vedder Regional Sales Manager/Microsoft Account Manager
David Seymour Director, Print & Online Production
Serena Barnes Production Coordinator/[email protected]

"lightning fast" Redmond Magazine


Neal Vitale President & Chief Executive Officer
Richard Vitale Senior Vice President & Chief Financial Officer
"covers all data sources" eWeek Michael J. Valenti Executive Vice President
Christopher M. Coates Vice President, Finance & Administration
Erik A. Lindgren Vice President, Information Technology & Application Development
"results in less than a second" David F. Myers Vice President, Event Operations

InfoWorld
Jeffrey S. Klein Chairman of the Board

MSDN Magazine (ISSN 1528-4859) is published monthly by 1105 Media, Inc., 9201 Oakdale Avenue,
Ste. 101, Chatsworth, CA 91311. Periodicals postage paid at Chatsworth, CA 91311-9998, and at
additional mailing offices. Annual subscription rates payable in US funds are: U.S. $35.00, International

hundreds more reviews and developer


$60.00. Annual digital subscription rates payable in U.S. funds are: U.S. $25.00, International $25.00.
Single copies/back issues: U.S. $10, all others $12. Send orders with payment to: MSDN Magazine,
P.O. Box 3167, Carol Stream, IL 60132, email [email protected] or call (847) 763-9560.

case studies at www.dtsearch.com


POSTMASTER: Send address changes to MSDN Magazine, P.O. Box 2166, Skokie, IL 60076. Canada
Publications Mail Agreement No: 40612608. Return Undeliverable Canadian Addresses to Circulation
Dept. or XPO Returns: P.O. Box 201, Richmond Hill, ON L4B 4R5, Canada.

Printed in the U.S.A. Reproductions in whole or part prohibited except by written permission. Mail
requests to “Permissions Editor,” c/o MSDN Magazine, 4 Venture, Suite 150, Irvine, CA 92618.

Legal Disclaimer: The information in this magazine has not undergone any formal testing by 1105 Media,
dtSearch products: Inc. and is distributed without any warranty expressed or implied. Implementation or use of any information
contained herein is the reader’s sole responsibility. While the information has been reviewed for accuracy,

Desktop with Spider Web with Spider


there is no guarantee that the same or similar results may be achieved in all environments. Technical
inaccuracies may result from printing errors and/or new developments in the industry.

Network with Spider .


Engine for Win & NET
Corporate Address: 1105 Media,Inc.,9201 Oakdale Ave.,Ste 101,Chatsworth,CA 91311,www.1105media.com

Media Kits: Direct your Media Kit requests to Matt Morollo, VP Publishing, 508-532-1418 (phone),

Publish (portable media) Engine for Linux


508-875-6622 (fax), [email protected]

Reprints: For single article reprints (in minimum quantities of 250-500), e-prints, plaques and posters contact:
Document filters also available for separate PARS International, Phone: 212-221-9595, E-mail: [email protected], www.magreprints.com/
QuickQuote.asp
licensing List Rental: This publication’s subscriber list, as well as other lists from 1105 Media, Inc., is available
for rental. For more information, please contact our list manager, Jane Long, Merit Direct. Phone: 913-
685-1301; E-mail: [email protected]; Web: www.meritdirect.com/1105

All customer service inquiries should be sent to [email protected] or call 847-763-9560.

Ask about fully-functional evaluations


The Smart Choice for Text Retrieval ® since 1991
www.dtSearch.com 1-800-IT-FINDS
Printed in the USA
Untitled-5 1 1/13/14 12:22 PM
EDITOR’S NOTE MICHAEL DESMOND

Everything I Need to Know


I Learned from Calvin and Hobbes
Don’t Get Me Started columnist David Platt this month dives Value Extensibility Then there was the Transmogrifier, which
into the revolt at Avon over the company’s attempt to deploy an could turn anyone into one of four target animals: eel, baboon, giant
SAP-based order entry and customer management system. Our bug or dinosaur. Calvin showed great awareness allowing support
back-page columnist takes his cues from many a muse, be they Nobel- for additional targets, including an extensible UI to handle them.
winning physicists or cartoon characters from the funny pages. And The Transmogrifier would later support worms, elephants, tigers
in that last regard, he and I share common inspiration. and giant slugs. I wonder if he used XML?
When Bill Watterson’s brilliant Calvin and Hobbes comic strip Leverage the platform Both the Duplicator and Transmogrifier—
exploded onto newspaper pages in 1985, it was an unexpected as well as later Calvin inventions the Cerebral Enhance-O-Tron and
well spring of insight and wisdom. As a parent, I’ve marveled at the Time Machine—were built on a common, corrugated cardboard
Watterson’s ability to capture the simple genius of a boy at play. And box platform and permanent marker UI. Simple geometries, famil-
as editor in chief of MSDN Magazine, I’ve found that Watterson’s iar materials and streamlined interfaces defined all four inventions.
incorrigible 6-year-old, Calvin, and his loyal tiger, Hobbes, offer Don’t Skimp on Security When Calvin
real lessons for working developers. Here are just a few. and Hobbes created their exclusive club,
Test, Test, Test! The Duplicator story arc is one of my favorites in “Get Rid Of Slimy girlS (G.R.O.S.S),” they
the 10-year run of the comic, but it’s a cautionary tale for developers. secured entry to the club treehouse with a
Calvin invented a box that creates copies of himself, who he hoped long, multi-verse password about tigers,
would do all his chores and school work. But Calvin never tested which ended with the line “Tigers are
his Duplicator, and he quickly faced a squad of ill-behaved dupes. If great! They’re the toast of the town. Life’s
Calvin had designed a test to determine the actual behavior of the always better when a tiger’s around!” That
dupes his invention created, he might have saved himself a lot of work. final stanza alone is a 308-bit password,
Remediate: Calvin later developed and I haven’t even described the dancing component. But Calvin
an add-on for his Duplicator, called the struggled to remember the verse, illuminating the deep challenge
Ethicator, which let the operator set each of balancing usability and security.
dupe’s personality to either Good or Evil. Mind the org chart: G.R.O.S.S. offered a final, valuable lesson—the
A simple patch saved what would other- danger posed by vague, shifting or tangled lines of authority. Calvin
wise have been a costly project failure, as may have been “Dictator for Life” of G.R.O.S.S., but that didn’t stop
Calvin created a compliant, good-aligned “First Tiger” Hobbes from trying to usurp his authority. Constant
dupe to do his chores. management reorgs created a volatile environment that produced
Fail Gracefully: Alas, the good Calvin hijacked meetings, failed initiatives and constant, internecine bicker-
dupe tried to befriend Calvin’s nemesis Susie Derkins. “I don't mind ing. G.R.O.S.S. never did mount a successful attack on Susie Derkins.
if he cleans my room and gets me good grades,” Calvin griped, “but Make Space for Creativity If Watterson’s protagonists have
when he starts talking to girls that’s going too darn far.” The unpre- one message for developers, it’s this: Dare to dream. Some of
dicted behavior led to an angry confrontation between Calvin and Calvin’s greatest insights occur while careening through the woods
his dupe, who suddenly cried “Oops! I’ve had an evil thought!” and in a toboggan or wagon. Take risks. Make mistakes. And, remem-
vanished in a puff of smoke. An exception-handling routine could ber, life’s always better when a
have preserved the investment in the duplicate Calvin. tiger’s around.
Visit us at msdn.microsoft.com/magazine. Questions, comments or suggestions for MSDN Magazine? Send them to the editor: [email protected].

© 2014 Microsoft Corporation. All rights reserved.


Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, you are not permitted to reproduce, store, or introduce into a retrieval system MSDN Magazine or any part of MSDN
Magazine. If you have purchased or have otherwise properly acquired a copy of MSDN Magazine in paper format, you are permitted to physically transfer this paper copy in unmodified form. Otherwise, you are not permitted to transmit
copies of MSDN Magazine (or any part of MSDN Magazine) in any form or by any means without the express written permission of Microsoft Corporation.
A listing of Microsoft Corporation trademarks can be found at microsoft.com/library/toolbar/3.0/trademarks/en-us.mspx. Other trademarks or trade names mentioned herein are the property of their respective owners.
MSDN Magazine is published by 1105 Media, Inc. 1105 Media, Inc. is an independent company not affiliated with Microsoft Corporation. Microsoft Corporation is solely responsible for the editorial contents of this magazine. The
recommendations and technical guidelines in MSDN Magazine are based on specific environments and configurations. These recommendations or guidelines may not apply to dissimilar configurations. Microsoft Corporation does not make
any representation or warranty, express or implied, with respect to any code or other information herein and disclaims any liability whatsoever for any use of such code or other information. MSDN Magazine, MSDN, and Microsoft logos are
used by 1105 Media, Inc. under license from owner.

4 msdn magazine
Untitled-2 1 4/30/13 10:48 AM
CUTTING EDGE DINO ESPOSITO

A First Look at ASP.NET Identity

Offspring of the “One ASP.NET” approach to Web development that Figure 1 Foundation of a Controller Based on ASP.NET Identity
came with Visual Studio 2013, the new ASP.NET Identity system is the public class AccountController : Controller
preferred way to handle user authentication in ASP.NET applications, {
public UserManager<ApplicationUser> UserManager { get; private set; }
whether based on Web Forms or MVC. In this column, I’ll review
the basics of ASP.NET authentication and explore the new ASP.NET public AccountController(UserManager<ApplicationUser> manager)
{
Identity system from the perspective of ASP.NET MVC 5 developers. UserManager = manager;
ASP.NET has long supported two basic types of authentication: }
Windows authentication and forms authentication. Windows public AccountController() :
authentication is seldom practical for public Web sites because this(new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext())))
it’s based on Windows accounts and access control list (ACL) {
tokens. Thus, it requires users to have a Windows account in the }
application’s domain, and it also assumes clients are connecting ...
from Windows-equipped machines. The other option is forms }
authentication, a widely adopted approach. Forms authentication
is based on a simple idea. For each access to a protected resource, introducing right into the framework a provider-based architecture
the application ensures the request includes a valid authentica- and the membership provider. Instead of reinventing the wheel
tion cookie. If a valid cookie is found, then the request is served as every time, you could just derive membership from the built-in
usual; otherwise, the user is redirected to a login page and asked system and override only the functions you intended to change.
to provide credentials. If these credentials are recognized as valid, The ASP.NET native membership provider is a standalone
then the application issues an authentication cookie with a given component that exposes a contracted interface. The ASP.NET
expiration policy. It’s simple and it just works. runtime, which orchestrates the authentication process, is aware
of the membership interface and can invoke whatever component

The simple membership API has


is configured as the membership provider of the application.
ASP.NET came with a default membership provider based on a

become quite a popular way of


new given database schema. However, you could easily write your
own membership provider to basically target a different database—

managing authentication.
typically, an existing database of users.
Does that sound like a great chunk of architecture? In the begin-
ning, nearly everybody thought so. In the long run, though, quite
Implementation of any forms authentication module can’t happen a few people who repeatedly tried to build a custom membership
without a distinct module that takes care of collecting user creden- provider started complaining about the verbosity of the interface.
tials and checking them against a database of known users. Writing Actually, the membership provider comes in the form of an inher-
this membership subsystem has been one of the key responsibilities itable base class, MembershipProvider, which includes more than
of development teams—but also one of the most annoying things 30 members marked as abstract. This means that for any new mem-
ever. Writing a membership system is not hard, per se. It mostly bership provider you wanted to create, there were at least 30 mem-
requires running a query against some sort of storage system and bers to override. Worse yet, you didn’t really need many of them
checking a user name and password. This code is boilerplate and most of the time. A simpler membership architecture was needed.
can grow fairly big as you add new authentication features such as
changing and recovering passwords, handling a changing number Introducing the Simple Membership Provider
of online users and so on. In addition, it has to be rewritten nearly To save you from the burden of creating a custom membership layer
from scratch if you change the structure of the storage or add more completely from scratch, Microsoft introduced with Visual Studio
information to the object that describes the user. Back in 2005, with 2010 SP1 another option: the simple membership API. Originally
the release of ASP.NET 2.0, Microsoft addressed this problem by available in WebMatrix and Web Pages, the simple membership

6 msdn magazine
Untitled-2 1 2/6/14 10:45 AM
API has become quite a popular way of managing authentication, than not, the main reason for having a custom membership system
especially in ASP.NET MVC. In particular, the Internet application was to circumvent structural differences between the required
template in ASP.NET MVC 4 uses the simple membership API to database format and the format of the existing database of user
support user management and authentication. credentials, which might have been in use for years.
Looking under the hood of the API, it turns out that it’s just a Clearly, this wasn’t a situation that could last forever. The commu-
wrapper on top of the classic ASP.NET membership API and its nity of developers demanded with loud voices a unified system for
SQL Server-based data stores. Simple membership lets you work membership that’s simple to use, narrowly focused and usable in the
with any data store you have and requires only that you indicate same way from within any flavor of ASP.NET. This idea weds together
which columns in the table serve as the user name and user ID. well with the One ASP.NET approach pushed by Visual Studio 2013.

One Identity Framework


The Internet application The purpose of authentication is getting the identity associated with

template in ASP.NET MVC 4 uses


the current user. The identity is retrieved and the provided credentials
are compared to records stored in a database. Subsequently, an iden-

the simple membership API to


tity system such as ASP.NET Identity is based on two primary blocks:
the authentication manager and the store manager. In the ASP.NET

support user management


Identity framework, the authentication manager takes the form of
the UserManager<TUser> class. This class basically provides a façade

and authentication.
for signing users in and out. The store manager is an instance of the
UserStore<TUser> class. Figure 1 shows the skeleton of an ASP.NET
MVC account controller class that’s based on ASP.NET Identity.
The major difference from the classic membership API is a The controller holds a reference to the authentication identity
significantly shorter list of parameters for any methods. In addi- manager, UserManager. This instance of UserManager is injected
tion, you get a lot more freedom as far as the schema of the mem- into the controller. You can use either an Inversion of Control (IoC)
bership storage is concerned. As an example of the simplified API, framework or the poor man’s alternative, the dependency injection
consider what it takes to create a new user: (DI) pattern, which uses two controllers, one of which gets a
WebSecurity.CreateUserAndAccount(username, password, default value (see Figure 1).
new { FirstName = fname, LastName = lname, Email = email });
The identity store, in turn, is injected into the authentication
You do most of the membership chores via the WebSecurity class.
identity manager, where it’s used to verify credentials. The identity
In ASP.NET MVC 4, however, the WebSecurity class expects to work
store takes the form of the UserStore<TUser> class. This class
with an extended membership provider, not a classic membership pro-
results from the composition of multiple types:
vider. The additional capabilities in an extended membership provider public class UserStore<TUser> :
are related to dealing with OAuth accounts. As a result, in ASP.NET IUserLoginStore<TUser>,
IUserClaimStore<TUser>,
MVC 4, you have two parallel routes for membership implemen- IUserRoleStore<TUser>,
tation: classic membership API using the MembershipProvider IUserPasswordStore<TUser>,
IUserSecurityStampStore<TUser>,
class and simple membership API using the ExtendedMember- IUserStore<TUser>,
shipProvider class. The two APIs are incompatible. IDisposable where TUser : IdentityUser
{
Before the arrival of Visual Studio 2013 and ASP.NET MVC 5, }
ASP.NET already offered quite a few ways to handle user authen- All interfaces implemented by UserStore<TUser> are basic
tication. With forms authentication, you could rely on classic repositories for optional user-related data such as passwords, roles,
membership, the simple membership API as defined in Web Pages claims and, of course, user data. The identity store needs to know
and a variety of custom membership systems. Consider the com- about the actual data source, though. As shown in Figure 1, the data
mon position among ASP.NET experts was that complex real-world source is injected in the UserStore class through the constructor.
applications require their own membership provider. More often Storage of users’ data is managed through the Entity Framework
Code First approach. This means you don’t strictly need to create a
Figure 2 Definition of the Default User Class in ASP.NET Identity
physical database to store your users’ credentials; you can, instead,
namespace Microsoft.AspNet.Identity.EntityFramework define a User class and have the underlying framework create the
{
public class IdentityUser : IUser most appropriate database to store such records.
{ The ApplicationDbContext class wraps up the Entity Framework
public string Id { get; }
public string UserName { get; set; } context to save users’ data. Here’s a possible definition for the
public string PasswordHash { get; set; } ApplicationDbContext class:
public string SecurityStamp { get; set; }
public ICollection<IdentityUserRole> Roles { get; private set; } public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
public ICollection<IdentityUserClaim> Claims { get; private set; } {
public ICollection<IdentityUserLogin> Logins { get; private set; } }
} Basically, the database context of ASP.NET Identity handles the
}
persistence of a given user type. The user type must implement the
8 msdn magazine Cutting Edge
Figure 3 Finalizing the Authentication Process The method SignInAsync checks the specified user name and
through an External Endpoint password against the store associated with the authentication
public async Task<ActionResult> ExternalLoginCallback(
manager. To register a user and add the user to the membership
string loginProvider, string returnUrl) database, you use code like this:
{ var user = new ApplicationUser() { UserName = model.UserName };
ClaimsIdentity id = await UserManager var result = await UserManager.CreateAsync(user, model.Password);
.Authentication if (result.Succeeded)
.GetExternalIdentityAsync(AuthenticationManager); {
await SignInAsync(user, isPersistent: false);
var result = await UserManager return RedirectToAction("Index", "Home");
.Authentication }
.SignInExternalIdentityAsync(
AuthenticationManager, id); All in all, ASP.NET Identity provides a unified API for tasks
if (result.Success) related to authentication. For example, it unifies the code required
return RedirectToLocal(returnUrl);
else if (User.Identity.IsAuthenticated) to authenticate against a proprietary database or a social network
{
result = await UserManager
OAuth-based endpoint. Figure 3 shows a fragment of the code
.Authentication you need to authenticate users against an external login engine.
.LinkExternalIdentityAsync(
id, User.Identity.GetUserId());
The code in Figure 3 gets called once the OAuth authentication
if (result.Success) (for example, against Facebook) has been completed successfully.
return RedirectToLocal(returnUrl);
else

}
}
return View("ExternalLoginFailure");
ASP.NET Identity is bound to
Visual Studio 2013, but it’s also
IUser interface or just inherit from IdentityUser. Figure 2 presents
the source code of the default IdentityUser class. expected to have an autonomous
Here’s an example of a realistic custom user class you might want
to use in your applications:
public class ApplicationUser : IdentityUser
life of its own when it comes to
{

}
public DateTime Birthdate { get; set; } future builds and releases.
The use of Entity Framework Code First is a great move here as it
makes the structure of the database a secondary point. You still need The Bottom Line
one, but to create it, you can use code based on classes. In addition, As I see things, ASP.NET Identity is an overdue solution that should
you can use Entity Framework Code First migration tools to modify have come years ago. The key issue concerning ASP.NET Identity
a previously created database as you make changes to the class behind right now is the development team is trying to come up with a
it. (For more information on this, see the “Code First Migrations” programming interface that’s generic and testable enough to last
article in the MSDN Data Developer Center at bit.ly/Th92qf.) for a long time—or at least until something newer and better shows
up in the industry.
Authenticating Users For the foreseeable future, ASP.NET Identity promises to be
ASP.NET Identity is based on the newest Open Web Interface for as good as old-fashioned membership was perceived to be a
.NET (OWIN) authentication middleware. This means the typical decade ago. Personally, I like the expressiveness of the API and the
steps of authentication (for example, creating and checking cookies) attempt to fuse together different forms of authentication—built-in
can be carried out through the abstract OWIN interfaces and not and OAuth-based, for example. Another great plus is the integra-
directly via ASP.NET/IIS interfaces. Support for OWIN requires tion with OWIN, which makes it somewhat independent from a
the account controller to have another handy property, like this: specific runtime such as IIS/ASP.NET.
private IAuthenticationManager AuthenticationManager ASP.NET Identity is bound to Visual Studio 2013, but it’s also
{ expected to have an autonomous life of its own when it comes to
get {
return HttpContext.GetOwinContext().Authentication; future builds and releases. I’ve just scratched the surface of the new
} identity API here. Stay tuned for newer builds and releases! Q
}
The IAuthenticationManager interface is defined in the
Microsoft.Owin.Security namespace. This property is import-
ant because it needs to be injected into any operation that involves DINO ESPOSITO is the author of “Architecting Mobile Solutions for the Enterprise”
(Microsoft Press, 2012) and the upcoming “Programming ASP.NET MVC 5”
authentication-related steps. Here’s a typical login method: (Microsoft Press). A technical evangelist for the .NET and Android platforms at Jet-
private async Task SignInAsync(ApplicationUser user, bool isPersistent) Brains and frequent speaker at industry events worldwide, Esposito shares his vision
{
var identity = await UserManager.CreateIdentityAsync(user, of software at software2cents.wordpress.com and on Twitter at twitter.com/despos.
DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() {
IsPersistent = isPersistent }, identity); THANKS to the following technical expert for reviewing this article:
} Pranav Rastogi (Microsoft)
msdnmagazine.com March 2014 9
Untitled-12 2 1/27/14 3:07 PM
Untitled-12 3 1/27/14 3:04 PM
BRUNO TERKALY AND
WINDOWS AZURE INSIDER RICARDO VILLALOBOS

The Windows Azure Service Bus and the


Internet of Things, Part 2
In our last column (msdn.microsoft.com/magazine/dn574801), we discussed In our proposed architecture, devices “talk” to a .NET application
the current technology landscape for machine-to-machine (M2M) running on Windows Azure Cloud Services, which acts as a gate-
computing, which refers to technologies that interconnect devices, way to the Service Bus in order to simplify the communication
usually for industrial instrumentation, in the form of sensors or meters. process with its assigned queue. This approach fully enables
The proliferation of affordable and easy-to-program tiny computers any of the four IoT communication patterns described in our
has expanded this concept into what’s called the Internet-of-Things previous column: Telemetry, Inquiry, Command and Notification.
(IoT), opening the door to scenarios where even ordinary home Here, we’ll implement a scenario in which a mobile device sends
appliances can be controlled or used as sources of information to a command to another device in order to execute an action—in
generate events. This way, it isn’t difficult to send alerts when it’s time this case, turn an LED on or off. One of the benefits of this solu-
to replenish the fridge, automatically close the window blinds as tion is that if the device is temporarily offline, it can pick up the
night falls or set the thermostat based on the family habits. commands whenever it reconnects to the Internet. You can also
We also made the case for using the Windows Azure Service Bus for set up an expiration time in a message to avoid the execution of a
device connectivity, as an alternative to using a VPN, when trying to task at an inconvenient moment or schedule messages to be sent
solve the addressability, security, and performance concerns associated at a specific time in the future.
with deploying a large number of sensors or meters. This is becoming For this example, we’ll use the well-known, well-documented
increasingly relevant considering that, according to the latest BI Intel- Arduino device, as described in our previous column. For the
ligence report from Business Insider, there will be more than 9 billion mobile client portion of the proof-of-concept, we’ll create a
connections directly related to the IoT by the year 2018 (read.bi/18L5cg8). Windows Phone application.
Using a designated Service Bus queue or topic for a device Here’s our simple scenario:
provides an elegant way to incorporate resiliency and occasional 1. When the Arduino device is started, it sends an identifica-
connectivity for IoT applications. In this article, we’ll walk through tion signal to the gateway application running on Windows
a hands-on Windows Azure implementation that illustrates these Azure Cloud Services. The gateway creates a Service Bus
concepts, designing a Service Bus blueprint with device queues,
deploying a listening worker role in Cloud Services, and program-
ming an Arduino device that executes commands sent remotely
by mobile clients, as shown in Figure 1.
If you look at the diagram, the Windows Azure Service Bus Mobile and Desktop Devices
component becomes the centerpiece of the design, providing the
authentication, message distribution and scalability to support
the multiple devices that will be sending data or receiving remote
REST Interface/SDKs
commands. The Service Bus is available in all Microsoft datacenters
that offer Windows Azure services, and it’s backed up by a highly Windows Azure Service Bus
redundant storage infrastructure. Also, like all other Windows
Azure components, it offers an open and easy-to-understand REST
interface, along with multiple SDKs (Microsoft .NET Framework,
TCP Connection
Java, PHP, Ruby, among others) built on top of it.

BUILD A FREE DEV/TEST SANDBOX IN THE CLOUD TCP Connection


Arduino
MSDN subscribers can quickly spin up a dev/test environment on
Windows Azure at no cost. Get up to $150 in credits each month! Windows Azure TCP Connection
aka.ms/msdnmag Cloud Service

Code download available at msdn.microsoft.com/magazine/msdnmag0314. Figure 1 An Internet-of-Things Architecture Using the


Windows Azure Service Bus
12 msdn magazine
queue for the device in case it doesn’t exist, and establishes If you inspect the ServiceConfiguration.cscfg files (for both cloud
a TCP connection, ready to send commands. and local deployment) for the MSDNArduinoListener project,
2. A Windows Phone application sends a command to the you’ll see a setting that stores the connection string for the Service
Windows Azure Service Bus queue assigned to the device. Bus. Replace its value with the one obtained in Step 1. The rest is
3. The message remains in the queue until the gateway appli- already configured for the solution to work, including the defini-
cation picks it up and sends the command to the Arduino tion of port 10100 for receiving connections from the devices. Next,
device via the established TCP connection. open the WorkerRole.cs file in the ArduinoListener project, where
4. The Arduino device turns the LED on or off based on the main code is located.
the command. There are four main sections to analyze.
Let’s look at the steps to make this happen, one by one. First, a TcpListener is created, and connections from devices
Step 1: Create the Windows Azure Service Bus Namespace are accepted:
Using your Windows Azure credentials (you can request a trial var deviceServer = new TcpListener(deviceEP);
deviceServer.Start(10);
account at bit.ly/1atsgSa), log in to the Web portal and click on the try
SERVICE BUS section (see Figure 2). Select the CREATE option, {
do
and enter a name for your namespace. Then, click on CONNEC- {
TION INFORMATION and copy the text in the Connection String TcpClient connection = await deviceServer.AcceptTcpClientAsync();
if (connection != null)
box, which you’ll need later. {
Step 2: Create the Gateway Application and Deploy to Windows ...

Azure Cloud Services Code for the gateway application, which Once a connection with the device has been established, a Network-
retrieves messages from the Service Bus queue and relays the commands Stream is defined and set to listening mode. The readBuffer variable
to the Arduino device, is included with the code download (available will contain the identifier value sent by each Arduino device:
NetworkStream deviceConnectionStream = connection.GetStream();
at msdn.microsoft.com/magazine/msdnmag0314). It’s based on the work of var readBuffer = new byte[64];
Clemens Vaster, who kindly contributed his guidance and expertise if (await deviceConnectionStream.ReadAsync(readBuffer, 0, 4) == 4)
{
to this article. His original project can be found at bit.ly/L0uK0v. int deviceId = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(readBuffer, 0));
Before we dive into this code, be sure you have Visual Studio ...
2013 installed, along with version 2.2 of the Windows Azure SDK Next, a queue is created based on the deviceId value (in case it
for .NET (bit.ly/JYXx5n). The solution includes three different projects: doesn’t exist), and a message receiver object is defined (see Figure
• ArduinoListener—contains the main WorkerRole code. 3). Then, the device queue receiver is set to asynchronous mode to
• ConsoleListener—the console version of the Arduino- pull messages (commands from the queue). This queue will store
Listener, for local testing. commands sent by mobile devices, such as a Windows Phone.
• MSDNArduinoListener—the Windows Azure deploy- When a message is received in the queue, its content is inspected and
ment project for ArduinoListener. if it matches the “ON” or “OFF” commands, the information is written
to the connection stream established
with the device (see Figure 4).
Notice that the message isn’t
removed from the queue (message.
CompleteAsync) unless the writing
operation to the device connection
stream is successful. Also, in order
to keep the connection alive, the
device is expected to send a ping
heartbeat. For this proof of concept,
we aren’t expecting confirmation from
the device when it receives the message.
In a production system, however, this
would be required to comply with the
“command” pattern.
Step 3: Deploy the Arduino-
Listener Windows Azure Project
to Cloud Services Deploying the
ArduinoListener to Windows Azure
is extremely simple. In Visual Studio
2013, right-click on the MSDN-
ArduinoListener project and
Figure 2 Creating the Windows Azure Service Bus Namespace select the Publish option. You’ll find
msdnmagazine.com March 2014 13
1&1 eCOM
NEW SELL MORE WITH A
PROFESSIONAL DESIGN.
Q Whether beginner or professional, create
your store online in a few easy steps
Q Choose from over a hundred high-quality
designs and templates between industries
Q Store links easily with an existing domain
or your new included domain (free)*
Q Whether PC, tablet or smartphone your
shop will be displayed optimally on
all devices

DOMAINS | E-MAIL | WEB HOSTING | eCOMMERCE | SERVERS

* Offer valid for a limited time only. Complete packages come with a 30 day money back guarantee and no minimum contract term. The $7.99 per month price reflects a 12 month
pre-payment option for the 1&1 Online Store Starter package. After 12 months, regular price of $9.99 per month applies. Some features listed are only available with package upgrade.

Untitled-5 2 2/10/14 11:05 AM


MERCE
1&1 ONLINE STORE
COMPLETE PACKAGES

$7.
starting at

99
month*
Try now! 30 day money back guarantee.

START SELLING NOW!

MORE POSSIBILITIES. FIND CUSTOMERS. MAXIMUM RELIABILITY.


MORE SUCCESS. KEEP CUSTOMERS. PROFESSIONAL SUPPORT.
Q Your shop can grow with your Q Search engine optimization (SEO): Q Convenient shipping processing
business rank higher on Google and other via UPS, FedEx, etc.
search engines
Q Target customers with special Q Reliability through geo-
promotions and cross selling Q Easy synchronization with Amazon, redundant operation in two
Ebay, and more separate 1&1 Data Centers
Q Product rating capability: Build
trust by letting customers share Q Easily create your own Facebook Store Q 24/7 expert customer service
feedback by experienced eCommerce
Q Create customer loyalty by providing
professionals
Q Sell internationally: Wide selection free newsletters and coupons
of languages, currencies and
payment options

TEST MONTH CALL


30 DAY MONEY FLEXIBLE PAYMENT SPEAK WITH AN
BACK GUARANTEE OPTIONS EXPERT 24/7

Call 1 (877) 461-2631 1and1.com


Visit www.1and1.com for billing information and full promotional details. Program and pricing specifications and availability subject to change without notice. 1&1 and the 1&1 logo are
trademarks of 1&1 internet, all other trademarks are the property of their respective owners. © 2014 1&1 Internet. All rights reserved.

Untitled-5 3 2/10/14 11:06 AM


Figure 3 Creating a Queue Figure 4 Writing to the Connection Stream
var namespaceManager = NamespaceManager. if (message != null)
CreateFromConnectionString(RoleEnvironment. {
GetConfigurationSettingValue("serviceBusConnectionString")); Stream stream = message.GetBody<Stream>();
if (!namespaceManager.QueueExists(string.Format("dev{0:X8}", deviceId))) StreamReader reader = new StreamReader(stream);
{ string command = reader.ReadToEnd();
namespaceManager.CreateQueue(string.Format("dev{0:X8}", deviceId));
} if (command != null)
var deviceQueueReceiver = messagingFactory.CreateMessageReceiver( {
string.Format("dev{0:X8}", deviceId), ReceiveMode.PeekLock); switch (command.ToUpperInvariant())
do {
{ case "ON":
BrokeredMessage message = null; await deviceConnectionStream.WriteAsync(OnFrame, 0, OnFrame.Length);
message = await deviceQueueReceiver.ReceiveAsync(); await message.CompleteAsync();
... break;
case "OFF":
await deviceConnectionStream.WriteAsync(OffFrame, 0, OffFrame.Length);
specific instructions for the Publish Windows Azure Application await message.CompleteAsync();
break;
Wizard at bit.ly/1iP9g2p. After completing the wizard, you end up with }
a cloud service located at xyz.cloudapp.net. Record this name, as }
}
you’ll need it when you create the Arduino client in the next step.
Step 4: Program the Arduino Device to Talk to the Gateway
(Listener) Arduino devices offer a rich interface for performing net- data into the buf array when it arrives. If a value of “1” is detected,
work operations using a simple Web client object. For our prototype, the LED is turned on, if the value is “2,” the LED is turned off. The
we decided to use the Arduino Uno R3 model (bit.ly/18ZlcM8), along stopwatch object is reset after each command.
with its corresponding Ethernet shield (bit.ly/1do6eRD). To install, Once the sketch has been uploaded to the device, the code runs on
interact and program Arduino devices using Windows, follow the the Arduino controller in an infinite loop, trying to connect to a cloud
guide at bit.ly/1dNBi9R. You’ll end up with an easy-to-use IDE (called service. When connected, it forwards the device id so the cloud service
the Arduino application), where you can write programs (called knows to which device it’s talking. Then the code begins to read input
sketches) using JavaScript, as shown in Figure 5. from the cloud service, telling the device whether to turn on or off the
Figure 6 shows the sketch for interacting with the Arduino LED light (in this case, it’s connected to digital port 8 of the device).
Listener created in Step 3, and now deployed in Windows Azure. Step 5: Creating a Windows Phone Client to Send to Device
Sketches for the Arduino have two main sections: setup and Queue Interacting with the device is as simple as sending messages
loop. Instructions in the setup section are executed once, and this to the device queue. As we mentioned at the beginning of the
is where variables are initialized and connections established. In article, the Windows Azure Service Bus provides a REST interface
our example, the Ethernet client and related values are defined, a that lets you interact with it from multiple programming languages.
serial connection (for debugging purposes) is established, and the Because there’s no official SDK for Windows Phone developers, we
pin where the LED is connected is initialized as an output port. used one of the examples from the Windows Phone community,
Code in the loop section is executed constantly, and it includes which shows how to authenticate and interact with the Service
two main blocks based on the status Bus using HTTP requests and the
of the TCP connection between the WebClient object. The source code
Arduino device and the listener run- is also included with the code down-
ning in Windows Azure Cloud Services: load, in the Visual Studio 2013 project
connected or disconnected. When the called MSDNArduinoClient. Figure
connection is established for the first 7 shows the client’s main screen, from
time, a stopWatch object is started to which you send commands to the
keep track of the time elapsed for the Arduino device.
connection. Also, the device identifier Creating similar clients for other
is sent to the listener, to be used as the mobile devices (including iOS and
name of the queue where messages and Android) wouldn’t be difficult, as most
commands will be stored. of them provide libraries to gener-
The code block that handles the ate REST commands using HTTP
Arduino behavior after the connec- request clients. Moreover, it’s possible
tion has been established keeps track to directly interact with the Windows
of the time elapsed since the connec- Azure Service Bus using traditional
tion was created, pinging the listener languages such as Java, PHP or Ruby,
every 200,000 ms, to keep the con- which simplifies this process. These
nection alive when no commands are SDKs are published under an open
received. This code also tries to read source license, and can be found
data from the listener, putting the Figure 5 The Arduino Application at github.com/WindowsAzure.
16 msdn magazine Windows Azure Insider
Figure 6 The Arduino Device Code
#include <SPI.h> else
#include <Ethernet.h> {
#include <StopWatch.h> Serial.println("Connection unsuccessful");
// Enter a MAC address and IP address for your controller below. client.stop();
// The IP address will be dependent on your local network, stopWatch.reset();
// and it's optional if DHCP is enabled. }
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0xBC, 0xAE }; }
static const byte deviceId[] = { 0x00, 0x00, 0x00, 0x01 }; if (connected == 1)
static const uint8_t ACK = 0x01; {
static const int LED_PIN = 8; if (stopWatch.elapsed() > pingInterval)
int connected = 0; {
EthernetClient client; Serial.println("Pinging Server to keep connection alive...");
StopWatch stopWatch; client.write(deviceId, sizeof(deviceId));
long pingInterval = 200000; stopWatch.reset();
void setup() stopWatch.start();
{ }
Serial.begin(9600); byte buf[16];
Serial.println("Initialized"); int readResult = client.read(buf, 1);
Ethernet.begin(mac); if (readResult == 0)
pinMode(LED_PIN, OUTPUT); {
} Serial.println("Can't find listener, disconnecting...");
void turnLedOn() connected = 0;
{ stopWatch.reset();
digitalWrite(LED_PIN, HIGH); }
} else if (readResult == 1)
void turnLedOff() {
{ Serial.println("Data acquired, processing...");
digitalWrite(LED_PIN, LOW); switch ( buf[0] )
} {
void loop() case 1:
{ Serial.println("Command to turn led on received...");
if ( connected == 0) turnLedOn();
{ break;
Serial.println("Trying to connect"); case 2:
char* host = "xyz.cloudapp.net"; Serial.println("Command to turn led off received...");
client.setTimeout(10000); turnLedOff();
connected = client.connect(host, 10100); break;
if (connected) }
{ stopWatch.reset();
Serial.println( stopWatch.start();
"Connected to port, writing deviceId and waiting for commands..."); }
client.write(deviceId, sizeof(deviceId)); }
stopWatch.start(); }
}

Wrapping Up geo-distributed and robust infrastructure for deploying the services


Building an Internet- required with a high volume of interconnected sensors and
of-Things architecture meters—a trend that will continue to grow in the years ahead. Q
using the Windows Azure
Service Bus to manage BRUNO TERKALY is a developer evangelist for Microsoft. His depth of knowledge
devices and services con- comes from years of experience in the field, writing code using a multitude of
nections provides an easy platforms, languages, frameworks, SDKs, libraries and APIs. He spends time
writing code, blogging and giving live presentations on building cloud-based
way to secure, scale and applications, specifically using the Windows Azure platform. You can read his
address clients individ- blog at blogs.msdn.com/b/brunoterkaly.
ually without incurring
costly VPN solutions, with RICARDO VILLALOBOS is a seasoned software architect with more than 15 years
the benefit of efficiently of experience designing and creating applications for companies in multiple
handling occasionally industries. Holding different technical certifications, as well as a master’s degree
in business administration from the University of Dallas, he works as a cloud
disconnected scenarios. architect in the DPE Globally Engaged Partners team for Microsoft, helping
Queues act as dedicated companies worldwide to implement solutions in Windows Azure. You can read
mailboxes where messages his blog at blog.ricardovillalobos.com.
between devices and
services are exchanged, Terkaly and Villalobos jointly present at large industry conferences. They
supporting the different encourage readers of Windows Azure Insider to contact them for availability.
Terkaly can be reached at [email protected] and Villalobos can be reached
communication use cases at [email protected].
and patterns commonly
Figure 7 The Windows Phone Cli- found in the field. Windows THANKS to the following Microsoft technical experts for reviewing this article:
ent Interface Azure provides a reliable, Abhishek Lal and Clemens Vasters
msdnmagazine.com March 2014 17
ASYNC PROGRAMMING

Patterns for
Asynchronous
MVVM Applications:
Data Binding
Stephen Cleary

Asynchronous code using the async and await keywords As of this writing, the async and await keywords are supported
is transforming the way programs are written, and with good on a wide number of MVVM platforms: desktop (Windows
reason. Although async and await can be useful for server software, Presentation Foundation [WPF] on the Microsoft .NET Framework 4
most of the current focus is on applications that have a UI. For and higher), iOS/Android (Xamarin), Windows Store (Windows 8
such applications, these keywords can yield a more responsive UI. and higher), Windows Phone (version 7.1 and higher), Silverlight
However, it’s not immediately obvious how to use async and await (version 4 and higher), as well as Portable Class Libraries (PCLs)
with established patterns such as Model-View-ViewModel (MVVM). targeting any mix of these platforms (such as MvvmCross). The
This article is the first in a short series that will consider patterns time is now ripe for “async MVVM” patterns to develop.
for combining async and await with MVVM. I’m assuming you’re somewhat familiar with async and await
To be clear, my first article on async, “Best Practices in Asyn- and quite familiar with MVVM. If that’s not the case, there are a
chronous Programming” (msdn.microsoft.com/magazine/jj991977), was number of helpful introductory materials available online. My blog
relevant to all applications that use async/await, both client and (bit.ly/19IkogW) includes an async/await intro that lists additional
server. This new series builds on the best practices in that article resources at the end, and the MSDN documentation on async is
and introduces patterns specifically for client-side MVVM appli- quite good (search for “Task-based Asynchronous Programming”).
cations. These patterns are just patterns, however, and may not For more information on MVVM, I recommend pretty much
necessarily be the best solutions for a specific scenario. If you find anything written by Josh Smith.
a better way, let me know!
A Simple Application
This article discusses: In this article, I’m going to build an incredibly simple application,
as Figure 1 shows. When the application loads, it starts an HTTP
• Combining asynchronous programming with the MVVM pattern
request and counts the number of bytes returned. The HTTP
• Developing an asynchronous data-bound property
request may complete successfully or with an exception, and the
• Common mistakes with ViewModels application will update using data binding. The application is fully
• An approach that’s data-binding friendly responsive at all times.
Technologies discussed: First, though, I want to mention that I follow the MVVM pattern
Asynchronous Programming, MVVM
rather loosely in my own projects, sometimes using a proper
domain Model, but more often using a set of services and data
18 msdn magazine
and the “UI-agnostic code” (probably the Model and definitely all
other layers, such as services and data access).
Furthermore, all code outside the View layer (that is, the View-
Model and Model layers, services, and so on) should not depend on
any type tied to a specific UI platform. Any direct use of Dispatcher
(WPF/Xamarin/Windows Phone/Silverlight), CoreDispatcher
(Windows Store), or ISynchronizeInvoke (Windows Forms) is a
bad idea. (SynchronizationContext is marginally better, but barely.)
For example, there’s a lot of code on the Internet that does some
asynchronous work and then uses Dispatcher to update the UI; a
more portable and less cumbersome solution is to use await for
asynchronous work and update the UI without using Dispatcher.
ViewModels are the most interesting layer because they have UI
affinity but don’t depend on a specific UI context. In this series, I’ll
combine async and MVVM in ways that avoid specific UI types
while also following async best practices; this first article focuses
on asynchronous data binding.

Asynchronous Data-Bound Properties


The term “asynchronous property” is actually an oxymoron.
Property getters should execute immediately and retrieve current
Figure 1 The Sample Application values, not kick off background operations. This is likely one of the
reasons the async keyword can’t be used on a property getter. If you
transfer objects (essentially a data access layer) instead of an actual find your design asking for an asynchronous property, consider
Model. I’m also rather pragmatic when it comes to the View; I some alternatives first. In particular, should the property actually
don’t shy away from a few lines of codebehind if the alternative is be a method (or a command)? If the property getter needs to kick
dozens of lines of code in supporting classes and XAML. So, when off a new asynchronous operation each time it’s accessed, that’s not
I talk about MVVM, understand that I’m not using any particular a property at all. Asynchronous methods are straightforward, and
strict definition of the term. I’ll cover asynchronous commands in another article.
One of the first things you have to consider when introducing In this article, I’m going to develop an asynchronous data-bound
async and await to the MVVM pattern is identifying which parts of property; that is, a data-bound property that I update with the
your solution need the UI threading context. Windows platforms results of an async operation. One common scenario is when a
are serious about UI components being accessed only from the UI ViewModel needs to retrieve data from some external source.
thread that owns them. Obviously, the view is entirely tied to the As I explained earlier, for my sample application, I’m going to
UI context. I also take the stand in my applications that anything define a service that counts the bytes in a Web page. To illustrate
linked to the view via data binding is tied to the UI context. the responsiveness aspect of async/await, this service will also
Recent versions of WPF have loosened this restriction, allowing delay a few seconds. I’ll cover more realistic asynchronous services
some sharing of data between the UI thread and background in a later article; for now, the “service” is just the single method
threads (for example, BindingOperations.EnableCollection- shown in Figure 2.
Synchronization). However, support for cross-thread data binding
isn’t guaranteed on every MVVM platform (WPF, iOS/Android/ Figure 2 MyStaticService.cs
Windows Phone, Windows Store), so in my own projects I just
using System;
treat anything data-bound to the UI as having UI-thread affinity. using System.Net.Http;
As a result, I always treat my ViewModels as though they’re using System.Threading.Tasks;

tied to the UI context. In my applications, the ViewModel is more public static class MyStaticService
closely related to the View than the Model—and the ViewModel {
public static async Task<int> CountBytesInUrlAsync(string url)
layer is essentially an API for the entire application. The View {
literally provides just the shell of UI elements in which the actual // Artificial delay to show responsiveness.
await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);
application exists. The ViewModel layer is conceptually a testable
UI, complete with a UI thread affinity. If your Model is an actual // Download the actual data and count it.
using (var client = new HttpClient())
domain model (not a data access layer) and there’s data binding {
between the Model and ViewModel, then the Model itself also var data = await client.GetByteArrayAsync(url).ConfigureAwait(false);
return data.Length;
has UI-thread affinity. Once you’ve identified which layers have }
UI affinity, you should be able to draw a mental line between the }
}
“UI-affine code” (View and ViewModel, and possibly the Model)
msdnmagazine.com March 2014 19
Note that this is considered a service, so it’s UI-agnostic. Because properly, just as a service should. However, this is easy to forget,
the service is UI-agnostic, it uses ConfigureAwait(false) every time especially if you (or your coworkers) don’t regularly use async.
it does an await (as discussed in my other article, “Best Practices in Consider what could happen over time as the service code is main-
Asynchronous Programming”). tained. A maintenance developer might forget a ConfigureAwait,
Let’s add a simple View and ViewModel that starts an HTTP and at that point the blocking of the UI thread would become a
request on startup. The example code uses WPF windows with deadlock of the UI thread. (This is described in more detail in my
the Views creating their ViewModels on construction. This is just previous article on async best practices.)
for simplicity; the async principles and patterns discussed in this OK, so you should use “async all the way.” However, many devel-
series of articles apply across all MVVM platforms, frameworks opers proceed to the second faulty approach, illustrated in Figure 3.
and libraries. The View for now will consist of a single main Again, if you execute this code, you’ll find that it works. The UI
window with a single label. The XAML for the main View just binds now shows immediately, with “0” in the label for a few seconds
to the UrlByteCount member: before it’s updated with the correct value. The UI is responsive, and
<Window x:Class="MainWindow" everything seems fine. However, the problem in this case is han-
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> dling errors. With an async void method, any errors raised by the
<Grid> asynchronous operation will crash the application by default. This is
<Label Content="{Binding UrlByteCount}"/>
</Grid> another situation that’s easy to miss during development and shows
</Window> up only in “weird” conditions on client devices. Even changing the
The codebehind for the main window creates the ViewModel: code in Figure 3 from async void to async Task barely improves the
public partial class MainWindow application; all errors would be silently ignored, leaving the user
{
public MainWindow() wondering what happened. Neither method of handling errors is
{ appropriate. And though it’s possible to deal with this by catching
DataContext = new BadMainViewModelA();
InitializeComponent(); exceptions from the asynchronous operation and updating other
} data-bound properties, that would result in a lot of tedious code.
}

Common Mistakes A Better Approach


Ideally, what I really want is a type just like Task<T> with properties
You might notice the ViewModel type is called BadMainView-
for getting results or error details. Unfortunately, Task<T> is not
ModelA. This is because I’m going to first look at a couple of
data-binding friendly for two reasons: it doesn’t implement INotify-
common mistakes relating to ViewModels. One common mistake
PropertyChanged and its Result property is blocking. However, you
is to synchronously block on the operation, like so:
public class BadMainViewModelA
can define a “Task watcher” of sorts, such as the type in Figure 4.
{
public BadMainViewModelA()
{ Figure 3 BadMainViewModelB.cs
// BAD CODE!!!
UrlByteCount = using System.ComponentModel;
MyStaticService.CountBytesInUrlAsync("http://www.example.com").Result; using System.Runtime.CompilerServices;
}
public sealed class BadMainViewModelB : INotifyPropertyChanged
public int UrlByteCount { get; private set; } {
} public BadMainViewModelB()
This is a violation of the async guideline “async all the way,” but {
Initialize();
sometimes developers try this if they feel they’re out of options. }
If you execute that code, you’ll see it works, to a certain extent.
// BAD CODE!!!
Code that uses Task.Wait or Task<T>.Result instead of await is private async void Initialize()
synchronously blocking on that operation. {
UrlByteCount = await MyStaticService.CountBytesInUrlAsync(
There are a few problems with synchronous blocking. The most "http://www.example.com");
obvious is the code is now taking an asynchronous operation and }

blocking on it; by doing so, it loses all the benefits of asynchronicity. private int _urlByteCount;
If you execute the current code, you’ll see the application does public int UrlByteCount
{
nothing for a few seconds, and then the UI window springs fully get { return _urlByteCount; }
formed into view with its results already populated. The problem private set { _urlByteCount = value; OnPropertyChanged(); }
}
is the application is unresponsive, which is unacceptable for many
modern applications. The example code has a deliberate delay to public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
emphasize that unresponsiveness; in a real-world application, this {
problem might go unnoticed during development and show up only PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
in “unusual” client scenarios (such as loss of network connectivity). handler(this, new PropertyChangedEventArgs(propertyName));
Another problem with synchronous blocking is more subtle: The }
}
code is more brittle. My example service uses ConfigureAwait(false)
20 msdn magazine Async Programming
ement1); areaSerie
areaSeries
ies.A
Add(seriesElement2);
A dd(se
(s riesElement2
t2);
) areaSeries
areaSeries.Ad
Add(seriesElement3);
A d(seriesElement3); // Add series to the plot area plotArea
plotArea.Series.Add(areaSeries);
Series Add(areaSeries); //page
//page.Elemen
Elements
em ts.Add(
Add(
dd( new
ne LayoutGrid()
LayyoutGrid() ); /// Add
La Add the page elements to the page AddEA
s, 240, 0); AddEAN1
AN 3Sup
Sup5(pa
5(p ge.Eleme
ent
nts, 480, 0); Ad
ddUPCVersionA(page.Elemen
e ts, 0, 135); AddUPCVersionASup2(page.Elements, 240, 135); Ad
dddUPC
UPC
CVers
sionA
o Sup5((page.Elemen
nts,
t 480, 135); AddEAN8(page.Elements, 0,
.Elements, 480, 270);; Add
ddUUPCVersionE(pa
page.Elementts, 0, 405); AddUPCVersionESu
E p2(page.Elements, 240, 405); AddUPCVersionESup5(page
age.Ele
.Ele
lemments,
s, 48
480, 405); // Add the page
e tto the document document.Pages.Add(pa
CaptionAndRectang
a lee(eleme
ements, “EAN/JA
/JAN 13 Bar Cod
de”, x, y, 204, 99); BarCode barCode = new Ean13(“123456789012”, x, y + 21); barCode.
ode.XX+
+= (20
04 - ba
arCo
ode.GettSymbolWidth()
h ) / 2; elements.Add(barCode); } private vo
dRectangle(element
e s,, “EAN
EAN/JAN 13 Bar
ar Code,
C 2 digit supplement”, x, y, 204, 99); BarCode barCode = new Ean13Sup2(“1
2 2345
56789
678 012
1212”,
2”, x, y + 21
21);
1); barCo
ode.X
X += (204 - barCode.GetSymbolWidth()) / 2; elements.Add((barC
ts, float x, float y) { AddCa
A CaptionAndRectan
angle(elements, “EAN/JAN 13 Bar Code, 5 digit supplement”, x, y, 204, 99); BarCo
a C de barCo
arC
Code
de = new
n Ean13Su
Ean1 S p5(“12
2345
567890
01212345”, x, y + 21); barCode.X += (204 - barCode
e.Get
ddUPCVersionA(Grou
Group elemen
em ts, float x, float
floa y) { AddCaptionAndRectangle(element
e s, “UPC Version A Bar Code”, x, y, 204,
2 99);
9) Ba
B
BarrCode
C barC
bar Code = ne
ew UpcVe
pcVersionA
A(“12345678901”, x, y + 21); barCode.X += (204 - ba
arCo
ddUPCVersionAS
Sup2(
up2 Grou
oupp elements,, float
oa x, float y) { AddCaptionAndRectangle(ele
( ments, “UPC Version E Bar Code, 2 digit
git sup
supp
up lement”
nt”, x,
x y, 204, 99
9); Ba
arCod
de barC
Code = new UpcVersionASup2(“12345678
7 90112”, x,
x, y +
s.Add(barCode); } private
te vo
oid AddUPCV
PCVersi
ers onASup5(Group elements, float x, float
o y) { AddCaptionAndRectangle(ele
emment
ents, “UPC
“UPC Ver
Version E Bar Code, 5 dig
git su
upplem
ment”, x, y, 204, 99); BarCode barCode = new UpcVe
ersio
ode.GetSymbolW
Width(
dth )) / 2
2; elements.Add
Add(bar
(ba Code); } private
e voi
v d AddEAN
EAN8(Group
p elements, float x, float y) { AddC
ddCapti
tionAn
onA
n dRec
Rec
ect
ctangle(elem
ments, “EAN
N/JAN
N 8 Bar Cod
de”, x, y, 204, 99); BarCode barCode = new
n Ean8(“1234
34
g(); fileDialog.Title
le = “Op
“Open
en File
F Dialog
g”; fil
fi eDialog.Filter = “Ad
Adobe PDF
F files
es (*.pdf)
f)|*.pdf|All Files (*.*)|*.*”; if (fileDi
eDialog.
log.Sh
Show
wDiallog()
og == Dialog
gResult.OK) { pdfViewe
ewer.Op
penFile(fileDialog.FileName, “”); } Save
Sav File
F Diallog sa
av
aveF
File Dialog”; ssaveF
veFileDialo
al g.Filter = “Ado
Adobe
e PDF files (*.pdf)
f)|*.pdf|All Files (**.*)|*.*”; if (saveFileDialog.ShowD
owDialo
ialo
a g()=
g()==Dia
=Dia
Di logResul
esult .O
OK) { pdfV
fViewe
ewerr.SaveAs(saveFileDia
Dialog.FileName
e); } if (p
( dfVi
dfV ewe
wer.P
Page
WithDialog(); } else
e se { Mess
Me ageBox.S
Show(
w “P
Please open a file to printt”); } OpenFile
F Dialog fileDi
D alog
log = ne
ew Ope
penFileD
pen Dialog(); file Dialog.Tiitle = “Open File Dialo
og”; filleDialog.InitialDirec
ecto
ory = @”c:\”
:\ ; fi
fileD
leDialog
lo .Filter
ter = “All
“ F
) == DialogResul
es t.O
t.OK) { Dy
D namicPDF
FView
ewerrClass test = new
ew Dynam
D micPDFVie
ewerC
r lass(); PDF
DFPrin
Printter prin
inter
nter
er = test.OpenF
pe ileForP
orPrin
in
ntter (fileDia
(file alog.FileName); prin
nter.PrintQuie
Quiet();
() } byt
by e[] cont
contents
t =
pServices; GCH
GC and
andle gc
ch = GCHandle
d .All
Al oc
c(contents, GCH
Hand
ndleTyp
Type.Pinned
d); Int
IntPtr cont
contents
entsIntPtr =gc
ch.
ch.
h.A
AddrOfPinned
nn Obje
ect()
ct ;p
pd
df Viewer.O
.OpenB
pe ufffe
fer(co
r( ntentsIntPtr
t ,
kmark Page Eleme
lement:”, x, y); p
pageEle
ement
en s.A
Add(new Book
kmarrk(“
( Bookmarked Text”
x , x + 5, y + 20,
0 par
pare
ent
e ntOutline)); pageEl
g emen
nts.A
ts.Add (new Label(“Thiss tex
text is bookma
okmarked
okma rked.”,
”, x + 5, y + 20, 2
ageElements,
s, float
fl a x, float
at y) { // Adds
dss a circltt to the pageElleme
ents AddCaptio
onAndR
AndRecta
ectangle(pag
pag
geEle
eEl ment
men s, “Circle Page Elem
ment:
ent:”,
t:: x,, y); pageEl
geElements.A
s.Add(n
dd(new Circle(x
dd (x + 112.5f
2 ,
shLarge)); } priva
vate
te void AddF
Ad orormatted
te
edTextArrea(Group
p page
geElemen
nts, float x, floa
floatt y)
y { // A
Adds a fo
for
o mattted
d text area to
o thepageE
eElle ments strring formatt
m edHt
edHtml = “<p><
“<p <i>Dynamic</i><b>PD
b>P F</b
b>&tm
tm;
m Genera
era
ator
o v6.0 for
or or .NE
matting suppo
ort for
or text th
that appearss in the document.
t. You
Y u havve “ + “com
complet
ple
ete co
ontro
rol ovve
err 8 par
aragraph
ph properties:
p spacing befo
efore
e, spacing
g after, fir
first liine “ + “indenta
ntation, left indentati
tation, righ
r t in
ndent
ntatio
tionn, aliignment, al
alllowi
<font face=’Tim
’Times’
es’>fontt fac
f e, </fon
nt>
><font poin
o tSiz
Size=’6’>ffont “ + “size, </fon
</fon
nt><fo
<f ntt co
color
olo
llor=’FF0000
00 ’>>colo
or, </font>
><b>b
<b> old, </b
</b><
<i>italic and </i><
<u>u
underline</u>
>; “ + “and 2 line pro
opert
rties: lea
eading
ng, an
nd le
ead
ding type. Text
extArea = ne
ew Format
Form tedT
dTextA
Area(for
orrmattedH
Htm
tml, x + 5, y + 20,, 215
5, 60,, F
Font
ontFamil
mi y.He
He
elvet
lv
vetica
e ica, 9, false)
e); // Sets the indent prope
roperty fo
formatte
att dTextAre
ea.Styyle.P
Paragrap
ph.In
ndent = 18; AddC
CapttionAndRect
Rectangl
gle(pa
e(pa
e (pa
pa
ageE
geElements,
ts, “F
ageEleme
m ntts, “Fo
“Form
mattedT
dTextA
tArea Overflow
flow Text:”, x + 279
9, y); pag
geEle
ement
men s.Ad
Add(fo
Add(fo
d ormat
orm
rmat
atttedTextA
tArea)); // Create
eaan overflow formatted
ed ttextt a
area forr the
t overfl
flow textt Fo
ormattedTextArea
a ove
overflowFor
Formatt
ma edTe
Text
xtAr
tArea
ea = formatte
e
a(x + 284, y + 20);
20) ppageE
Elemen
ements.Ad
dd(o
(overfl
verflow
ow
wForm
wFo rmat
a tedTe
extArea); } priv
private
ate void A
Add
dImag
mage(Group
mag up pag
geElem
emeents, float x, float y) { /// Adds an
n image
e to th
he pa
ageElem
men
ents AddCaptionAndRe
dRectangle((pag
pageElem
ments
en
nts “Image
nts, ge
e Pag
es/DPDFLo
ogo.pn
.png”), x + 112.5
5f, y + 50f,
50f, 0.24
0.24f
4f); // Image is size
ed an
nd cente
entered
d in
n the rec
ecta
tangle ima
m ge.SetBo
B unds(215, 60); image.VAlign = VAlign.Cen
enterr; ima
age.Align
n = Align.Center; pag
geE
eElements.Ad
.Addd(imag
ge); } priv
vate void A
pageElemen
nts Ad
AddCap
dC tion
onAndRecta
angle(pag
ngle(pag
geElements,
eEle “L
Labell & PageNumbe
Page eringL
ering bel Page E
gLab Elem
ementts:”, x, y); string labelText
T = “Labels can be rottated”; strring number
mbe Text = “PageNum
mbe
eringLabels
els cont
contai
ain page num
mb
beering
xt, x + 5, y + 12,
12 22
220, 80,
0 Fontt.Time
messRom
Roman,
an, 12, TextAlign..Cen
nter);; lab
label.Ang
Ang
gle = 8; Page
eNumb
Num erin
ri gLa
gLabel pageNumLabel = new PageNumber
b ingLabel
ab (nnumb
berText, x + 5, y + 55, 220, 80, Font.TimesR
esRoman, 12,
12 TextAl
tAlig
ign.
ign
n Ce
m nts.Add(labe
me abel);
l); } private
e void AddL
dLin
ne(G p pageElemen
ne(Group nts, float x, floa
oat y)) { /// Addss a lin
ne to the
he pageE
pag lements AddCaptionAndRectangle(p
( ageEleme
e nts, “Line Pa
age Element:”, x, y); page
eElemen
nts.A
s.Add(ne
ew Line(x
x+5
5, y +
w Line(x
x + 220,
2 y + 20, x + 5, y + 80, 3,
3, RgbCo
Rg olor.Green)); } pr
priv
iva
ate void
d AddLi
A ink
nk(Group
up p
pag
pa eElement
ments, float x, float y) { // Adds a link to the pageEleme
em ntts Fo
ont font = Fo
ont.TimesRoman;; st
string text = “T
This iss a link
k to Dyna
amic
m :”, x, y); Label label = ne
ment new Lab
Label(textt, x + 5, y + 20, 215
5, 80
5, 0, fon
nt, 12, Rgb
R bColor.
or.BBlu
lue);; label.Und
Under
erline = true; Link link = new Link(x + 5, y + 20, font.
on GetTe
extWidth
h(tex
xt, 12), 12 - font.G
GetD
Descend
der(1
r(12), ne
ew Url
UrlA
lAction(“
n(“h
http
E men
Ele nts.Add(li
( nk);; } priva
p vatee vvoid
d AddPath(
ath Grroup pageElem
mentts, fl
float
oat x, floatt y) { // Ad
Adds a path
h to the pageElements ceTe.DynamicPDF.PageElement
en s.Path
h path = new
w ceTe.DynamicPD
PDF.P
F.PageElemen
men
nts.P
s.Pa
ath(
h(xx + 5, y + 20,
2 R
P s.A
Path Add(new Line
eSubPat
Pa h(x
x + 215, y + 40))); path.Su
h.S bPatths.A
Add
dd((new Curv
urve
eToSubPat
Pa h(x + 10
08, y + 80, x + 160, y + 80)); path.SubPaths.Add(new
w Curv
veSu
ubPath(x + 5, y + 40, x + 65,
6 y + 80, x + 5, y + 60))); Add
AddCCa ionA
Capt And
Add(p
path); } private
e void AddR
Rec
ctangle(Gr
G oup
p page
eElemen
nts, float
float x, float
at y)
y) ordere
e dLis
dL t = ord
deredList.GetOverFlowList(x + 5, y + 20); AddCaptionAn
AndR
Recta
ang
gle(pag
ge.Elements, “Order
r ed List
L Page
ge
e Ele
El ment
nt Ove
Ove
v rflow
rfl :”, x, y, 2
8; //
8; // Create
C an uno
ordere
ed list Unordered
nor e List unorder
e edList
er stt = ne
ew Uno
norder
rderedL
edList(x
x + 5, y + 20,
20 400, 90, Font.Helvetica, 10); unorderedList.Items.Add(
Add(“Fruits””); unordered
ere List.Items.Add(“
d “Vege
Vege
g table
es””); U
Unorde
r eredS
re
edS
d ub
bList unord
t();
(); unorderedSubList.Items.
ms.Add(“
dd((“ Citrus”); unord
ordered
eredSu
edSubLiist.Ite
emss.Ad
Add
d(“ Non
n-Citr
t us”)
s” ; Ad
AddC
CaptionAndRectangle(page.Elemen
nts, “Unordered Lis
st Page
e Elem
meent:”, x, y,
y 225, 110); Uno
n rd
dere
edSubLis
bLis
st u
unnorde
deredS
redS
d ubLi
ub st2 = uno
erredSub
bList2.Items.Add((“Po
Potato”); unorderedS
SubLis
ubLiist2.Item
ms.Ad
dd(“B
Beans”); Unor
no dere
deredSub
dSubLisst subUnorderedSubList = unordered
e SubL
S ist.Items[0]].Su
ubLists.A
AddUnorder
rde edSubList(); ssub
bUnor
UnorderedSub
dS
Sub
bList.Ite
te
ems.A
m Add
d(“Lime”); s

;<M<CFG<;=FI
List sub
bUnorderedS
SubList2
st = unorderedSubL
S bList.Items
ist. s[1].Su
ubLis
sts.A
AddUnorde
eredSu
edS bLis
bL t();
t() su
ubUnorderedSubList2.Items.Add(“Man
a go”); subUnorrdere
edS
SubList2.I
t2 tem
ms.A
Add(“Banana”);
); Un
Unord
deredS
dS
Su Lis
SubList
s sub
ubUnor
n dere
de dSubList
t();
() subU
Unordered
dSubList3.Items.Add(“Swe
Swee
w t Po
Potato””); Uno
order
d redS
SubList sub
bUnor
Uno dere
er dSub
dSubList
List4
4 = unorrderedSubList2.It
2 ems[1].S
].SubLists.AddUno
orde
ered
dSub
bList(
s ); subUn
ubU orderedSubLi
ubList
b st4.
s Ite
ems.Ad
A d(“S
Ad “Strin
ing Be
ean”)
an” ; subUno
U rde
Add(“Kid
dney Bean
ean”); x += 279; pag
a e.Eleme
ements.Ad
dd(u
d nordere
edLisst); unorderedList
Lis = unord
nordered
ere List.GetOver
e FlowList(x + 5, y + 20);
) Add
A CaptionAndRec
ctan
ngle(p
page
ag .Eleme
ents, “Unor
Unordere
dere
r d List Page
e Elem
ment Ove
ve flow:
ver flo ”, x, y, 225
oid
d Add
dTextField(Group pageElemen
dTextF me ts,, flo
float x,, flo
oat y)) { Tex
xtField txtt = new TextFi
xtF eld(“txt
“t fna
fname”, x + 20, y + 40, 120, 20); txt.Defaul
u tValue = “This is a Scrrollab
ble
e TextF
Field
d”; txt.Borde
derCol
rCol
C o or = Rgb
RgbCColo
or.B
r.Black; txt
xt ackgro
xt.BackgroundC
un o
d(txt); T
d( TextField
ex txt1 = new TextFi
Field(
ld “tx
xtf1na
f1 me”,
me” x + 175, y + 40, 120, 20); txtt1.Def
De ault
u Valu
alue = “TextField”; txt1.Password = true; txt1.MaxLength = 9; txtt1.Bo
ord
derColor = RgbCollor.Black;
or.B
B txt1.B
1.Backg
ckgroun
ou dCol
olor
or = Rgb
R Colo
Colorr.Al
Al
e ies(); pieSeries.DataLabel = da
er da;
a; plo
p tAre
Area
a.Series
rie .Add(pieS
Series
s); pieSeries.Eleme
lementss.Add(
Add(27,
27, “Website A”); pieSeries.Elements.Add
d (19, “Website B”)); pieSerries
es.Element
men s.Add(21
d(21, “W
Web
ebsi
s te
e C”);
); pieS
p eries.El
Elemen
ements[0
me ts[0
s[0].Co
s[0 ].C lor
or = a
ess.Elements[2].Color = aut
utogra
og dien
dientt3;”RgbC
RgbColo
or.Alice
eBlue; txt2.Too
.ToolTip = “Multilin
ne”; page
pageEl
Elements.Add(txt2); AddCaptionAndRectangle(pageEleme
mennts, “Tex
xtFi
Field Form
orm Pag
Pagee Elemen
Elemen
nt:”,
:”, x, yy,, 504
04, 85);
5) } priva
ate
e void
oid AddC
dComb
omb

@EKL@K@M<LJ<
C
Comb oBox(“cm
mbnam
bna e”, x + 51, y + 40, 150,
15 20); cb.B
BorderColo
or = RgbColor.Blac
Black; cb.Ba
b.Back
ckgroundColor = RgbColor.AliceBlue; cb.Font = Font.Helve
elvetica
a; cb
b.Fon
Fo tSiz
Sizze = 12; cb.I
cb.Items
temss A (“Item
tems.Add e 1”);
em ); cb.
cb.Items
tems.Add
.Add(“It
Ad (“It
( Item
em 2”); cb
d table””);
di ”) cb.Item
ms[
s[“
[“Edita
able”].Select
cted
ed = true;; c
cb.Editable = tru
ue; cb.ToolTip = “Edi
“Ed tabl
ab e Co
C mbo Box”; pageElements.Add(cb); ComboBox cb1 = new
ew Combo
omb
b Box(
B x(“cmb
mb1nam
me”, x + 303,
303, y + 40, 150, 20);
20); cb1.B
cb1 Borde
rder
derColor = R
= Font.H
Helvetic
ca; cb
cb1.FontS
nt ize = 12
2; cb1.Itemss.A
Add(“IItem 1”); cb1.Ittems.Add(“It
“Item
em 2”);
”) cb1.
cb1.It
Items.Add(“Item 3”); cb1.Items.Add(“Item 4”); cb1.Itemss.A
Add
d(“No
on-Edi
ditab
table
e”);
); c
cb1.Items[“
[“Non-
“Non-Edit
Editable
able”].S
”].Seelected = true; cb1.
1 Edita
nts.Ad
nts d(cb
(cb1); Converter.Co
C nvert(“http://www.go
goog
ogle.c
com”, “Outputt.pdf”);Convert
ve er.C
er.Conve
onvert(GetDocPath(“DocumentA.rtf”), “Output.pdf”);System.Dia
iagno
ostics
css.Pro
P ocess
ess.S
s Start(“Outp
tput.p
ut.pdf”)
pdf”);; AsyncC
As ncConverter
rt aCo
Co
onverte
nve er = new A
err(aCo
(aC nverrter_Converted); aConverter.ConversionErro
or += new Con
nversionErrorEv
ventH
tHandler(aConverter_ConversionError); aConverter.Convert(@”C:\t
C:\ em
mp\Docum
mp mentA.rtf”, @”C:\tem
ment em
mp\Ou
p\Output
tputA.pd
A.pdf”);
f”);
) aConver
v rter.
ter.Co
onvert(@”C
ve t(@”C:\temp\DocumentC.rtf”, @”C:\temp\OutputC
ver C.pdf”)); aCo
onve
erter.Convert(
e “h http://
p://www.yahoo.com”, @”C:\Temp\yahoo.pdf”); ConversionOptions
ion op
option
ns = new Conversio
ns sionOpt
nOpttions
ions(720
(720,, 720,
72 72, true); ce
eTe.D
Te. ynamicPDF
te \\o
temp output.pdf”, options); ceTe.DynamicPDF.Conve
ersion.Con
nvertter.Convert(“C:\\\te
emp\\Document2.docx”, “C:\\temp\\output.pdf”, options); string
g sam
ampl
mp eHtm
mp H mll = “<ht
h ml><
ml><body
body><p>
><p>
p This is a very simpl
m e HTML
ML strring includ
<t le border=\”1\”>
<tab 1 <tr><td>100</td><td>200</td>”” + “<ttd>3
300<
</td></tr><tr><
<td>
>400</td><td>500</td><td>600</t
< d></tr></table><
></bod
/body><
y></
</h
</
/htm
ht >”;Conve
html o version.Co
n.Co
C nver
nverter.
ter.C
ConvvertHtmlString(sa
ample
mpleHtmll, “C:\\
“C \temp\
emp\\Sam
\Sam
erName
er e”, Path.Comb
o ine(GetPath(), “LetterPortrait.pdff”)); prrintJo
ob.D
DocumentName
e = “Lett
Letter
e Portrait”; if (printJob.P
ob. rinter.Color)
r pri tJob.PrintOp
prin P ntO
n tions.Co
s. lor
o = true;
true; if (prin
(prin
ri tJob
tJo .Printer.Col
C late) printJob.P
b.P
.P
PrintOpti
Opti
p ons.
ons Co
on ollat = tru
ollate

;peXd`ZG;=Ç:fdgi\_\ej`m\G;=Jfclk`fej]fi%E<K;\m\cfg\ij
Z\K\ Jf]knXi\Ëj ;peXd`ZG;= gif[lZkj gifm`[\ i\Xc$k`d\ G;= ^\e\iXk`fe# dXe`glcXk`fe# Zfem\ij`fe#
gi`ek`e^#m`\n`e^#Xe[dlZ_dfi\%Gifm`[`e^k_\Y\jkf]Yfk_nfic[j#k_\fYa\Zkdf[\cjXi\\oki\d\cp
Õ\o`Yc\ Ylk jk`cc jlggcp k_\ i`Z_ ]\Xkli\j pfl e\\[ Xj X [\m\cfg\i% I\c`XYc\ Xe[ \]ÔZ`\ek# k_\ _`^_$
g\i]fidXeZ\ jf]knXi\ `j \Xjp kf c\Xie Xe[ lj\% @] pfl [f \eZflek\i X hl\jk`fe n`k_ Xep f] fli
NNN%;PE8D@:G;=%:FD
Zfdgfe\ekj#j`dgcpZfekXZkZ\K\Jf]knXi\Ëji\X[`cpXmX`cXYc\#`e[ljkip$c\X[`e^jlggfikk\Xd%

KIPFLIG;=JFCLK@FEJ=I<<KF;8P
K
nnn%;peXd`ZG;=%Zfd&\mXcfiZXcc/''%-*(%,''-s"(+('%..)%/-)'
n

Untitled-1 1 9/8/11 12:41 PM


Figure 4 NotifyTaskCompletion.cs
using System; }
using System.ComponentModel; else if (task.IsFaulted)
using System.Threading.Tasks; {
propertyChanged(this, new PropertyChangedEventArgs("IsFaulted"));
public sealed class NotifyTaskCompletion<TResult> : INotifyPropertyChanged propertyChanged(this, new PropertyChangedEventArgs("Exception"));
{ propertyChanged(this,
public NotifyTaskCompletion(Task<TResult> task) new PropertyChangedEventArgs("InnerException"));
{ propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage"));
Task = task; }
if (!task.IsCompleted) else
{ {
var _ = WatchTaskAsync(task); propertyChanged(this,
} new PropertyChangedEventArgs("IsSuccessfullyCompleted"));
} propertyChanged(this, new PropertyChangedEventArgs("Result"));
}
private async Task WatchTaskAsync(Task task) }
{
try public Task<TResult> Task { get; private set; }
{ public TResult Result { get { return (Task.Status == TaskStatus.RanToCompletion) ?
await task; Task.Result : default(TResult); } }
} public TaskStatus Status { get { return Task.Status; } }
catch public bool IsCompleted { get { return Task.IsCompleted; } }
{ public bool IsNotCompleted { get { return !Task.IsCompleted; } }
} public bool IsSuccessfullyCompleted { get { return Task.Status ==
TaskStatus.RanToCompletion; } }
var propertyChanged = PropertyChanged; public bool IsCanceled { get { return Task.IsCanceled; } }
if (propertyChanged == null) public bool IsFaulted { get { return Task.IsFaulted; } }
return; public AggregateException Exception { get { return Task.Exception; } }
public Exception InnerException { get { return (Exception == null) ?
propertyChanged(this, new PropertyChangedEventArgs("Status")); null : Exception.InnerException; } }
propertyChanged(this, new PropertyChangedEventArgs("IsCompleted")); public string ErrorMessage { get { return (InnerException == null) ?
propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted")); null : InnerException.Message; } }
if (task.IsCanceled)
{ public event PropertyChangedEventHandler PropertyChanged;
propertyChanged(this, new PropertyChangedEventArgs("IsCanceled")); }

Let’s walk through the core method NotifyTaskCompletion<T>. Note that the label content is data-bound to NotifyTask-
WatchTaskAsync. This method takes a task representing the asynchro- Completion<T>.Result, not Task<T>.Result. NotifyTaskComple-
nous operation, and (asynchronously) waits for it to complete. Note tion<T>.Result is data-binding friendly: It is not blocking, and it
that the await does not use ConfigureAwait(false); I want to return to will notify the binding when the task completes. If you run the code
the UI context before raising the PropertyChanged notifications. This now, you’ll find it behaves just like the previous example: The UI
method violates a common coding guideline here: It has an empty is responsive and loads immediately (displaying the default value
general catch clause. In this case, though, that’s exactly what I want. I of “0”) and then updates in a few seconds with the actual results.
don’t want to propagate exceptions directly back to the main UI loop; The benefit of NotifyTaskCompletion<T> is it has many other
I want to capture any exceptions and set properties so that the error properties as well, so you can use data binding to show busy
handling is done via data binding. When the task completes, the type indicators or error details. It isn’t difficult to use some of these
raises PropertyChanged notifications for all the appropriate properties. convenience properties to create a busy indicator and error
An updated ViewModel using NotifyTaskCompletion<T>
would look like this: Figure 5 MainWindow.xaml
public class MainViewModel <Window x:Class="MainWindow"
{ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
public MainViewModel() xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
{ <Window.Resources>
UrlByteCount = new NotifyTaskCompletion<int>( <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
MyStaticService.CountBytesInUrlAsync("http://www.example.com")); </Window.Resources>
} <Grid>
<!-- Busy indicator -->
public NotifyTaskCompletion<int> UrlByteCount { get; private set; } <Label Content="Loading..." Visibility="{Binding UrlByteCount.IsNotCompleted,
} Converter={StaticResource BooleanToVisibilityConverter}}"/>
This ViewModel will start the operation immediately and then <!-- Results -->
create a data-bound “watcher” for the resulting task. The View <Label Content="{Binding UrlByteCount.Result}" Visibility="{Binding
UrlByteCount.IsSuccessfullyCompleted,
data-binding code needs to be updated to bind explicitly to the Converter={StaticResource BooleanToVisibilityConverter}}"/>
result of the operation, like this:
<!-- Error details -->
<Window x:Class="MainWindow" <Label Content="{Binding UrlByteCount.ErrorMessage}" Background="Red"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Visibility="{Binding UrlByteCount.IsFaulted,
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Grid> </Grid>
<Label Content="{Binding UrlByteCount.Result}"/> </Window>
</Grid>
</Window>

22 msdn magazine Async Programming


details completely in the View, such as the updated data-binding in mind the community is still developing these patterns; feel free
code in Figure 5. to adjust them for your particular needs. Q
With this latest update, which changes only the View, the appli-
cation displays “Loading…” for a few seconds (while remaining STEPHEN CLEARY is a husband, father and programmer living in northern Michigan.
responsive) and then updates to either the results of the operation He has worked with multithreading and asynchronous programming for 16 years
or to an error message displayed on a red background. and has used async support in the Microsoft .NET Framework since the first CTP.
His homepage, including his blog, is at stephencleary.com.
NotifyTaskCompletion<T> handles one use case: When you
have an asynchronous operation and want to data bind the results. THANKS to the following Microsoft technical experts for reviewing this article:
This is a common scenario when doing data lookups or loading James McCaffrey and Stephen Toub
during startup. However, it doesn’t help much
when you have an actual command that’s
asynchronous, for example, “save the current
record.” (I’ll consider asynchronous com-
mands in my next article.)
At first glance, it seems like it’s a lot more
work to build an asynchronous UI, and that’s
true to some extent. Proper use of the async
and await keywords strongly encourages you
to design a better UX. When you move to
an asynchronous UI, you find you can no
longer block the UI while an asynchronous
operation is in progress. You must think
about what the UI should look like during
the loading process, and purposefully
design for that state. This is more work, but it
is work that should be done for most modern
applications. And it’s one reason that newer
platforms such as the Windows Store sup-
port only asynchronous APIs: to encourage
developers to design a more responsive UX.

Wrapping Up
When a code base is converted from syn-
chronous to asynchronous, usually the ser-
vice or data access components change first,
and async grows from there toward the UI.
Once you’ve done it a few times, translating a
method from synchronous to asynchronous
becomes fairly straightforward. I expect (and
hope) that this translation will be automated
by future tooling. However, when async hits
the UI, that’s when real changes are necessary.
When the UI becomes asynchronous, you
must address situations where your applica-
tions are unresponsive by enhancing their
UI design. The end result is a more respon-
sive, more modern application. “Fast and
fluid,” if you will.
This article introduced a simple type that
can be summed up as a Task<T> for data
binding. Next time, I’ll look at asynchronous
commands, and explore a concept that’s
essentially an “ICommand for async.” Then,
in the final article in the series, I’ll wrap up
by considering asynchronous services. Keep
msdnmagazine.com March 2014 23
Untitled-4 2
PLATINUM SPONSOR
SUPPORTED BY
BE
MAY
THE
CODE

magazine
WITH
00110010010100101010010101011010010101010110101001010010100101010010010101010101101010001001010101010010000100110011011100010101010
1101010010000100001100101000100100011010100101011010000101101011010101110010000101000110011010101010011100110101000110101010001101110
0010101000100101010011101011011001010101011010100101001000000110101010001001010010101011100100010101101010000100111000010111001001000
01100101010100100101010101100101010001101010001010010101001101010101001010100010010101000100100010101100110110000101010001101110010

YOU!
PRODUCED BY

1/27/14 11:18 AM
LAS VEGAS 2O14
March 10 - 14 | Planet Hollywood Resort & Casino

COMPREHENSIVE
TRAINING
DEVELOPER

FOR THE
WORLD.
Visual Studio Live! Las Vegas is part of
Visual Studio Live! is your guide Live! 360 DEV, which means you’ll have
to the .NET Development universe, access to four (4) other co-located events
at no additional cost:
featuring code-Àlled days,
networking nights and independent
education. Whether you are a .NET
developer, software architect or a
designer, Visual Studio Live!’s
multi-track events include focused,
cutting-edge education on the .NET
platform that you’ll be ready to
implement as soon as you get back
to the ofÀce.
AND INTRODUCING Web Dev Live!

Five (5) events means over a hundred


sessions to choose from – mix and match
sessions to create your own, custom event
line-up - it’s like no other dev conference
available today!

Sessions are Àlling up


quickly – Register Today!
Use promo code VSLMAR2

vslive.com/lasvegas Scan the QR code to


register or for more
event details.

vslive.com/lasvegas
Untitled-4 3 1/27/14 11:18 AM
ASYNC PROGRAMMING

Asynchronous
TCP Sockets as an
Alternative to WCF
James McCaffrey

In a Microsoft technologies environment, using Windows changes the balance, so using socket programming for asynchronous
Communication Foundation (WCF) is a common approach for client-server systems is now a more attractive option than it used
creating a client-server system. There are many alternatives to WCF, to be. This article explains how to use these new asynchronous
of course, each with its own advantages and disadvantages, including features of the .NET Framework 4.5 to create low-level, high-
HTTP Web Services, Web API, DCOM, AJAX Web technologies, performance asynchronous client-server software systems.
named pipe programming and raw TCP socket programming. But The best way to see where I’m headed is to take a look at the demo
if you take into account factors such as development effort, man- client-server system shown in Figure 1. At the top of the image
ageability, scalability, performance and security, in many situations a command shell is running an asynchronous TCP socket-based
using WCF is the most efficient approach. service that accepts requests to compute the average or minimum of
However, WCF can be extremely complicated and might be overkill a set of numeric values. In the middle part of the image is a Windows
for some programming situations. Prior to the release of the Microsoft Forms (WinForm) application that has sent a request to compute
.NET Framework 4.5, asynchronous socket programming was, the average of (3, 1, 8). Notice the client is asynchronous—after the
in my opinion, too difficult in most cases to justify its use. But request is sent, while waiting for the service to respond, the user is
the ease of using the new C# await and async language features able to click on the button labeled Say Hello three times, and the
application is responsive.
The bottom part of Figure 1 shows a Web application client in
This article discusses: action. The client has sent an asynchronous request to find the
• Setting up a TCP socket-based service minimum value of (5, 2, 7, 4). Although it’s not apparent from the
• Creating a Windows Forms application demo client screenshot, while the Web application is waiting for the service
• Creating a Web application demo client response, the application is responsive to user input.
In the sections that follow, I’ll show how to code the service, the
Technologies discussed:
WinForm client and the Web application client. Along the way I’ll
Visual Studio 2012, Microsoft .NET Framework 4.5, C# discuss the pros and cons of using sockets. This article assumes you
Code download available at: have at least intermediate-level C# programming skill, but does
msdn.microsoft.com/magazine/msdnmag0314
not assume you have deep understanding or significant experi-
ence with asynchronous programming. The code download that
26 msdn magazine
accompanies this article has the complete source code
for the three programs shown in Figure 1. I have
removed most normal error checking to keep the
main ideas as clear as possible.

Creating the Service


The overall structure of the demo service, with a few
minor edits to save space, is presented in Figure 2. To
create the service, I launched Visual Studio 2012, which
has the required .NET Framework 4.5, and created a
new C# console application named DemoService.
Because socket-based services tend to have specific,
limited functionality, using a more descriptive name
would be preferable in a real-life scenario.
After the template code loaded into the editor, I
modified the using statements at the top of the source
code to include System.Net and System.Net.Sockets.
In the Solution Explorer window, I renamed file
Program.cs to ServiceProgram.cs and Visual Studio
automatically renamed class Program for me. Starting
the service is simple:
int port = 50000;
AsyncService service = new AsyncService(port);
service.Run();
Each custom socket-based service on a server
must use a unique port. Port numbers between 49152
and 65535 are generally used for custom services.
Avoiding port number collisions can be tricky. It’s
possible to reserve port numbers on a server using the
system registry ReservedPorts entry. The service uses
an object-oriented programming (OOP) design and
is instantiated via a constructor that accepts the port
number. Because service port numbers are fixed, the
Figure 1 Demo TCP-Based Service with Two Clients
port number can be hardcoded rather than passed as
a parameter. The Run method contains a while loop
that will accept and process client requests until the console shell The Service Constructor and Run Methods
receives an <enter> key press. The two public methods of the socket-based demo service are presented
in Figure 3. After storing the port name, the constructor uses method
GetHostName to determine the name of the server, and to then fetch a
Each custom socket-based structure that contains information about the server. The AddressList
collection holds different machine addresses, including IPv4 and IPv6
service on a server must use a addresses. The InterNetwork enum value means an IPv4 address.
This approach restricts the server to listen to requests using only
unique port. the server’s first assigned IPv4 address. A simpler alternative could
allow the server to accept requests sent to any of its addresses by
just assigning the member field as this.ipAddress = IPAddress.Any.
The AsyncService class has two private members, ipAddress and Notice the service’s Run method signature uses the async modi-
port. These two values essentially define a socket. The constructor fier, indicating that in the body of the method some asynchronous
accepts a port number and programmatically determines the IP method will be called in conjunction with the await keyword. The
address of the server. Public method Run does all the work of method returns void rather than the more usual Task because Run
accepting requests, then computing and sending responses. The is called by the Main method, which, as a special case, does not
Run method calls helper method Process, which in turn calls helper allow the async modifier. An alternative is to define method Run
Response. Method Response calls helpers Average and Minimum. to return type Task and then call the method as service.Run().Wait.
There are many ways to organize a socket-based server. The struc- The service’s Run method instantiates a TcpListener object using
ture used in the demo tries to strike a balance between modularity the server’s IP address and port number. The listener’s Start method
and simplicity, and has worked well for me in practice. begins monitoring the specified port, waiting for a connection request.
msdnmagazine.com March 2014 27
Figure 2 The Demo Service Program Structure Inside the main processing while loop, a TcpClient object, which
using System;
you can think of as an intelligent socket, is created and waits for
using System.Net; a connection via the AcceptTcpClientAsync method. Prior to the
using System.Net.Sockets;
using System.IO;
.NET Framework 4.5, you’d have to use BeginAcceptTcpClient
using System.Threading.Tasks; and then write custom asynchronous coordination code, which,
namespace DemoService
{
believe me, is not simple. The .NET Framework 4.5 adds many new
class ServiceProgram methods that, by convention, end with “Async.” These new methods,
{
static void Main(string[] args)
combined with the async and await keywords, make asynchronous
{ programming much, much easier.
try
{
Method Run calls method Process using two statements. An
int port = 50000; alternative is to use shortcut syntax and call method Process in a
AsyncService service = new AsyncService(port);
service.Run();
single statement: await Process(tcpClient).
Console.ReadLine();

One of the advantages of using


}
catch (Exception ex)
{

low-level sockets instead of


Console.WriteLine(ex.Message);
Console.ReadLine();
}

WCF is that you can easily insert


}
}

diagnostic WriteLine statements


public class AsyncService
{
private IPAddress ipAddress;

anywhere you choose.


private int port;

public AsyncService(int port) { . . }


public async void Run() { . . }
private async Task Process(TcpClient tcpClient) { . . }
private static string Response(string request) To summarize, the service uses TcpListener and TcpClient objects
private static double Average(double[] vals) { . . }
private static double Minimum(double[] vals) { . . }
to hide the complexity of raw socket programming, and uses the
} new AcceptTcpClientAsync method in conjunction with the new
}
async and await keywords to hide the complexity of asynchronous
programming. Method Run sets up and coordinates connection
Figure 3 Service Constructor and Run Methods activities, and calls method Process to process requests and then
public AsyncService(int port) a second statement to await on the return Task.
{

The Service Process and Response Methods


this.port = port;
string hostName = Dns.GetHostName();
IPHostEntry ipHostInfo = Dns.GetHostEntry(hostName);
this.ipAddress = null;
The Process and Response methods of the service object are
for (int i = 0; i < ipHostInfo.AddressList.Length; ++i) { presented in Figure 4. The Process method’s signature uses the
if (ipHostInfo.AddressList[i].AddressFamily ==
AddressFamily.InterNetwork)
async modifier and returns type Task.
{ One of the advantages of using low-level sockets instead of
this.ipAddress = ipHostInfo.AddressList[i];
break;
Windows Communication Foundation (WCF) is that you can
} easily insert diagnostic WriteLine statements anywhere you choose.
}
if (this.ipAddress == null)
In the demo, I replaced clientEndPoint with the dummy IP address
throw new Exception("No IPv4 address for server"); value 123.45.678.999 for security reasons.
}
The three key lines in method Process are:
public async void Run() string request = await reader.ReadLineAsync();
{ ...
TcpListener listener = new TcpListener(this.ipAddress, this.port); string response = Response(request);
listener.Start(); ...
Console.Write("Array Min and Avg service is now running" await writer.WriteLineAsync(response);
Console.WriteLine(" on port " + this.port); You can interpret the first statement to mean, “read a line of the
Console.WriteLine("Hit <enter> to stop service\n");
request asynchronously, allowing other statements to execute if
while (true) { necessary.” Once the request string is obtained, it’s passed to the
try {
TcpClient tcpClient = await listener.AcceptTcpClientAsync(); Response helper. Then the response is sent back to the requesting
Task t = Process(tcpClient); client asynchronously.
await t;
} The server is using a read-request, write-response cycle. It’s sim-
catch (Exception ex) { ple, but there are several caveats of which you should be aware. If
Console.WriteLine(ex.Message);
} the server reads without writing, it can’t detect a half-open situation.
} If the server writes without reading (for example, responding
}
with a large amount of data), it could deadlock with the client. A
28 msdn magazine Async Programming
/update/2014/03
www.componentsource.com

BEST SELLER Help & Manual Professional from $583.10


Easily create documentation for Windows, the Web and iPad.
• Powerful features in an easy accessible and intuitive user interface
• As easy to use as a word processor, but with all the power of a true WYSIWYG XML editor
• Single source, multi-channel publishing with conditional and customized output features
• Output to HTML, WebHelp, CHM, PDF, ePUB, RTF, e-book or print
• Styles and Templates give you full design control

BEST SELLER Aspose.Total for .NET from $2,449.02


Every Aspose .NET component in one package.
• Programmatically manage popular file formats including Word, Excel, PowerPoint and PDF
• Work with charts, diagrams, images, project plans, emails, barcodes, OCR, and document
management in .NET applications
• Common uses also include mail merging, adding barcodes to documents, building dynamic
reports on the fly and extracting text from PDF files

BEST SELLER GdPicture.NET Ultimate from $4,127.59


All-in-one AnyCPU document-imaging and PDF toolkit for .NET and ActiveX.
• Document viewing, processing, printing, scanning, OMR, OCR, Barcode Recognition, DICOM
• Annotate image and PDF within your Windows & Web applications
• Read, write and convert vector & raster images in more than 90 formats, including PDF
• Color detection engine for image and PDF compression
• 100% royalty-free and world leading Imaging SDK

BEST
BEST SELLER
SELLER ComponentOne ActiveReports 8 from $1,567.02
The award-winning .NET reporting tool for HTML5, WPF, WinForms, ASP.NET & Windows Azure.
• Create sophisticated, fast and powerful reports
• Generate flexible layouts using Section, Region and Fixed page designers
• Experience a new scalable, distributed and load balanced Enterprise-grade report server
• Utilize the just released HTML5 and touch-optimized report viewers for mobile devices
• Explore an array of data visualization options including charts, maps and more

We accept purchase orders.


© 1996-2014 ComponentSource. All Rights Reserved. All prices correct at the time of press. Online prices may vary from those shown due to daily fluctuations & online discounts. Contact us to apply for a credit account.

US Headquarters European Headquarters Asia / Pacific Headquarters


ComponentSource ComponentSource ComponentSource Sales Hotline - US & Canada:
650 Claremore Prof Way 30 Greyfriars Road 3F Kojimachi Square Bldg
Suite 100
Woodstock
GA 30188-5188
Reading
Berkshire
RG1 1PE
3-3 Kojimachi Chiyoda-ku
Tokyo
Japan
(888) 850-9911
USA United Kingdom 102-0083 www.componentsource.com

Untitled-6 1 1/30/14 12:29 PM


read-write design is acceptable for simple in-house services but instead of sending a response just as “4.00,” you might want to send
shouldn’t be used for services that are critical or public-facing. the response as “average=4.00.”
The Response method accepts the request string, parses the Method Process uses a relatively crude approach to close a
request and computes a response string. A simultaneous strength and connection if an Exception occurs. An alternative is to use the C#
weakness of a socket-based service is that you must craft some sort using statement (which will automatically close any connection)
of custom protocol. In this case, requests are assumed to look like: and remove the explicit call to method Close.
method=average&data=1.1 2.2 3.3&eor

An advantage of
In other words, the service expects the literal “method=” followed
by the string “average” or “minimum,” then an ampersand character

low-level services is that you


(“&”) followed by the literal “data=”. The actual input data must be in
space-delimited form. The request is terminated by an “&” followed

have greater control over your


by the literal “eor,” which stands for end-of-request. A disadvan-
tage of socket-based services compared to WCF is that serializing

data-access approach.
complex parameter types can be a bit tricky sometimes.
In this demo example, the service response is simple, just a string
representation of the average or minimum of an array of numeric
values. In many custom client-server situations, you’ll have to Helper methods Average and Minimum are defined as:
design some protocol for the service response. For example, private static double Average(double[] vals)
{
double sum = 0.0;
Figure 4 The Demo Service Process and Response Methods for (int i = 0; i < vals.Length; ++i)
sum += vals[i];
private async Task Process(TcpClient tcpClient) return sum / vals.Length;
{ }
string clientEndPoint = private static double Minimum(double[] vals)
tcpClient.Client.RemoteEndPoint.ToString(); {
Console.WriteLine("Received connection request from " double min = vals[0]; ;
+ clientEndPoint); for (int i = 0; i < vals.Length; ++i)
try { if (vals[i] < min) min = vals[i];
NetworkStream networkStream = tcpClient.GetStream(); return min;
StreamReader reader = new StreamReader(networkStream); }
StreamWriter writer = new StreamWriter(networkStream);
writer.AutoFlush = true; In most situations, if you’re using a program structure similar
while (true) { to the demo service, your helper methods at this point would
string request = await reader.ReadLineAsync();
if (request != null) { connect to some data source and fetch some data. An advantage
Console.WriteLine("Received service request: " + request); of low-level services is that you have greater control over your
string response = Response(request);
Console.WriteLine("Computed response is: " + response + "\n"); data-access approach. For example, if you’re getting data from
await writer.WriteLineAsync(response); SQL, you can use classic ADO.NET, the Entity Framework or any
}
else other data access method.
break; // Client closed connection A disadvantage of a low-level approach is you must explicitly
}
tcpClient.Close(); determine how to handle errors in your system. Here, if the demo
} service is unable to satisfactorily parse the request string, instead of
catch (Exception ex) {
Console.WriteLine(ex.Message); returning a valid response (as a string), the service returns an error
if (tcpClient.Connected) message. Based on my experience, there are very few general princi-
tcpClient.Close();
} ples on which to rely. Each service requires custom error handling.
} Notice the Response method has a dummy delay:
private static string Response(string request) int delay = ((int)vals[0]) * 1000;
{ System.Threading.Thread.Sleep(delay);
string[] pairs = request.Split('&'); This response delay, arbitrarily based on the first numeric value
string methodName = pairs[0].Split('=')[1];
string valueString = pairs[1].Split('=')[1]; of the request, was inserted to slow the service down so that the
WinForm and Web application clients could demonstrate UI
string[] values = valueString.Split(' ');
double[] vals = new double[values.Length]; responsiveness while waiting for a response.
for (int i = 0; i < values.Length; ++i)

The WinForm Application Demo Client


vals[i] = double.Parse(values[i]);

string response = "";


if (methodName == "average") response += Average(vals);
To create the WinForm client shown in Figure 1, I launched Visual
else if (methodName == "minimum") response += Minimum(vals); Studio 2012 and created a new C# WinForm application named
else response += "BAD methodName: " + methodName;
DemoFormClient. Note that, by default, Visual Studio modularizes a
int delay = ((int)vals[0]) * 1000; // Dummy delay WinForm application into several files that separate the UI code from
System.Threading.Thread.Sleep(delay);
the logic code. For the code download that accompanies this article,
return response; I refactored the modularized Visual Studio code into a single source
}
code file. You can compile the application by launching a Visual Studio
30 msdn magazine Async Programming
Untitled-1 1 10/13/11 11:25 AM
Figure 5 WinForm Demo Client Button Click Handlers Notice the signature of the button1 control’s click handler was
private async void button1_Click(object sender, EventArgs e)
changed to include the async modifier. The handler sets up a
{ hardcoded server machine name as a string and port number.
try {
string server = "mymachine.network.microsoft.com";
When using low-level socket-based services, there’s no automatic
int port = 50000; discovery mechanism and so clients must have access to the server
string method = (string)comboBox1.SelectedItem;
string data = textBox1.Text;
name or IP address and port information.
Task<string> tsResponse = SendRequest(server, port, method, data); The key lines of code are:
listBox1.Items.Add("Sent request, waiting for response"); Task<string> tsResponse = SendRequest(server, port, method, data);
await tsResponse; // Perform some actions here if necessary
double dResponse = double.Parse(tsResponse.Result); await tsResponse;
istBox1.Items.Add("Received response: " + dResponse.ToString("F2")); double dResponse = double.Parse(tsResponse.Result);
}
catch (Exception ex) { SendRequest is a program-defined asynchronous method. The
listBox1.Items.Add(ex.Message); call can be loosely interpreted as “send an asynchronous request
}
} that will return a string, and when finished continue execution at
private void button2_Click(object sender, EventArgs e)
the statement ‘await tsResponse,’ which occurs later.” This allows
{ the application to perform other actions while waiting for the
listBox1.Items.Add("Hello"); response. Because the response is encapsulated in a Task, the
}
actual string result must be extracted using the Result property.
That string result is converted to type double so that it can be
Figure 6 WinForm Demo Client SendRequest Method
formatted nicely to two decimal places.
private static async Task<string> SendRequest(string server, An alternative calling approach is:
int port, string method, string data)
{ string sResponse = await SendRequest(server, port, method, data);
try { double dResponse = double.Parse(sResponse);
IPAddress ipAddress = null; listBox1.Items.Add("Received response: " + dResponse.ToString("F2"));
IPHostEntry ipHostInfo = Dns.GetHostEntry(server);

A disadvantage of a low-level
for (int i = 0; i < ipHostInfo.AddressList.Length; ++i) {
if (ipHostInfo.AddressList[i].AddressFamily ==
AddressFamily.InterNetwork)

approach is that you must


{
ipAddress = ipHostInfo.AddressList[i];
break;

explicitly determine how to


}
}
if (ipAddress == null)

handle errors in your system.


throw new Exception("No IPv4 address for server");

TcpClient client = new TcpClient();


await client.ConnectAsync(ipAddress, port); // Connect

NetworkStream networkStream = client.GetStream(); Here, the await keyword is placed in-line with the asynchronous
StreamWriter writer = new StreamWriter(networkStream);
StreamReader reader = new StreamReader(networkStream); call to SendRequest. This simplifies the calling code a bit and also al-
lows the return string to be fetched without a call to Task.Result. The
writer.AutoFlush = true;
string requestData = "method=" + method + "&" + "data=" + choice of using an inline await call or using a separate-statement await
data + "&eor"; // 'End-of-request' call will vary from situation to situation, but as a general rule of thumb,
await writer.WriteLineAsync(requestData);
string response = await reader.ReadLineAsync(); it’s better to avoid the explicit use of a Task object’s Result property.
Most of the asynchronous work is performed in the Send-
client.Close();
return response; Request method, which is listed in Figure 6. Because SendRequest
} is asynchronous, it might better be named SendRequestAsync or
catch (Exception ex) {
return ex.Message; MySendRequestAsync.
} SendRequest accepts a string representing the server name
}
and begins by resolving that name to an IP address using the
same code logic that was used in the service class constructor.
command shell (which knows where the C# compiler is), and exe- A simpler alternative is to just pass the name of the server: await
cuting the command: csc.exe /target:winexe DemoFormClient.cs. client.ConnectAsync(server, port).
Using the Visual Studio design tools, I added a ComboBox control, After the server’s IP address is determined, a TcpClient
a TextBox control, two Button controls and a ListBox control, along intelligent-socket object is instantiated and the object’s Connect-
with four Label controls. For the ComboBox control, I added strings Async method is used to send a connection request to the server.
“average” and “minimum” to the control’s Items collection property. After setting up a network StreamWriter object to send data to the
I changed the Text properties of button1 and button2 to Send Async server and a StreamReader object to receive data from the server, a
and Say Hello, respectively. Then, in design view, I double-clicked request string is created using the formatting expected by the server.
on the button1 and button2 controls to register their event handlers. The request is sent and received asynchronously and returned by
I edited the click handlers as shown in Figure 5. the method as a string.
32 msdn magazine Async Programming
CHICAGO 2O14
4
May 5 - 8 | Chicago Hilton
on
vslive.com/chicago

live long
and code
Intense Take-Home
Training for Developers,
Software Architects
and Designers

Topics include:
REGISTER BY
³ Visual Studio/.NET APRIL 2 AND
³ Windows Client (Windows 8.1/
WinRT/WPF)
SAVE $200!
³ JavaScript/HTML5 Client
³ ASP.NET
³ Cloud Computing
³ Windows Phone
³ Cross-Platform Mobile Development
³ SharePoint/OfÀce
³ SQL Server Use promo code CHTIP1

vslive.com/chicago

Untitled-1 1 1/27/14 10:21 AM


CHICAGO 2O14
May 5 – 8 | Chicago Hilton

This May, developers, software REGISTER BY


architects, engineers, and designers
will blast off in the windy city for four
APRIL 2
days of unbiased and cutting-edge
AND SAVE $200!
education on the Microsoft Platform. vslive.com/chicago
Live long and code with .NET gurus,
CONNECT WITH VISUAL STUDIO LIVE!
launch ideas with industry experts
twitter.com/vslive – @VSLive
and rub elbows with Microsoft stars
in pre-conference workshops, facebook.com – Search ”VSLive”
60+ sessions and fun networking linkedin.com – Join the
events – all designed to make you “Visual Studio Live” group!
better at your job.
Plus, explore hot topics like Web API,
jQuery, MongoDB, SQL Server Data
vslive.com/chicago Tools and more!

Use promo code CHTIP1

SUPPORTED BY PRODUCED BY

magazine

Untitled-1 2 1/27/14 10:21 AM


The Web Application Demo Client control to demonstrate UI responsiveness while the application
I created the demo Web application client shown in Figure 1 in waits for the service to respond to a request.
two steps. First, I used Visual Studio to create a Web site to host the The code for the Web application’s SendRequest method is
application, and then I coded the Web application using Notepad. exactly the same as the code in the WinForm application’s Send-
I launched Visual Studio 2012 and created a new C# Empty Web Request. The code for the Web application’s Button1_Click handler
Site named DemoClient at http://localhost/. This set up all the differs only slightly from the WinForm’s button1_Click handler to
necessary IIS plumbing to host an application and created the phys- accommodate the different UI:
ical location associated with the Web site at C:\inetpub\wwwroot\ try {
string server = "mymachine.network.microsoft.com";
DemoClient\. The process also created a basic configuration file, int port = 50000;
Web.config, which contains information to allow applications in string method = TextBox1.Text;
string data = TextBox2.Text;
the site to access async functionality in the .NET Framework 4.5: string sResponse = await SendRequest(server, port, method, data);
<?xml version="1.0"?> double dResponse = double.Parse(sResponse);
<configuration> TextBox3.Text = dResponse.ToString("F2");
<system.web> }
<compilation debug="false" targetFramework="4.5" /> catch (Exception ex) {
<httpRuntime targetFramework="4.5" /> TextBox3.Text = ex.Message;
</system.web> }
</configuration>
Even though the code for the Web application is essentially the
Next, I launched Notepad with administrative privileges. When same as the code for the WinForm application, the calling mech-
creating simple ASP.NET applications, I sometimes prefer using anism is quite a bit different. When a user makes a request using
Notepad instead of Visual Studio so I can keep all application code the WinForm, the WinForm issues the call directly to the service
in a single .aspx file, rather than generating multiple files and un- and the service responds directly to the WinForm. When a user
wanted example code. I saved the empty file as DemoWebClient. makes a request from the Web application, the Web application
aspx at C:\inetpub\wwwroot\DemoClient. sends the request information to the Web server that’s hosting
The overall structure of the Web application is shown in Figure 7. the application, the Web server makes the call to the service, the
At the top of the page I added Import statements to bring the service responds to the Web server, the Web server constructs a
relevant .NET namespaces into scope, and a Page directive that response page that includes the response and the response page is
includes the Async=true attribute. sent back to the client browser.
The C# script region contains two methods, SendRequest
and Button1_Click. The application page body has two TextBox Wrapping Up
controls and one Button control for input, an output TextBox con- So, when should you consider using asynchronous TCP sockets
trol to hold the service response, and a dummy, unused TextBox instead of WCF? Roughly 10 years ago, before the creation of WCF
Figure 7 Web Application Demo Client Structure
and its predecessor technology ASP.NET Web Services, if you wanted
to create a client-server system, using sockets was often the most log-
<%@ Page Language="C#" Async="true" AutoEventWireup="true"%> ical option. The introduction of WCF was a big advance, but because
<%@ Import Namespace="System.Threading.Tasks" %>
<%@ Import Namespace="System.Net" %> of the huge number of scenarios WCF is designed to handle, using it
<%@ Import Namespace="System.Net.Sockets" %> for simple client-server systems might be overkill in some situations.
<%@ Import Namespace="System.IO" %>
Although the latest version of WCF is easier to configure than
<script runat="server" language="C#"> previous versions, it can still be tricky to work with WCF.
private static async Task<string> SendRequest(string server,
private async void Button1_Click(object sender, System.EventArgs e) { . . } For situations where the client and server are on different networks,
</script> making security a major consideration, I always use WCF. But for
<head> many client-server systems where client and server are located on a
<title>Demo</title> single secure enterprise network, I often prefer using TCP sockets.
</head>
A relatively new approach for implementing client-server sys-
<body> tems is to use the ASP.NET Web API framework for HTTP-based
<form id="form1" runat="server">
<div> services combined with the ASP.NET SignalR library for asyn-
chronous methods. This approach, in many cases, is simpler to
<p>Enter service method:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox></p> implement than using WCF and avoids many of the low-level
<p>Enter data: details involved with a socket approach. Q
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox></p>
<p><asp:Button Text="Send Request" id="Button1"
runat="server" OnClick="Button1_Click"> </asp:Button> </p>
<p>Response:
DR. JAMES MCCAFFREY works for Microsoft Research in Redmond, Wash. He has
<asp:TextBox ID="TextBox3" runat="server"></asp:TextBox></p> worked on several Microsoft products including Internet Explorer and Bing. He
<p>Dummy responsive control: can be reached at [email protected].
<asp:TextBox ID="TextBox4" runat="server"></asp:TextBox></p>

</div> THANKS to the following technical experts for their advice and for reviewing
</form> this article: Piali Choudhury (MS Research), Stephen Cleary (consultant),
</body>
Adam Eversole (MS Research) Lynn Powers (MS Research) and
</html>
Stephen Toub (Microsoft)
msdnmagazine.com March 2014 33
A S P. N E T M V C 5

A .NET Developer
Primer for Single-Page
Applications
Long Le

A majority of Microsoft .NET Framework developers explosion of different kinds of JavaScript libraries. Part of the trend
have spent most of their professional lives on the server side, coding of moving to the client side is the increasing use of single-page
with C# or Visual Basic .NET when building Web applications. Of applications (SPAs). To say that SPA development is the future
course, JavaScript has been used for simple things such as modal is an extreme understatement. SPAs are how some of the best
windows, validation, AJAX calls and so on. However, JavaScript applications on the Web offer fluid UX and responsiveness, while
(client-side code for the most part) has been leveraged as a utility minimizing payloads (traffic) and round-trips to the server.
language, and applications were largely driven from the server side.
Lately there’s been a huge trend of Web application code
migrating from the server side to the client side (browser) to meet To say that SPA development
users’ expectations for fluid and responsive UX. With this being
the case, a lot of .NET developers (especially in the enterprise) are is the future is an extreme
dealing with an extreme amount of anxiety about JavaScript best
practices, architecture, unit testing, maintainability and the recent understatement.
This article discusses:
• Steps to convert an ASP.NET MVC 5 application to a single-page In this article, I’ll address the anxieties you might experience when
application (SPA) making the transition from the server side into the SPA realm. The
• Setting up an SPA infrastructure best way to deal with these anxieties is to embrace JavaScript as a
• Adding create, read, update and delete functionality
first-class language just like any .NET language, such as C#, Visual
Basic .NET, Python and so on.
Technologies discussed:
Following are some fundamental principles of .NET development
ASP.NET MVC 5, JavaScript, Single-Page Applications, Kendo UI, that are sometimes ignored or forgotten when developing apps
RequireJS, Entity Framework, Web API, OData in JavaScript:
Code download available at: • Your code base is manageable in .NET because you’re
easyspa.codeplex.com
decisive with class boundaries and where classes actually
live within your projects.
34 msdn magazine
• You separate concerns, so you don’t 7. Update the layout navigation (menu)
have classes that are responsible for links to match the new SPA-friendly
hundreds of different things with URLs (Northwind.Web/Views/Shared/
overlapping responsibilities. _Layout.cshtml).
• You have reusable repositories, queries, After these seven steps have been carried
entities (models) and data sources. out, your Web application project structure
• You put some thought into naming should look something like Figure 1.
your classes and files so they’re I’ll show how to build an awesome SPA
more meaningful. in ASP.NET MVC with the following
• You practice good use of design patterns, JavaScript libraries, available via NuGet:
coding conventions and organization. • RequireJS (requirejs.org): This is a Java-
Because this article is for .NET developers Script file and module loader. RequireJS
who are being introduced to the SPA world, will provide #include/import/require
I’ll incorporate the least number of frame- APIs and the ability to load nested
works possible to build a manageable SPA dependencies with dependency
with sound architecture. injection (DI). The RequireJS design
approach uses the Asynchronous
Creating an SPA in Seven Key Steps Module Definition (AMD) API for
Following are seven key steps to convert a JavaScript modules, which helps to
new ASP.NET Web Application that was encapsulate pieces of code into useful
created with the out-of-the-box Visual units. It also provides an intuitive way to
Studio 2013 ASP.NET MVC template into refer to other units of code (modules).
an SPA (with references to the appropriate RequireJS modules also follow the
project files that can be found in the accom- module pattern (bit.ly/18byc2Q). A simpli-
panying code download). fied implementation of this pattern uses
1. Download and install the NuGet JavaScript functions for encapsulation.
packages RequireJS, RequireJS text You’ll see this pattern in action later as
plug-in and Kendo UI Web. Figure 1 ASP.NET MVC Project Structure all JavaScript modules will be wrapped
2. Add a configuration module within a “define” or “require” function.
(Northwind.Web/Scripts/app/main.js). Those familiar with DI and Inversion of Control (IoC)
3. Add an app module (Northwind.Web/Scripts/app/app.js). concepts can think of this as a client-side DI framework. If that’s
4. Add a router module (Northwind.Web/Scripts/app/router.js). as clear as mud at the moment, no worries—I’ll soon get into
5. Add an action and view both named Spa (Northwind. some coded illustrations where all this will make sense.
Web/Controllers/HomeController.cs and Northwind. • Text plug-in for RequireJS (bit.ly/1cd8lTZ): This will be used to
Web/Views/Home/Spa.cshtml). remotely load chunks of HTML (views) into the SPA.
6. Modify the _ViewStart.cshtml file so MVC will load • Entity Framework (bit.ly/1bKiZ9I): This is pretty self-explanatory,
views without using the _Layout.cshtml file by default and because the focus of this article is on SPA, I won’t get too
(Northwind.Web/Views/_ViewStart.cshtml). much into Entity Framework. However, if you’re new to this,
there’s plenty of documentation available.
Figure 2 RequireJS Configuration • Kendo UI Web (bit.ly/t4VkVp): This is a comprehensive JavaScript/
HTML5 framework that encompasses Web UI Widgets, Data-
require.config({
paths: {
Sources, templates, the Model-View-ViewModel (MVVM)
// Packages pattern, SPAs, styling, and so on to help deliver a responsive and
'jquery': '/scripts/jquery-2.0.3.min',
'kendo': '/scripts/kendo/2013.3.1119/kendo.web.min',
adaptive application that will look great.
'text': '/scripts/text',
'router': '/scripts/app/router' Figure 3 Registered Route Definitions and Corresponding URLs
},
shim : { Registered Route (Definition) Actual Full (Bookmarkable) URL
'kendo' : ['jquery']
}, / localhost:25061/home/spa/home/index
priority: ['text', 'router', 'app'],
jquery: '2.0.3',
/home/index localhost:25061/home/spa/#/home/index/
waitSeconds: 30 home/about
});
/home/about localhost:25061/home/spa/#/home/
require([ about/home/contact
'app' /home/contact localhost:25061/home/spa/#/home/
], function (app) {
app.initialize(); contact/customer/index
}); /customer/index localhost:25061/home/spa/#/customer/index

msdnmagazine.com March 2014 35


Setting up the SPA Infrastructure This view will basically load up the shell or all the HTML for the
To show how to set up the SPA infrastructure, first I’ll explain how layout of the SPA. From this view, I’ll load in the other views (for
to create the RequireJS (config) module (Northwind.Web/Scripts/ example, About.cshtml, Contact.cshtml, Index.cshtml and so on)
app/main.js). This module will be the app start-up entry point. If as the user navigates through the SPA, by swapping the views that
you’ve created a console app, you can think of this as the Main replace all the HTML in the “content” div.
entry point in Program.cs. It basically contains the first class and Creating the SPA Landing Page (Layout) (Northwind.Web/
the method that’s called when the SPA starts up. The main.js file Views/Spa.cshtml) Because the Spa.cshtml view is the SPA’s
basically serves as the SPA’s manifest and is where you’ll define landing page where you’ll load in all your other views, there won’t
where all things in the SPA are and their dependencies, if any. The be much markup here, other than referencing the required style
code for RequireJS configuration is shown in Figure 2. sheets and RequireJS. Note the data-main attribute in the following
code, which tells RequireJS which module to load first:

You have two options


@{
ViewBag.Title = "Spa";
Layout = "~/Views/Shared/_Layout.cshtml";

for SPA views that will be loaded


}

<link href=

into the SPA: standard HTML


"~/Content/kendo/2013.3.1119/kendo.common.min.css" rel="stylesheet" />
<link href=
"~/Content/kendo/2013.3.1119/kendo.bootstrap.min.css" rel="stylesheet" />

(*.html) or ASP.NET MVC Razor


<script src=
"@Url.Content("~/scripts/require.js")"
data-main="/scripts/app/main"></script>

(*.cshtml) pages.
<div id="app"></div>
Adding an Action for the SPA Layout (Northwind.Web/
Controllers/HomeController.cs) To create and load the Spa.cshtml
In Figure 2, the paths property contains a list of where all the view, add an action and view:
modules are located and their names. Shim is the name of a module public ActionResult Spa()
{
defined previously. The shim property includes any dependencies return View();
the module may have. In this case, you’re loading a module named }

kendo and it has a dependency on a module Create the Application Module


named jquery, so if a module requires the (Northwind.Web/Scripts/app/app.js)
kendo module, go ahead and load jQuery Here’s the Application module, respon-
first, because jQuery has been defined as a sible for initializing and starting the
dependency for the kendo module. Kendo UI Router:
define([
In Figure 2 , the code “require([], 'router'
function(){})” will load in the next module, ], function (router) {
var initialize = function() {
which is the module I named app. Note router.start();
that I’ve deliberately given meaningful };
names to modules. return {
So, how does your SPA know to invoke initialize: initialize
};
this module first? You configure this on });
the first landing page in the SPA with the Create the Router Module (North-
data-main attribute in the script reference wind.Web/Scripts/app/router.js) This
tag for RequireJS. I’ve specified that it run is called by app.js. If you’re already familiar
the main module (main.js). RequireJS will with ASP.NET MVC routes, it’s the same
handle all the heavy lifting involved in notion here. These are the SPA routes for
loading this module; you just have to tell your views. I’ll define all the routes for all
it which module to load first. the SPA views so when the user navigates
You have two options for SPA views that through the SPA, the Kendo UI router will
will be loaded into the SPA: standard HTML know what views to load into the SPA. See
(*.html) or ASP.NET MVC Razor (*.cshtml) Listing 1 in the accompanying download.
pages. Because this article is intended for .NET The Kendo UI Router class is responsible
developers—and a lot of enterprises have for tracking the application state and nav-
server-side libraries and frameworks they’d igating between the application states. The
like to continue using in their views—I’ll go router integrates into the browser history
with the latter option of creating Razor views. using the fragment part of the URL (#page),
I’ll start off by adding a view and name making the application states bookmark-
it Spa.cshtml, as mentioned previously. Figure 4 A Best-Practice Solution Structure able and linkable. When a routable URL
36 msdn magazine ASP.NET MVC 5
Alexsys Team®ŽīĞƌƐŇĞdžŝďůĞƚĂƐŬŵĂŶĂŐĞŵĞŶƚƐŽŌǁĂƌĞĨŽƌtŝŶĚŽǁƐ͕tĞď͕ĂŶĚDŽďŝůĞĞǀŝĐĞƐ͘
dƌĂĐŬǁŚĂƚĞǀĞƌLJŽƵŶĞĞĚƚŽŐĞƚƚŚĞũŽďĚŽŶĞͲĂŶLJǁŚĞƌĞ͕ĂŶLJƟŵĞŽŶƉƌĂĐƟĐĂůůLJĂŶLJĚĞǀŝĐĞ͊

dŚŽƵƐĂŶĚƐĂƌĞƵƐŝŶŐůĞdžƐLJƐdĞĂŵΠƚŽĐƌĞĂƚĞǁŽƌŬŇŽǁƐŽůƵƟŽŶƐĂŶĚǁĞďĂƉƉƐͲǁŝƚŚŽƵƚĐŽĚŝŶŐ͊
&ŽƌƚƵŶĞ ϱϬϬ ĐŽŵƉĂŶŝĞƐ͕ ƐƚĂƚĞ͕ ůŽĐĂů͕ K͕ ĂŶĚ ŽƚŚĞƌ ĨĞĚĞƌĂů ĂŐĞŶĐŝĞƐ ƵƐĞ dĞĂŵ ƚŽ ŵĂŶĂŐĞ ƚŚĞŝƌ ƚĂƐŬƐ͘
ĂƐŝůLJƚĂŝůŽƌdĞĂŵƚŽŵĞĞƚLJŽƵƌĞdžĂĐƚƌĞƋƵŝƌĞŵĞŶƚƐͲĞǀĞŶŝĨƚŚĞLJĐŚĂŶŐĞĚĂŝůLJ͕ǁĞĞŬůLJ͕ŽƌĞǀĞŶĞǀĞƌLJŵŝŶƵƚĞ͘

Alexsys Team® Features Include: KƵƌƌĞŶŽǁŶĞĚƚĞĐŚƐƵƉƉŽƌƚƚĞĂŵŝƐŚĞƌĞƚŽ


Ͳ&ŽƌŵĂŶĚĂƚĂďĂƐĞĐƵƐƚŽŵŝnjĂƟŽŶ ŵĂŬĞLJŽƵĂŶĚLJŽƵƌƚĞĂŵĂƐƵĐĐĞƐƐ͘ŽŶ͛ƚ
ͲƵƐƚŽŵǁŽƌŬŇŽǁƐ ŚĂǀĞ ĞŶŽƵŐŚ ƌĞƐŽƵƌĐĞƐ͍ KƵƌ ƉƌŽĨĞƐƐŝŽŶĂů
ͲZŽůĞͲďĂƐĞĚƐĞĐƵƌŝƚLJ ƐĞƌǀŝĐĞƐ ƐƚĂī ŚĂƐ ŚĞůƉĞĚ ĐŽŵƉĂŶŝĞƐ ůĂƌŐĞ
ͲĚĂƉƟǀĞZƵůĞƐŶŐŝŶĞ ĂŶĚƐŵĂůůƵƐĞůĞdžƐLJƐdĞĂŵΠĂƚĂĨƌĂĐƟŽŶ
ͲKĐĂƌĚƐƵƉƉŽƌƚ ŽĨƚŚĞĐŽƐƚŽĨƚŚŽƐĞďŝŐĐŽŶƐƵůƟŶŐĮƌŵƐ͘
ͲdŝŵĞƌĞĐŽƌĚŝŶŐ
ͲƵƚŽŵĂƚĞĚƐĐĂůĂƟŽŶƐ͕ &ŝŶĚŽƵƚLJŽƵƌƐĞůĨ͗
>AF<GMLEGJ=
EŽƟĮĐĂƟŽŶƐ͕ĂŶĚ^KWDĞƐƐĂŐŝŶŐ Free Trial and Single User FreePack™
Ͳ^ƵƉƉŽƌƚƐD^Ͳ^Y>͕DLJ^Y>͕ĂŶĚ available at Alexcorp.com ϭͲϴϴϴͲϴϴϬͲ>y;ϮϱϯϵͿ
KƌĐĂůĞĚĂƚĂďĂƐĞƐ >yKZW͘KD

Untitled-1 1 7/15/13 10:18 AM


Figure 5 Customer Grid View Markup with an MVVM Widget layout works the same exact way. You swap out the view (content)
and Event Bindings of the Kendo UI layout using the showIn method. View contents
<div class="demo-section">
(HTML) will be placed in the div with the ID “content,” which
<div class="k-content" style="width: 100%"> was passed into the Kendo UI layout when it was initialized. After
<div id="grid"
data-role="grid"
initializing the layout, you then render it inside the div with the ID
data-sortable="true" “app,” which is a div in the landing page (Northwind.Web/Views/
data-pageable="true"
data-filterable="true"
Home/Spa.cshtml). I’ll review that shortly.
data-editable="inline"

The Kendo UI Router class is


data-selectable="true"
data-toolbar='[ { template: kendo.template($("#toolbar").html()) } ]'
data-columns='[

responsible for tracking the


{ field: "CustomerID", title: "ID", width: "75px" },
{ field: "CompanyName", title: "Company"},
{ field: "ContactName", title: "Contact" },

application state and navigating


{ field: "ContactTitle", title: "Title" },
{ field: "Address" },
{ field: "City" },

between the application states.


{ field: "PostalCode" },
{ field: "Country" },
{ field: "Phone" },
{ field: "Fax" } ]'
data-bind="source: dataSource, events:
{ change: onChange, dataBound: onDataBound }"> The loadView helper method takes in a view model, a view and—if
</div>
<style scoped>
needed—a callback to invoke once the view and view model binding
#grid .k-toolbar { takes place. Within the loadView method, you leverage the Kendo
padding: 15px;
}
UI FX library to aesthetically enhance the UX by adding some
simple animation to the view swapping process. This is done by
.toolbar {
float: right;
sliding the current loaded view to the left, remotely loading in the
} new view and then sliding the new loaded view back to the center.
</style>
</div>
Obviously, you can easily change this to a variety of different
</div> animations using the Kendo UI FX library. One of the key benefits
<script type="text/x-kendo-template" id="toolbar">
of using the Kendo UI layout is shown when you invoke the show-
<div> In method to swap out views. It will ensure the view is unloaded,
<div class="toolbar">
<span data-role="button" data-bind="click: edit">
destroyed properly and removed from the browser’s DOM, thus
<span class="k-icon k-i-tick"></span>Edit</span> ensuring the SPA can scale and is performant.
<span data-role="button" data-bind="click: destroy">
<span class="k-icon k-i-tick"></span>Delete</span>
<span data-role="button" data-bind="click: details">
Figure 6 Customer Web API OData Controller
<span class="k-icon k-i-tick"></span>Edit Details</span>
</div> public class CustomerController : EntitySetController<Customer, string>
<div class="toolbar" style="display:none"> {
<span data-role="button" data-bind="click: save"> private readonly NorthwindContext _northwindContext;
<span class="k-icon k-i-tick"></span>Save</span>
<span data-role="button" data-bind="click: cancel"> public CustomerController()
<span class="k-icon k-i-tick"></span>Cancel</span> {
</div> _northwindContext = new NorthwindContext();
</div> }
</script>
public override IQueryable<Customer> Get()
{
return _northwindContext.Customers;
is clicked, the router kicks in and tells the application to put itself }
back into the state that was encoded into the route. The route defi-
protected override Customer GetEntityByKey(string key)
nition is a string representing a path used to identify the state of {
the application the user wants to see. When a route definition is return _northwindContext.Customers.Find(key);
}
matched from the browser’s URL hash fragment, the route handler
is called (see Figure 3). protected override Customer UpdateEntity(string key, Customer update)
{
As for the Kendo UI layout widget, its name speaks for itself. _northwindContext.Customers.AddOrUpdate(update);
You’re probably familiar with the ASP.NET Web Forms MasterPage _northwindContext.SaveChanges();
return update;
or MVC layout included in the project when you create a new }
ASP.NET MVC Web Application. In this SPA project, it’s located
public override void Delete(string key)
at the path Northwind.Web/Views/Shared/_Layout.cshtml. There’s {
little difference between the Kendo UI layout and MVC layout, var customer = _northwindContext.Customers.Find(key);
_northwindContext.Customers.Remove(customer);
except the Kendo UI layout runs on the client side. Just as the lay- _northwindContext.SaveChanges();
out worked on the server side, where the MVC runtime would }
}
swap out the content of the layout with other views, the Kendo UI

38 msdn magazine ASP.NET MVC 5


There are many .NET ‘Rich Text Editors’ out there...

Meh, sorry!
There is only one

5HSRUWLQJŁ5LFK7H[W(GLWLQJŁ6SHOO&KHFNLQJŁ%DUFRGHV

www.textcontrol.com
US: +1 855-533-TEXT
/txtextcontrol
EU: +49 421 427 067-10

Untitled-5 1 2/4/14 11:43 AM


Figure 7 Configuring ASP.NET MVC Web API Routes for OData Now that the SPA is up and running, I’ll go ahead and do what
public static void Register(HttpConfiguration config)
most developers will end up doing with an SPA, which is adding
{ some create, read, update and delete (CRUD) functionality.
// Web API configuration and services

ODataModelBuilder modelBuilder = new ODataConventionModelBuilder(); Adding CRUD Functionality to the SPA


var customerEntitySetConfiguration =
Here are the key steps needed to add a Customer grid view to the
modelBuilder.EntitySet<Customer>("Customer"); SPA (and the related project code files):
customerEntitySetConfiguration.EntityType.Ignore(t => t.Orders);
• Add a CustomerController MVC controller
customerEntitySetConfiguration.EntityType.Ignore(t => (Northwind.Web/Controllers/CustomerController.cs).
t.CustomerDemographics);
• Add a REST OData Customer Web API controller
var model = modelBuilder.GetEdmModel(); (Northwind.Web/Api/CustomerController.cs).
config.Routes.MapODataRoute("ODataRoute", "odata", model);
• Add a Customer grid view (Northwind.Web/Views/
config.EnableQuerySupport(); Customer/Index.cshtml).
// Web API routes

There’s little difference between


config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(

the Kendo UI layout and MVC


"DefaultApi", "api/{controller}/{id}",
new {id = RouteParameter.Optional});
}

layout, except the Kendo UI


Edit the _ViewStart.cshtml View (Northwind.Web/Views/
_ViewStart.cshtml) Here’s how to configure all views to not use layout runs on the client side.
the ASP.NET MVC layout by default:
@{
Layout = null;
• Add a CustomerModel module (Northwind.Web/Scripts/
} app/models/CustomerModel).
At this point, the SPA should be working. When clicking on • Add a customerDatasource module for the Customer
any of the navigation links on the menu, you see the current grid (Northwind.Web/Scripts/app/datasources/
content is being swapped out via AJAX thanks to the Kendo UI customerDatasource.js).
router and RequireJS. • Add an indexViewModel module for the Customer
These seven steps needed to convert a fresh ASP.NET Web grid view (Northwind.Web/Scripts/app/viewModels/
Application into an SPA aren’t too bad, are they? indexViewModel.js).
Setting Up the Solution Struc-
ture with Entity Framework Figure
4 shows the solution structure,
highlighting three projects: North-
wind.Data (1), Northwind.Entity
(2) and Northwind.Web (3). I’ll
briefly discuss each, along with
Entity Framework Power Tools.
• Northwind.Data: This
includes everything related
to the Entity Framework
Object-Relational Mapping
(ORM) tool, for persistence.
• Northwind.Entity: This
includes domain entities,
composed of Plain Old CLR
Object (POCO) classes. These
are all the persistent-ignorant
domain objects.
• Northwind.Web: This includes
the ASP.NET MVC 5 Web
Application, the presentation
layer, where you’ll build out
Figure 8 Querying the Customer Controller Web API OData Via a LINQPad Query the SPA with two previously
40 msdn magazine ASP.NET MVC 5
mentioned libraries—Kendo UI and RequireJS—and the rest of Figure 10 The Customer Grid View Model
the server-side stack: Entity Framework, Web API and OData. define(['kendo', 'customerDatasource'],
• Entity Framework Power Tools: To create all the POCO function (kendo, customerDatasource) {
var lastSelectedDataItem = null;
entities and mappings (database-first), I used the Entity
Framework Power Tools from the Entity Framework var onClick = function (event, delegate) {
event.preventDefault();
team (bit.ly/1cdobhk). After the code generation, all I did var grid = $("#grid").data("kendoGrid");
here is simply copy the entities into a separate project var selectedRow = grid.select();
var dataItem = grid.dataItem(selectedRow);
(Northwind.Entity) to address separation concerns. if (selectedRow.length > 0)
Note: Both the Northwind SQL install script and a backup of the delegate(grid, selectedRow, dataItem);
else
database are included in the downloadable source code under the alert("Please select a row.");
Northwind.Web/App_Data folder (bit.ly/1cph5qc). };
Now that the solution is set up to access the database, I’ll go ahead var indexViewModel = new kendo.data.ObservableObject({
and write the MVC CustomerController.cs class to serve up the
save: function (event) {
index and edit views. Because the controller’s only responsibility is to onClick(event, function (grid) {
serve up an HTML view for the SPA, the code here will be minimal. grid.saveRow();
Creating MVC Customer Controller (Northwind.Web/ $(".toolbar").toggle();
});
Controllers/CustomerController.cs) Here’s how to create the },
Customer controller with the actions for the index and edit views: cancel: function (event) {
public class CustomerController : Controller onClick(event, function (grid) {
{ grid.cancelRow();
public ActionResult Index() $(".toolbar").toggle();
{ });
return View(); },
}
details: function (event) {
public ActionResult Edit() onClick(event, function (grid, row, dataItem) {
{ router.navigate('/customer/edit/' + dataItem.CustomerID);
return View(); });
} },
}
Creating the View with the Customers Grid (Northwind.Web/ edit: function (event) {
onClick(event, function (grid, row) {
Views/Customers/Index.cshtml) Figure 5 shows how to create grid.editRow(row);
the view with the Customers grid. $(".toolbar").toggle();
});
If the markup in Figure 5 isn’t familiar, don’t panic—it’s just the },
Kendo UI MVVM (HTML) markup. It simply configures an HTML
destroy: function (event) {
element, in this case the div with an ID of “grid.” Later on when onClick(event, function (grid, row, dataItem) {
you bind this view to a view model with the Kendo UI MVVM grid.dataSource.remove(dataItem);
grid.dataSource.sync();
framework, this markup will be converted to Kendo UI widgets. });
You can read more on this at bit.ly/1d2Bgfj. },

Creating MVC (OData) Web API Customer Controller (North- onChange: function (arg) {
wind.Web/Api/CustomerController.cs) Now I’ll show how to var grid = arg.sender;
lastSelectedDataItem = grid.dataItem(grid.select());
create the MVC (OData) Web API Customer controller. OData is a },
data-access protocol for the Web that provides a uniform way to query
dataSource: customerDatasource,

Figure 9 Creating the Customer (Kendo UI Observable) Model onDataBound: function (arg) {
// Check if a row was selected
define(['kendo'], if (lastSelectedDataItem == null) return;
function (kendo) { // Get all the rows
var customerModel = kendo.data.Model.define({ var view = this.dataSource.view();
id: "CustomerID", // Iterate through rows
fields: { for (var i = 0; i < view.length; i++) {
CustomerID: { type: "string", editable: false, nullable: false }, // Find row with the lastSelectedProduct
CompanyName: { title: "Company", type: "string" }, if (view[i].CustomerID == lastSelectedDataItem.CustomerID) {
ContactName: { title: "Contact", type: "string" }, // Get the grid
ContactTitle: { title: "Title", type: "string" }, var grid = arg.sender;
Address: { type: "string" }, // Set the selected row
City: { type: "string" }, grid.select(grid.table.find("tr[data-uid='" + view[i].uid + "']"));
PostalCode: { type: "string" }, break;
Country: { type: "string" }, }
Phone: { type: "string" }, }
Fax: { type: "string" }, },
State: { type: "string" } });
}
}); return indexViewModel;
return customerModel;
}); });

msdnmagazine.com March 2014 41


Figure 11 RequireJS Configuration Additions Querying OData Web API with LINQPad If you haven’t used
paths: {
LINQPad (linqpad.net) yet, please add this tool to your developer
// Packages toolkit; it’s a must-have and is available in a free version. Figure 8
'jquery': '/scripts/jquery-2.0.3.min',
'kendo': '/scripts/kendo/2013.3.1119/kendo.web.min',
shows LINQPad with a connection to the Web API OData
'text': '/scripts/text', (localhost:2501/odata), displaying the results of the LINQ query,
'router': '/scripts/app/router',
// Models
“Customer.Take (100).”
'customerModel': '/scripts/app/models/customerModel', Creating the (Observable) Customer Model (Northwind.Web/
// View models
'customer-indexViewModel': '/scripts/app/viewmodels/customer/indexViewModel',
Scripts/app/models/customerModel.js) Next is creating the
'customer-editViewModel': '/scripts/app/viewmodels/customer/editViewModel', (Kendo UI Observable) Customer model. You can think of this
// Data sources
'customerDatasource': '/scripts/app/datasources/customerDatasource',
as a client-side Customer entity domain model. I created the
// Utils Customer model so it can easily be reused by both the Customer
'util': '/scripts/util'
}
grid view and the edit view. The code is shown in Figure 9.

and manipulate data sets through CRUD operations. Using ASP.NET OData is a data-access protocol
for the Web that provides a
Web API, it’s easy to create an OData endpoint. You can control which
OData operations are exposed. You can host multiple OData endpoints

uniform way to query and


alongside non-OData endpoints. You have full control over your data
model, back-end business logic and data layer. Figure 6 shows the

manipulate data sets through


code for the Customer Web API OData controller.
The code in Figure 6 just creates an OData Web API controller

CRUD operations.
to expose Customer data from the Northwind database. Once this
is created, you can run the project, and with tools such as Fiddler
(a free Web debugger at fiddler2.com) or LINQPad, you can actually
query customer data. Creating a DataSource for the Customers Grid (North-
Configuring and Exposing OData from the Customer Table for wind.Web/Scripts/app/datasources/customersDatasource.js)
the Grid (Northwind.Web/App_Start/WebApiConfig.cs) Figure If you’re familiar with data sources from ASP.NET Web Forms,
7 configures and exposes OData from the Customer table for the grid. the concept is the same here, where you create a data source for
the Customers grid (Northwind.Web/
Scripts/app/datasources/customers-
Datasource.js). The Kendo UI Data-
Source (bit.ly/1d0Ycvd) component is an
abstraction for using local (arrays of
JavaScript objects) or remote (XML,
JSON or JSONP) data. It fully supports
CRUD data operations and provides
both local and server-side support
for sorting, paging, filtering, grouping
and aggregates.
Creating the View Model for the
Customers Grid View If you’re familiar
with MVVM from Windows Presenta-
tion Foundation (WPF) or Silverlight,
this is the same exact concept, just on
the client side (found in this project in
Northwind.Web/Scripts/ViewModels/
Customer/indexViewModel.cs). MVVM
is an architectural separation pattern
used to separate the view and its data
and business logic. You’ll see in a bit
that all the data, business logic and so
on is in the view model and that the
view is purely HTML (presentation).
Figure 10 shows the code for the
Figure 12 The Customer Grid View with MVVM Using the Index View Model Customer grid view.

42 msdn magazine ASP.NET MVC 5


The fastest rendering data visualization components
for WPF and WinForms...

LightningChart

HEAVY-DUTY DATA VISUALIZATION TOOLS FOR SCIENCE, ENGINEERING AND TRADING

WPF charts performance comparison


?Entirely DirectX GPU accelerated
Opening large dataset LightningChart is up to 977,000 % faster ?Superior 2D and 3D rendering performance
Real-time monitoring LightningChart is up to 2,700,000 % faster
?Optimized for real-time data monitoring
Winforms charts performance comparison ?Touch-enabled operations
Opening large dataset LightningChart is up to 37,000 % faster ?Supports gigantic data sets
Real-time monitoring LightningChart is up to 2,300,000 % faster ?On-line and off-line maps
Results compared to average of other chart controls. See details at
?Great customer support
www.LightningChart.com/benchmark. LightningChart results apply for Ultimate edition.
?Compatible with Visual Studio 2005...2013

Download a free 30-day evaluation from


www.LightningChart.com Pioneers of high-performance data visualization

Untitled-1 1 1/29/14 10:10 AM


Figure 13 Edit View Markup with an MVVM Widget and Event Binding
<div class="demo-section"> </dt>
<div class="k-block" style="padding: 20px"> <dd>
<div class="k-block k-info-colored"> <input id="country" type="text"
<strong>Note: </strong>Please fill out all of the fields in this form. data-bind="value: Customer.Country" class="k-textbox" />
</div> </dd>
<div> <dt>
<dl> <label for="phone">Phone:</label>
<dt> </dt>
<label for="companyName">Company Name:</label> <dd>
</dt> <input id="phone" type="text"
<dd> data-bind="value: Customer.Phone" class="k-textbox" />
<input id="companyName" type="text" </dd>
data-bind="value: Customer.CompanyName" class="k-textbox" /> <dt>
</dd> <label for="fax">Fax:</label>
<dt> </dt>
<label for="contactName">Contact:</label> <dd>
</dt> <input id="fax" type="text"
<dd> data-bind="value: Customer.Fax" class="k-textbox" />
<input id="contactName" type="text" </dd>
data-bind="value: Customer.ContactName" class="k-textbox" /> </dl>
</dd>
<dt> <button data-role="button"
<label for="title">Title:</label> data-bind="click: saveCustomer"
</dt> data-sprite-css-class="k-icon k-i-tick">Save</button>
<dd> <button data-role="button" data-bind="click: cancel">Cancel</button>
<input id="title" type="text"
<style scoped>
data-bind="value: Customer.ContactTitle" class="k-textbox" />
dd
</dd>
{
<dt>
margin: 0px 0px 20px 0px;
<label for="address">Address:</label>
width: 100%;
</dt>
}
<dd>
<input id="address" type="text" label
data-bind="value: Customer.Address" class="k-textbox" /> {
</dd> font-size: small;
<dt> font-weight: normal;
<label for="city">City:</label> }
</dt>
<dd> .k-textbox
<input id="city" type="text" {
data-bind="value: Customer.City" class="k-textbox" /> width: 100%;
</dd> }
<dt>
<label for="zip">Zip:</label> .k-info-colored
</dt> {
<dd> padding: 10px;
<input id="zip" type="text" margin: 10px;
data-bind="value: Customer.PostalCode" class="k-textbox" /> }
</dd> </style>
<dt> </div>
<label for="country">Country:</label> </div>
</div>

I’ll briefly describe various components of the code in Figure 10: Now add customerModel, indexViewModel and customersData-
• onClick (helper): This method is a helper function, which source modules to your RequireJS configuration (Northwind.Web/
gets an instance of the Customer grid, the current selected Scripts/app/main.js). The code is shown in Figure 11.
row and a JSON model of the representation of the
Customer for the selected row. Figure 14 The Utility Module
• save: This saves changes when doing an inline edit define([],
of a Customer. function () {

• cancel: This cancels out of inline edit mode. var util;


• details: This navigates the SPA to the edit Customer view,
util = {
appending the Customer’s ID to the URL.
• edit: This activates inline editing for the current getId:
function () {
selected Customer. var array = window.location.href.split('/');
• destroy: This deletes the current selected Customer. var id = array[array.length - 1];
return id;
• onChange (event): This fires every time a Customer is }
selected. You store the last selected Customer so you can };

maintain state. After performing any updates or navigating return util;


away from the Customer grid, when navigating back to the
});
grid you reselect the last selected Customer.
44 msdn magazine ASP.NET MVC 5
 
     
 

 %! &    '   !  ( 



    

       




 
   
  
    
 
 !      

  
      
 
"   #$$%
"  

!  


  
&

'    (
#$$%

)  *
*

* 
  
!+ 
'&

, ( -./  

      ! " 


01!&1 1


 
01!&2(1

  
01!&3

   
01!&"  1 
 4

#    $


( 


  4 
 

  ,-.,/  !"#$%#&'&(&


    
 01  ,-.,/ ) **!#%+""&(&#+

Untitled-1 1 2/7/14 11:02 AM


Figure 15 RequireJS Configuration Additions that at bit.ly/1f3zWuC. Figure 13 shows the edit view markup with an
for the Customer Edit Modules MVVM widget and event binding.
require.config({
Create a Utility to Get the ID of the Customer from the URL
paths: { Because you’re creating concise modules with clean boundaries
// Packages
'jquery': '/scripts/jquery-2.0.3.min',
to create a nice separation of concerns, I’ll demonstrate how to
'kendo': '/scripts/kendo/2013.3.1119/kendo.web.min', create a Util module where all of your utility helpers will reside.
'text': '/scripts/text',
'router': '/scripts/app/router',
I’ll start with a utility method that can retrieve the customer ID
// Models in the URL for the Customer DataSource (Northwind.Web/
'customerModel': '/scripts/app/models/customerModel',
// View models
Scripts/app/datasources/customerDatasource.js), as shown
'customer-indexViewModel': '/scripts/app/viewmodels/customer/indexViewModel', in Figure 14.
'customer-editViewModel': '/scripts/app/viewmodels/customer/editViewModel',
// Data sources
Add the Edit View Model and Util Modules to the RequireJS
'customerDatasource': '/scripts/app/datasources/customerDatasource', Configuration (Northwind.Web/Scripts/app/main.js) The
// Utils
'util': '/scripts/util'
code in Figure 15 shows RequireJS configuration additions for
}, the Customer edit modules.
shim : {
'kendo' : ['jquery']
Add the Customer Edit View Model (Northwind.Web/Scripts/
}, app/viewModels/editViewModel.js) The code in Figure 16 shows
priority: ['text', 'router', 'app'],
jquery: '2.0.3',
how to add a Customer edit view model.
waitSeconds: 30 I’ll briefly describe various components of the code in Figure 16:
});
• saveCustomer: This method is responsible for saving any
require([ changes on the Customer. It also resets the DataSource’s
'app'
], function (app) {
filter so the grid will be hydrated with all Customers.
app.initialize(); • cancel: This method will navigate the SPA back to the
});
Customer grid view. It also resets the DataSource’s filter
so that the grid will be hydrated with all Customers.
Add a Route for the New Customers Grid View Note that in • filter: This invokes the DataSource’s filter method and
the loadView callback (in Northwind.Web/Scripts/app/router.js) queries for a specific Customer by the ID that’s in the URL.
you’re binding the toolbar of the grid after it has been initialized
and MVVM binding has taken place. This is because the first time Figure 16 Customer Edit View Model Module
you bind your grid, the toolbar hasn’t initialized, because it exists in for the Customer View
the grid. When the grid is first initialized via MVVM, it will load
define(['customerDatasource', 'customerModel', 'util'],
in the toolbar from the Kendo UI template. When it’s loaded into function (customerDatasource, customerModel, util) {
the grid, you then bind only the toolbar to your view model so the
var editViewModel = new kendo.data.ObservableObject({
buttons in your toolbar are bound to the save and cancel methods
in your view model. Here’s the relevant code to register the route loadData: function () {
var viewModel = new kendo.data.ObservableObject({
definition for the Customer edit view: saveCustomer: function (s) {
router.route("/customer/index", function () { customerDatasource.sync();
require(['customer-indexViewModel', 'text!/customer/index'],
function (viewModel, view) { customerDatasource.filter({});
loadView(viewModel, view, function () { router.navigate('/customer/index');
kendo.bind($("#grid").find(".k-grid-toolbar"), viewModel); },
}); cancel: function (s) {
}); customerDatasource.filter({});
}); router.navigate('/customer/index');
}
You now have a fully functional Customers grid view. Load up });
localhost:25061/Home/Spa#/customer/index (the port number will
customerDatasource.filter({
likely vary on your machine) in a browser and you’ll see Figure 12. field: "CustomerID",
Wiring Up the Customers Edit View Here are the key steps to operator: "equals",
value: util.getId()
add a Customer edit view to the SPA: });
• Create a customer edit view bound to your Customer
customerDatasource.fetch(function () {
model via MVVM console.log('editViewModel fetching');
(Northwind.Web/Views/Customer/Edit.cshtml). if (customerDatasource.view().length > 0) {
viewModel.set("Customer", customerDatasource.at(0));
• Add an edit view model module for the Customer } else
edit view (Northwind.Web/Scripts/app/viewModels/ viewModel.set("Customer", new customerModel());
});
editViewModel.js). return viewModel;
• Add a utility helper module to get IDs from the URL },
});
(Northwind.Web/Scripts/app/util.js).
Because you’re using the Kendo UI framework, go ahead and return editViewModel;
});
style your edit view with Kendo UI styles. You can learn more about
46 msdn magazine ASP.NET MVC 5
router.route("/customer/edit/:id",
function () {
require(['customer-editViewModel',
'text!/customer/edit'],
function (viewModel, view) {
loadView(viewModel.loadData(), view);
});
});
Note that when the Customer edit view
model is requested from RequireJS, you’re
able to retrieve the Customer by invoking
the loadData method from the view model.
This way you’re able to load the correct
Figure 17 The Customer Edit View Customer data based on the ID that’s in the
URL each and every time the Customer
edit view is loaded. A route doesn’t have to
be just a hardcoded string. It can also con-
tain parameters, such as a back-end server
router (Ruby on Rails, ASP.NET MVC,
Django and so on). To do this, you name
a route segment with a colon before the
variable name you want.
You can now load the Customer edit view
in the browser (localhost:25061/Home/
Spa#/customer/edit/ANATR) and see the
Figure 18 The Customer Grid View screen depicted in Figure 17.
Note: Although the delete (destroy)
• fetch: This invokes the DataSource’s fetch method after functionality on the Customer grid view has been wired up, when
setting up the filter. In the callback of the fetch, you set the clicking the “Delete” button in the toolbar (see Figure 18), you’ll
Customer property of your view model with the Customer see an exception, as shown in Figure 19.
that was returned from your DataSource fetch, which will This exception is by design, because most Customer IDs are
be used to bind to your Customer edit view. foreign keys in other tables, for example, Orders, Invoices and so
When RequireJS loads a module, code within the “define” method on. You’d have to wire up a cascading delete that would delete all
body will only get invoked once—which is when RequireJS loads records from all tables where Customer ID is a foreign key.
the module—so you expose a method (loadData) in your edit view Although you aren’t able to delete anything, I still wanted to show
model so you have a mechanism to load data after the edit view the steps and code for the delete functionality.
model module has already been loaded (see this in Northwind.Web/ So there you have it. I’ve demonstrated how quick and easy it is
Scripts/app/router.js). to convert an out-of-the-box ASP.NET Web Application into an
Add a Route for the New Customer Edit View (Northwind.Web/ SPA using RequireJS and Kendo UI. Then I showed how easy it is
Scripts/app/router.js) Here’s the relevant code to add the router: to add CRUD-like functionality to the SPA.
You can see a live demo of the project at
bit.ly/1bkMAlK and you can see the CodePlex
project site (and downloadable code) at easys-
pa.codeplex.com.
Happy coding! Q

LONG LE is the principal .NET app/dev architect at CBRE


Inc. and a Telerik/Kendo UI MVP. He spends most of his
time developing frameworks and application blocks, pro-
viding guidance for best practices and patterns and stan-
dardizing the enterprise technology stack. He has been
working with Microsoft technologies for more than 10
years. In his spare time, he enjoys blogging (blog.longle.
net) and playing Call of Duty. You can reach and follow
him on Twitter at twitter.com/LeLong37.

THANKS to the following technical experts for


Figure 19 Expected Exception When Deleting a Customer Due to CustomerID reviewing this article: Derick Bailey (Telerik) and
Foreign Key Referential Integrity Mike Wasson (Microsoft)
msdnmagazine.com March 2014 47
live long
and code
SUPPORTED BY PRODUCED BY

magazine

Untitled-9 2 2/6/14 12:53 PM


CHICAGO 2O14
May 5 – 8 | Chicago Hilton

Register by April 2
This May, developers, software architects,
engineers, and designers will blast off in the
and Save $200!
windy city for four days of unbiased and Use promo code VSLMAR4
cutting-edge education on the Microsoft
Platform. Live long and code with .NET gurus,
launch ideas with industry experts and rub
elbows with Microsoft stars in pre-conference
workshops, 60+ sessions and fun networking Scan the QR code to
register or for more
events – all designed to make you better at your event details.
job. Plus, explore hot topics like Web API,
jQuery, MongoDB, SQL Server Data Tools
and more!

Tracks Include:
³ Visual Studio/.NET Framework

³ Windows Client

³ JavaScript/HTML5 Client

³ ASP.NET

³ Cloud Computing

³ Windows Phone

³ Cross-Platform Mobile Development

³ SharePoint

³ SQL Server

TURN THE PAGE FOR MORE EVENT DETAILS


£

vslive.com/chicago

Untitled-9 3 2/6/14 12:53 PM


AGENDA AT-A-GLANCE
Visual Studio / Windows Cloud Windows Cross-Platform
.NET Framework Client Computing Phone Mobile
Development

"Several of the START TIME END TIME Visual Studio Live! Pre-Conference Workshops: Monday, May 5,
presentations were 7:30 AM 9:00 AM Pre-Conference Workshop Registration

cutting edge – they 9:00 AM 6:00 PM MW01 - Workshop: Modern UX Design - Billy Hollis

would have insider 6:00 PM 9:00 PM Dine-A-Round Dinner


tips that you can’t
easily search for or START TIME END TIME Visual Studio Live! Day 1: Tuesday, May 6, 2014

wouldn’t know to
7:00 AM 8:00 AM Registration
8:00 AM 9:00 AM Keynote: To Be Announced
look for.” 9:15 AM 10:30 AM T01 - What's New in WinRT Development T02 - What's New in the Visual Studio
- Rockford Lhotka 2013 IDE
John Kilic 10:45 AM 12:00 PM T05 - What's New for XAML Windows T06 - ALM with Visual Studio 2013 and Team
Web Application Developer Store Apps - Ben Dewey Foundation Server 2013 - Brian Randell
12:00 PM 1:30 PM Lunch - Visit Exhibitors
Grand Canyon University
1:30 PM 2:45 PM T09 - Interaction Design Principles and T10 - What's New for Web Developers in
Patterns - Billy Hollis Visual Studio this Year? - Mads Kristensen

3:00 PM 4:15 PM T13 - Applying UX Design in XAML - T14 - Why Browser Link Changes Things,
Billy Hollis and How You Can Write Extensions? -
Mads Kristensen
4:15 PM 4:45 PM Networking Break - Visit Exhibitors
4:45 PM 6:00 PM T17 - What's New for HTML/WinJS T18 - Katana, OWIN, and Other Awesome
Windows Store Apps - Ben Dewey Codenames: What's coming? -
Howard Dierking
6:00 PM 7:30 PM Exhibitor Welcome Reception

START TIME END TIME Visual Studio Live! Day 2: Wednesday, May 7, 2014
7:00 AM 8:00 AM Registration
8:00 AM 9:00 AM Keynote: To Be Announced
9:15 AM 10:30 AM W01 - Windows 8 HTML/JS Apps for the W02 - Creating Data-Driven Mobile Web
ASP.NET Developer - Adam Tuliper Apps with ASPNET MVC and jQuery Mobile
- Rachel Appel
10:45 AM 12:00 PM W05 - Developing Awesome 3D W06 - Getting Started with Xamarin -
Applications with Unity and Walt Ritscher
C#/JavaScript - Adam Tuliper
12:00 PM 1:30 PM Birds-of-a-Feather Lunch & Exhibitor RafÁe at 1:15pm MUST be present to win
1:30 PM 2:45 PM W09 - What's New in WPF 4.5 - W10 - Building Multi-Platform Mobile Apps
Walt Ritscher with Push NotiÀcations - Nick Landry

3:00 PM 4:15 PM W13 - Implementing M-V-VM W14 - Getting Started with Windows Phone
(Model-View-View Model) for WPF - Development - Nick Landry
Philip Japikse

4:30 PM 5:45 PM W17 - Build Maintainable Windows Store W18 - Build Your First Mobile App in 1 Hour
Apps with MVVM and Prism - Brian Noyes with Microsoft App Studio - Nick Landry

7:00 PM 9:00 PM Blues after Dark at Buddy Guy’s Legends

START TIME END TIME Visual Studio Live! Day 3: Thursday, May 8, 2014
7:30 AM 8:00 AM Registration
8:00 AM 9:15 AM TH01 - Leveraging Windows Azure Web TH02 - To Be Announced
Sites (WAWS) - Rockford Lhotka

9:30 AM 10:45 AM TH05 - Zero to Connected with Windows TH06 - Essential C# 6.0 - Mark Michaelis
Azure Mobile Services - Brian Noyes
11:00 AM 12:15 PM TH09 - Building Services with ASP.NET TH10 - Performance and Diagnostics Hub
MVC Web API Deep Dive - Marcel de Vries in Visual Studio 2013 - Brian Peek
12:15 PM 1:30 PM Lunch
1:30 PM 2:45 PM TH13 - Beyond Hello World: A Practical TH14 - Visual Studio 2013 Release Manager:
Introduction to Node.js on Windows Reduce Your Cycle Time to Improve Your
Azure Websites - Rick Garibay Value Delivery - Marcel de Vries
3:00 PM 4:15 PM TH17 - From the Internet of Things to TH18 - Create Automated Cross Browser
Intelligent Systems: A Developer's Testing of Your Web Applications with
Primer - Rick Garibay Visual Studio CodedUI - Marcel de Vries
4:30 PM 5:30 PM Conference Wrap-Up Panel: Andrew Brust, Miguel Castro, Rockford Lhotka, Ted Neward,
*Speakers and sessions subject to change

Untitled-9 4 2/6/14 12:54 PM


CHICAGO 2O14
May 5 – 8 | Chicago Hilton

ASP.NET JavaScript / HTML5 SharePoint SQL Server


Client

, 2014 (Separate entry fee required)

MW02 - Workshop: Data-Centric Single Page MW03 - Workshop: SQL Server for Developers -
Applications with Durandal, Knockout, Breeze, Andrew Brust & Leonard Lobel
and Web API - Brian Noyes

T03 - HTML5 for Better Web Sites - T04 - Introduction to Windows Azure - Vishwas Lele
Robert Boedigheimer
m T07 - Great User Experiences with CSS 3 - T08 - Windows Azure Cloud Services - Vishwas Lele
Robert Boedigheimer

T11 - Build Angular Applications Using TypeScipt - T12 - Windows Azure SQL Database – SQL Server in
Part 1 - Sergey Barskiy the Cloud - Leonard Lobel

T15 - Build Angular Applications Using TypeScipt - T16 - Solving Security and Compliance Challenges with
Part 2 - Sergey Barskiy Hybrid Clouds - Eric D. Boyd

T19 - Building Real Time Applications with ASP.NET T20 - Learning Entity Framework 6 - Leonard Lobel Visual Studio Live! Chicago Blues After Dark
SignalR - Rachel Appel
Reception at Buddy Guy's Legends

CONNECT WITH VISUAL STUDIO LIVE!


W03 - Leveraging Visual Studio Online - W04 - Programming the T-SQL Enhancements in
e Brian Randell SQL Server 2012 - Leonard Lobel

twitter.com/vslive – @VSLive
W07 - JavaScript for the C# Developer - W08 - SQL Server 2014: Features Drill-down - Scott Klein
Philip Japikse

facebook.com – Search “VSLive”


ps W11 - Build Data-Centric HTML5 Single Page W12 - SQL Server 2014 In-memory OLTP - Deep Dive -
Applications with Breeze - Brian Noyes Scott Klein

e W15 - Knocking it Out of the Park, with Knockout. W16 - To Be Announced linkedin.com – Join the
JS - Miguel Castro “Visual Studio Live” group!
ur W19 - JavaScript: Turtles, All the Way Down - W20 - Building Apps for SharePoint - Mark Michaelis
Ted Neward

REGISTER BY APRIL 2
AND SAVE $200!
TH03 - What's New in MVC 5 - Miguel Castro TH04 - Excel, Power BI and You: An Analytics Superhub
- Andrew Brust

TH07 - What's New in Web API 2 - Miguel Castro TH08 - Big Data 101 with HDInsight - Andrew Brust

TH11 - Upgrading Your Existing ASP.NET Apps - TH12 - NoSQL for the SQL Guy - Ted Neward Use Promo Code VSLMAR4
Pranav Rastogi

: TH15 - Finding and Consuming Public Data APIs - TH16 - Git for the Microsoft Developer - Eric D. Boyd
G. Andrew Duthie

TH19 - Provide Value to Customers and TH20 - Writing Asynchronous Code Using .NET 4.5
Enhance Site Stickiness By Creating an API - and C# 5.0 - Brian Peek
G. Andrew Duthie Scan the QR code to
register or for more
d, & Brian Peek
event details.

vslive.com/chicago

Untitled-9 5 2/6/14 12:54 PM


WINDOWS 8.1

Building a
Netduino-Based HID
Sensor for WinRT
Donn Morse

The Human Interface Device (HID) protocol was origi- introduced the HID Windows Runtime (WinRT) API with
nally intended to simplify the use of devices such as mice, keyboards Windows 8.1 (bit.ly/1aot1by). This new API lets you write Windows
and joysticks. However, because of its unique features—including Store apps for your device using JavaScript, Visual Basic, C# or C++.
its self-descriptive nature—device manufacturers use the protocol In addition, Microsoft recently added support for several new
to support medical devices, health and fitness devices, and custom transports, so you aren’t limited to a USB cable. Today, you can
sensors. If you’re new to the HID API, refer to the USB HID create a HID device that transmits and receives packets over USB,
Information site (bit.ly/1mbtyTz) to find more information. Another Bluetooth, Bluetooth LE, and I2C. (For more information, see “HID
great resource is Jan Axelson’s book, “USB Complete: The Devloper’s Transports” at bit.ly/1asvwg6.)
Guide, Fourth Edition” (Lakeview Research LLC, 2009). In this article, I’ll show how you can build a simple temperature
Prior to Windows 8.1, if you were writing an application for a sensor that’s compatible with the HID protocol. Then I’ll describe
HID device you wrote a native Win32 app. But if you were a Web a sample Windows Store app that can display temperature data
or a .NET developer, the ramp was steep. To address this, Microsoft from the device.

Constructing the Temperature Sensor


This article discusses:
The sample device is based on the Netduino development board
• Building a temperature-sensor device (netduino.com). This open source board is used by hobbyists, academics
• The beta firmware for the Netduino and industrial engineers to build working prototype devices. And,
• The sensor firmware because the Netduino is pin-compatible with the Arduino, you can
• The HID protocol attach your Arduino shields to quickly add functionality. (A shield
• The HID temperature-sensor app
is a board with specific functionality, such as wireless communi-
cation, motor control, Ethernet, RS232, LCD display and so on.)
Technologies discussed:
My sample device uses an RS232 shield to download the firmware.
Windows 8.1, HID WinRT API, Microsoft .NET Micro Framework, It uses the onboard USB connector to transmit and receive data.
C#, Netduino Board The Netduino supports the .NET Micro Framework and its firm-
Code download available at: ware is created with a free copy of Visual C# Express.
msdn.microsoft.com/magazine/msdnmag0314
To obtain temperature data, the sample device uses the Texas
Instruments LM35 sensor. The sensor takes 5 volts of input from
52 msdn magazine
Figure 1 The Complete HID Temperature Sensor Setup Figure 2 Attaching the RS232 Shield to the Netduino

the Netduino and converts it into an output voltage proportional After you’ve upgraded the firmware on your board, you’re ready
to the current Celsius temperature. to begin constructing the temperature-sensor circuit. The first step
Here are the parts you need to build your own HID sensor: requires you to attach the RS232 shield to your board. (As I already
• Netduino 1 or Netduino Plus 1 (Amazon, amzn.to/1dvTeLh): A mentioned, the Netduino is pin-compatible with the Arduino, so if
development board with programmable microcontroller you’ve been working with the Arduino and have an RS232 shield
that supports the .NET Micro Framework. handy, you can use it.) Snap the RS232 shield onto the Netduino
• RS232 shield (CuteDigi, bit.ly/1j7uaMR): The RS232 module as shown in Figure 2.
for downloading and debugging the firmware. (This shield After you’ve attached the RS232 shield, the next step is to attach
is required for the beta version of the firmware being used.) the temperature sensor to the 5V power source, ground and pin
• LM35 Sensor (DigiKey, bit.ly/KjbQkN): The temperature sen- 0 of the Netduino. Figure 3, from the TI datasheet for the sensor,
sor that converts input voltage to output voltage based on shows the pin-outs.
the current temperature.
• RS232-to-USB converter cable (Parallax, bit.ly/1iVmP0a): The Installing the Sensor Firmware
cable for downloading the temperature-sensor firmware via There are two layers, or instances, of firmware on the Netduino.
the RS232 shield. (Note that an FTDI chipset is required The first is the manufacturer’s firmware, which includes the .NET
for compatibility with the shield.) Micro Framework; the second is your device’s firmware. The man-
• 9V 650mA power supply (Amazon, amzn.to/1d6R8LH): The ufacturer’s firmware processes requests from the device firmware.
power supply for the Netduino board. The manufacturer’s firmware is loaded once onto the development
• USB to Micro-USB cable (Amazon, amzn.to/Kjc8Ii): The board and executes each time you power up the device. In contrast,
cable for sending HID packets from the Netduino to your you typically refresh your device firmware multiple times during
Windows 8.1 tablet or laptop. the development and prototyping process.
Figure 1 shows the complete HID temperature sensor setup. In order to install any device firmware, you first need to install
The RS232 shield is attached to the top of the Netduino. The an instance of Visual C# Express 2010 on your development
breadboard contains the LM35 sensor, which is attached to 5V, machine. You’ll find a link to the download at bit.ly/1eRBed1.
ground and Pin 0. (Pin 0 is one of six analog-to-digital [ADC] pins For most Netduino projects, you can download and debug your
on the board). So, let’s get started. firmware using the native USB connection. However, the beta ver-
The firmware your Netduino 1 (or Netduino Plus 1) comes with sion of the manufacturer’s firmware requires an RS232 connection
doesn’t support the HID protocol. You’ll need to configure your (which is why the RS232 shield is required).
development board by installing version 4.1.1 of Once Visual C# Express is installed, attach the
the beta firmware, which includes support for RS232-to-USB cable and open Windows Device
HID. You’ll find a zip folder containing the beta Manager to determine which COM port Windows
firmware at bit.ly/1a7f6MB. (You’ll need to create an assigned to that cable.
account by registering with Secret Labs in order When I attached the Parallax RS232-to-USB
to download the file.) converter to my development machine, Windows
The download page on the Web site includes mapped it to COM6, as Figure 4 shows.
instructions for updating the firmware. However, Now that I know the COM port associated
these instructions are fairly complex, particularly with the converter, I can power up my Netduino
if you’re new to the Netduino. The video at bit.ly/ Plus 1, attach the RS232-to-USB cable, and start
1d73P9x is a helpful, concise description of the an instance of Visual C# Express to complete
firmware upgrade process. Figure 3 The Sensor Pin-Outs the download.
msdnmagazine.com March 2014 53
Supporting the USB Transport As I noted earlier, Microsoft
supports HID devices running over USB, Bluetooth, Bluetooth LE
and I2C. However, the sample device described in this article uses
the USB transport. What this actually means is that USB drivers will
be moving packets in both directions: packets originating with the
device are passed up to the HID driver (which passes them on to
the API if there are interested apps); packets originating with the
HID driver are passed back down to the device.
Windows uses specific data issued by the device upon connec-
tion to identify which USB drivers it should load.

The first thing to do after


starting Visual C# Express is to
identify the correct transport
and COM port.
Figure 4 The COM Port Assigned to the RS232-to-USB Cable
Defining the Firmware Classes The firmware for the tempera-
The first thing to do after starting Visual C# Express is to identify ture-sensor device is built around two classes: Program and Sensor.
the correct transport and COM port. You do this by right-clicking The Program class supports a single Main routine that’s invoked
on the project name in the Solution Explorer pane and choosing at startup. The Sensor class defines the USB and HID settings for
the Properties menu. the temperature sensor. In addition, it supports the methods that
When the Properties dialog appears, choose the .NET Micro Frame- send input reports and read output reports.
work tab and make the necessary selections, as shown in Figure 5. The Sensor class contains all of the code required to configure
After specifying the Transport and Device, you can deploy the USB transport. This includes the code that:
the firmware. Again, right-click the project name in the Solution • Configures a read endpoint
Explorer pane and, this time, choose Deploy. • Configures a write endpoint
When the deployment completes, Visual C# Express will report • Specifies the vendor ID (VID)
the success in the Output pane. • Specifies the product ID (PID)
You’re now ready to attach your device to a Windows 8.1 tablet or • Specifies friendly names (manufacturer name, product
laptop and test it with the Custom Temperature Sensor sample app. name and so on)
First, detach the RS232 cable, power down the Netduino, and then • Specifies other required USB settings for a HID device
restart it with the auxiliary power supply. Give the device several sec- Most of the USB configuration code is found in the Configure-
onds to power up and then attach the USB cable to the Netduino. HID method in the Sensors.cs module. This method, in turn, creates
After doing this, you should see your device added to the
collection of HID devices in Device Manager. (The VID
and PID in Figure 6 correspond to the VID and PID of
the sample device; these are the vendor and product IDs.)
Once the device is installed on your Windows 8.1
machine, you’ll want to install and build the sample app.
When the app starts, you can select the sensor and begin
monitoring the ambient temperature in your office.

The Device Firmware


Now let’s take a detailed look at the device firmware
for the temperature sensor. At the outset, I’d like to
thank the folks at Secret Labs (the manufacturers of
the Netduino) for the work they’ve done to support
HID over USB on the Netduino platform. The starting
point for this firmware was a sample on the forum, the
UsbHidEchoNetduinoApp, available at bit.ly/1eUYxAM. Figure 5 Configuring the .NET Micro Framework Properties

54 msdn magazine Windows 8.1


Untitled-1 1 10/10/13 10:10 AM
current report interval in millisec-
onds. (The sensor firmware issues
an input report at the frequency
specified by the report interval.)
The output report for the sample
device is even simpler—it’s a single
byte that specifies the report interval.
(This is an integer value that repre-
sents the interval in milliseconds.)
Creating the Report Descriptor
As I mentioned earlier, one of the
features of a HID device is its self-
reporting nature: Upon connecting
to a host, the device provides a
description of its purpose, capabili-
ties and packet format in what’s called
a report descriptor. This descriptor
indicates where the device fits in the
HID universe (is it a mouse, a key-
board, a vendor-defined device?). The
descriptor also specifies the format of
the individual feature reports, input
reports and output reports.
The report descriptor for the
Figure 6 The Vendor and Product IDs of the Sample Device temperature sensor is found in
Sensors.cs, as shown in Figure 8.
and initializes a Configuration object (bit.ly/1i1IcQ3) that contains the The first two lines of the descriptor inform the host that this
device’s USB settings (endpoints, VID, PID and so on). particular device is vendor-defined:
The read endpoint allows the device to receive packets from the 0x06,0x55,0xFF, //HID_USAGE_PAGE_VENDOR_DEFINED
0x09,0xA5, //HID_USAGE (vendor_defined)
API and the HID driver. The write endpoint allows the driver to
send packets up through the driver stack to the API. Lines four through 15 indicate the format of the two-byte input
Windows uses the vendor ID, product ID, and other USB report. Lines four through nine describe the first byte of the input
settings (which were specified in the ConfigureHID method) to report, which specifies the temperature reading:
0x09,0xA7, //HID_USAGE (vendor_defined)
determine whether the device is a valid USB device and then to 0x15,0x00, //HID_LOGICAL_MIN_8(0), // Minimum temp is 0 degrees F
load the appropriate drivers. 0x25,0x96, //HID_LOGICAL_MAX_8(150), // Max supported temp is
Opening the Device Connection The Sensor class includes 0x75,0x08, //HID_REPORT_SIZE(8),
// 150 degrees F

an Open method that’s called from within the Main routine of 0x95,0x01, //HID_REPORT_COUNT(1),
the Program class. As you can see in Figure 7, the Open method: 0x81,0x02, //HID_INPUT(Data_Var_Abs),

• Retrieves the available USB controllers


• Invokes the ConfigureHID method to establish the device’s
USB and HID settings
The HID protocol is based on
• Invokes the Start method on the first available controller
• Creates a USB stream object with read and write endpoints
reports: feature reports, input
The Sensor class also includes a Close method, which is called
when the device is detached from the host laptop or tablet.
reports and output reports.
Supporting the HID Protocol The 10th through 15th lines describe the second byte of the
The HID protocol is based on reports: feature reports, input input report, which specifies the report interval (in milliseconds):
0x09,0xA8, //HID_USAGE (vendor_defined)
reports and output reports. Feature reports can be sent by either 0x15,0x4B, //HID_LOGICAL_MIN_8(75), // minimum 75 ms
the host (that is, a connected laptop or tablet) or the device. Input 0x25,0xFF, //HID_LOGICAL_MAX_8(255), // maximum 255 ms
0x75,0x08, //HID_REPORT_SIZE(8),
reports are sent by the device to the host. Output reports are sent 0x95,0x01, //HID_REPORT_COUNT(1),
by the host to the device. 0x81,0x02, //HID_INPUT(Data_Var_Abs),
In the case of our sample temperature sensor, the input report is The report descriptor for the sample device is included as part
a very simple two-byte packet. The first byte specifies the current of the UsbController.Configuration object (bit.ly/1cvcq5G) that’s
temperature in degrees Fahrenheit; the second byte indicates the created within the ConfigureHID method in Sensor.cs.

56 msdn magazine Windows 8.1


Untitled-1 1 1/13/14 10:11 AM
Figure 7 The Open Method Supporting the HID Input Report The input report is defined
public bool Open()
as a structure in the Sensor.cs module:
{ struct InputReport
bool succeed = true; {
public byte Temperature; // Temperature in degrees Fahrenheit
started = false; public byte Interval; // Report interval (or frequency) in seconds
}
UsbController[] usbControllers = UsbController.GetControllers(); The firmware issues input reports using the UsbStream object
if (usbControllers.Length < 1) (bit.ly/1kElfUZ) it created in the Open method. These input reports
{ are issued from the SendInputReport method when the firmware
succeed = false;
} invokes the stream.Write method:
protected void SendInputReport(InputReport report)
if (succeed) {
{
usbController = usbControllers[0]; byte[] inputReport = new byte[2];

try inputReport[0] = (byte)report.Temperature;


{ inputReport[1] = (byte)report.Interval;

succeed = ConfigureHID(); stream.Write(inputReport, 0, 2);


}
if (succeed)
{ Issuing Temperature Data with Input Reports The Update method
succeed = usbController.Start(); in the Sensor class issues an input report to the connected host:
}
public int Update(int iTemperature, int iInterval)
if (succeed) {
{ InputReport inputReport = new InputReport();
stream = usbController.CreateUsbStream(WRITE_ENDPOINT, READ_ENDPOINT); byte Interval = 0;
}
} inputReport.Temperature = (byte)iTemperature;
catch (Exception) inputReport.Interval = (byte)iInterval;
{
succeed = false; SendInputReport(inputReport);
}
} Interval = GetOutputReport();
return (int)Interval;
started = true; }
return succeed; The Update method is invoked from within an infinite while
}
loop, shown in Figure 9, which executes in the firmware’s Main
routine (found in Program.cs).
Figure 8 The Report Descriptor for the Temperature Sensor
hidGenericReportDescriptorPayload = new byte[]
{
This new API lets your app
retrieve data from HID devices,
0x06,0x55,0xFF, //HID_USAGE_PAGE_VENDOR_DEFINED
0x09,0xA5, //HID_USAGE (vendor_defined)

and control them as well.


0xA1,0x01, //HID_COLLECTION(Application),

// Input report (device-transmits)


0x09,0xA7, //HID_USAGE (vendor_defined)
0x15,0x00, //HID_LOGICAL_MIN_8(0), // Minimum temp is 0 degrees F
0x25,0x96, //HID_LOGICAL_MAX_8(150), // Max supported temp is Supporting the HID Output Report The output report is
// 150 degrees F
0x75,0x08, //HID_REPORT_SIZE(8),
defined as a structure in the Sensor.cs module:
0x95,0x01, //HID_REPORT_COUNT(1), struct OutputReport
0x81,0x02, //HID_INPUT(Data_Var_Abs), {
public byte Interval; // Report interval (or frequency) in seconds
0x09,0xA8, //HID_USAGE (vendor_defined) }
0x15,0x4B, //HID_LOGICAL_MIN_8(75), // minimum 75 ms The firmware receives output reports via the same UsbStream
0x25,0xFF, //HID_LOGICAL_MAX_8(255), // maximum 255 ms
0x75,0x08, //HID_REPORT_SIZE(8), object it created in the Open method. These output reports are
0x95,0x01, //HID_REPORT_COUNT(1), received within the GetOutputReport method:
0x81,0x02, //HID_INPUT(Data_Var_Abs),
protected byte GetOutputReport()
// Output report (device-receives) {
0x09,0xA9, //HID_USAGE (vendor_defined) byte[] outputReport = new byte[1];
0x15,0x4B, //HID_LOGICAL_MIN_8(75), // minimum 75 ms int bytesRead = 0;
0x25,0xFF, //HID_LOGICAL_MAX_8(255), // maximum 255 ms if (stream.CanRead)
0x75,0x08, //HID_REPORT_SIZE(8), {
0x95,0x01, //HID_REPORT_COUNT(1), bytesRead = stream.Read(outputReport, 0, 1);
0x91,0x02, //HID_OUTPUT(Data_Var_Abs), }
if (bytesRead > 0)
0xC0 //HID_END_COLLECTION return outputReport[0];
else
}; return 0;
}

58 msdn magazine Windows 8.1


Figure 9 The While Loop That Invokes the Update Method The sample is designed to work with an attached HID device that
while (true)
detects temperatures from 0 to 150 degrees Fahrenheit. The app
{ monitors and then displays the temperature sensor’s current reading.
// Retrieve the current temperature reading
The app supports three “scenarios,” each of which maps to
milliVolts = (double)voltsPin.Read(); // Read returns a value in the specific features in the app’s UI. In addition, each scenario maps to
// specified range
tempC = milliVolts / 10.0; // Sensor returns 10mV per a corresponding XAML and C# source file. The following lists each
// degree Centigrade scenario, its corresponding modules and its function:
tempF = 1.8 * tempC + 32; // Convert to degrees Fahrenheit
simpleTemp = (int)tempF; Device Connect (Scenario1_ConnectToSensor.xaml; Scenario1_
ConnectToSensor.xaml.cs)
// Because there are voltage fluctuations when the external
// power supply is connected to the Netduino, use a running • Supports connecting a HID device to a Windows 8.1 PC.
// average to "smooth" the values • Enumerates the connected temperature sensors so the
if (firstReading) user can select one.
{
firstReading = false;
• Establishes a device watcher that monitors the status of
currentTemp = simpleTemp; the device. (The device watcher fires an event when the
for (i = 0; i < 12; i++)
tempArray[i] = simpleTemp;
user disconnects or reconnects the selected HID device.)
Get Temperature Data (Scenario2_GetTemperatureData.xaml;
}
else
Scenario2_GetTemperatureData.xaml.cs)
{ • Monitors the selected temperature sensor.
tempArray = Shift(simpleTemp, tempArray); // Shift the array elements and
// insert the new temp
• Depicts a temperature gauge and renders the current
currentTemp = Average(tempArray); // Compute a running average of reading using a slider control.
// the last 12 readings
}
Set Report Interval (Scenario3_SetReportInterval.xaml;
Scenario3_SetReportInterval.xaml.cs)
RequestedInterval = sensor.Update(currentTemp, CurrentInterval);
• Allows the user to control the frequency at which the tem-
// Check for a possible new interval requested via an perature sensor reports its status. (The default interval is 250
// output report
ms, but users can choose intervals from 75 ms to 250 ms.)
if (RequestedInterval != 0)

Supporting Device Connections


{
CurrentInterval = RequestedInterval;
} The device-connect scenario enables several aspects of connecting a
led.Write(true); HID device to a Windows 8.1 PC: enumerating connected devices,
Thread.Sleep(CurrentInterval);
led.Write(false);
establishing a device watcher, handling device disconnection and
} handling device reconnection.
}
Establishing a Device Connection The code that handles the
device connection is found in three modules: Scenario1_Connect-
Adjusting the Report Interval with Output Reports The ToSensor.xaml.cs, EventHandlerForDevices.cs and DeviceList.cs.
firmware supports a report interval specified in milliseconds. (The first module contains the primary code for this scenario; the
The minimum supported interval is 75 ms; the maximum other two contain supporting functionality.)
interval is 255 ms. An app requests a new interval by sending an The first phase of the connection occurs before the UI is visible. In
output report to the device. The device, in turn, reports this phase, the app creates a DeviceWatcher object that notifies the
the current interval in each input report that it sends to any app when devices are added, removed or changed. The second phase
connected app.
The firmware applies the current interval by
invoking the Thread.Sleep method (bit.ly/LaSYVF) for the
number of seconds specified by the current interval:
led.Write(true);
Thread.Sleep(CurrentInterval);
led.Write(false);
By pausing the while loop for this duration,
registered apps receive input reports at the
specified interval.

The HID Temperature-Sensor App


The sample app demonstrates how you can display
temperature data from an attached HID tempera-
ture sensor using the new HID WinRT API for
Windows 8.1. This new API lets your app retrieve
data from HID devices, and control them as well. Figure 10 The Windows 8.1 App Connected to a HID Device
msdnmagazine.com March 2014 59
Figure 11 The InitializeDeviceWatchers Method In terms of the HID API, the primary code of interest is found
private void InitializeDeviceWatchers()
in the InitializeDeviceWatchers method, shown in Figure 11.
{ This code invokes the HidDevice.GetDeviceSelector method
// Custom sensor
var CustomSensorSelector =
(bit.ly/1eGQI1k) and passes the UsagePage, UsageId, VID and PID for
HidDevice.GetDeviceSelector(CustomSensor.Device.UsagePage, the temperature sensor.
CustomSensor.Device.UsageId, CustomSensor.Device.Vid,
CustomSensor.Device.Pid);
The UsagePage and UsageId values are defined in the file constants.cs:
public class Device
// Create a device watcher to look for instances of the custom sensor {
var CustomSensorWatcher = public const UInt16 Vid = 0x16C0;
DeviceInformation.CreateWatcher(CustomSensorSelector); public const UInt16 Pid = 0x0012;
public const UInt16 UsagePage = 0xFF55;
// Allow EventHandlerForDevice to handle device watcher events that public const UInt16 UsageId = 0xA5;
// relate or affect the device (device removal, addition, app }
// suspension/resume)
AddDeviceWatcher(CustomSensorWatcher, CustomSensorSelector); These class members correspond to values specified in the HID
} report descriptor that’s defined in the device’s firmware:
hidGenericReportDescriptorPayload = new byte[]
{
occurs after the UI is displayed and the user is able to choose a specific 0x06,0x55,0xFF, //HID_USAGE_PAGE_VENDOR_DEFINED
device from the connected HID devices. The app displays a Device- 0x09,0xA5, //HID_USAGE (vendor_defined)

InstanceId string for each connected device; the string includes the The GetDeviceSelector method returns an Advanced Query
VID and PID for the given device. In the case of the sample tempera- Syntax (AQS) string in the CustomSensorSelector variable. The app
ture sensor, the DeviceInstanceId string has the form: then uses this string when it creates a device watcher and when it
HID\VID_16C0&PID_0012\6&523698d&0&0000 enumerates the DeviceInformation objects.
Figure 10 shows the app as it appears after enumeration has The Second Stage of Device Connection The second stage of
completed and the user has connected to the device. device connection allows the user to make a selection from the list
The First Stage of Device Connection Here are the methods of connected devices. This stage establishes the currently selected
called during the first stage of device connection (before the UI is device. Here are the methods (all in EventHandlerForDevices.cs)
displayed), along with tasks accomplished by each method: called and what each one does.
DeviceConnect (Scenario1_DeviceConnect.xaml.cs) invokes OpenDeviceAsync opens the connection to the device.
the CustomTemperatureSensor.InitializeComponent method, RegisterForAppEvents registers for app suspension and
which initializes the app’s UI components such as the text blocks resume events.
and buttons. RegisterForDeviceAccessStatusChange listens for changes
InitializeDeviceWatchers (Scenario1_DeviceConnect.xaml.cs) in device-access permissions.
invokes the HidDevice.GetDeviceSelector method to retrieve a RegisterForDeviceWatcherEvents registers for the added
device selector string. (The selector is required in order to create and removed events.
a device watcher.) Once the selector is obtained, the app invokes StartDeviceWatcher starts the device watcher.
DeviceInformation.Cre- SelectDeviceInList checks to see if the user has selected a
ateWatcher to create the device and, if so, saves the index for that device. It also writes a
DeviceWatcher object “Currently connected …” string to the output window if the con-
and then EventHandler- nection is successful.
ForDevice.Current.Add-
DeviceWatcher. (This Figure 13 Reading and Displaying Sensor Data
last method allows the
private async void OnInputReportEvent(HidDevice sender,
app to monitor changes HidInputReportReceivedEventArgs eventArgs)
in device status.) {
// Retrieve the sensor data
AddDeviceWatcher HidInputReport inputReport = eventArgs.Report;
(EventHandlerForDe- IBuffer buffer = inputReport.Data;
DataReader dr = DataReader.FromBuffer(buffer);
vices.cs) creates the event byte[] bytes = new byte[inputReport.Data.Length];
handlers for three device dr.ReadBytes(bytes);

events: Enumeration // Render the sensor data


Completed, Device Add- await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
ed and Device Removed. CurrentReadingText.TextAlignment = TextAlignment.Center;
SelectDeviceInList CurrentReadingText.Text = bytes[1].ToString();
TemperatureSlider.Value = (int)bytes[1];
(Scenario1_DeviceCon-
nect.xaml.cs) checks to rootPage.NotifyUser(bytes[1].ToString() + " degrees Fahrenheit, " +
bytes[2].ToString() +
see if the user has selected " millisecond report-interval", NotifyType.StatusMessage);
Figure 12 Displaying the Current a device and, if so, it saves });
}
Temperature the index for that device.
60 msdn magazine Windows 8.1
The primary method supporting this scenario is the OnInput-
ReportEvent event handler, found in Scenario2_GetTemperature-
Data.xaml.cs. The app registers this event handler when the user
chooses the Get temperature data scenario and presses the Regis-
ter for Temperature Detection button. The app registers the event
handler within the RegisterForInputReportEvents method. In
addition to registering the handler, this method saves an event
token so it can unregister.
private void RegisterForInputReportEvents()
{
if (!isRegisteredForInputReportEvents)
{
Figure 14 Setting the Report Interval inputReportEventHandler = new TypedEventHandler<HidDevice,
HidInputReportReceivedEventArgs>(this.OnInputReportEvent);

In terms of the HID API, the primary code of interest is in the registeredDevice = EventHandlerForDevice.Current.Device;

OpenDeviceAsync method. This code invokes the HidDevice.From- registeredDevice.InputReportReceived += inputReportEventHandler;


IdAsync method (bit.ly/1hyhVpI), which returns a HidDevice object
isRegisteredForInputReportEvents = true;
(bit.ly/1dsD2rR) that the app uses to access the device, retrieve input }
reports and send output reports: }

public async Task<Boolean> OpenDeviceAsync(DeviceInformation deviceInfo, Once the event handler is registered, it reads each input report
String deviceSelector) issued by the sensor and uses the new temperature value to update
{
// This sample uses FileAccessMode.ReadWrite to open the device the TemperatureSlider control. After it updates the control, this
// because you don’t want other apps opening the device and method writes the current temperature and report interval values
// changing the state of the device.
// FileAccessMode.Read can be used instead. to the Output section of the app, as shown in Figure 13.
device = await HidDevice.FromIdAsync(deviceInfo.Id, FileAccessMode.ReadWrite);

Sending Output Reports


...
Supporting the Device Watcher Device enumeration occurs
The report-interval scenario sends an output report to the tem-
when the app is first started and begins even before the UI is dis-
perature sensor and writes the count of bytes as well as the value
played. After enumeration completes, the app monitors device status.
written to the Output area of the app’s window. The app sends
Device status is reported by a DeviceWatcher object (bit.ly/1dBPMPd).
an output report after the user chooses the Set report interval
As the name implies, this object “watches” the connected devices—
scenario, selects a value from the Value to Write dropdown, and
if the user removes or connects his device, the watcher reports the
then presses the Send Output Report button.
event to the app. (These events are only reported after the enumer-
Figure 14 shows the Set report interval scenario and the drop-
ation process is finished.)
down that’s populated with the report interval options. (These
values represent a report-interval in milliseconds; so, by selecting
Retrieving Input Reports
100, the app will receive 10 readings every second.)
The temperature-retrieval scenario monitors the input reports
The primary method of the report-interval scenario is
issued by the temperature-sensor and uses a slider control to
SetReportIntervalAsync, found in the Scenario3_SetReport-
display the current temperature, as shown in Figure 12. (Note that
Interval.xaml.cs module (see Figure 15). This method invokes
this control is limited to displaying temperature data. The proper-
the HidDevice.SendOutputReportAsync method ( bit.ly/1ad6unK)
ties IsDoubleTapEnabled, IsHoldingEnabled, IsRightTapEnabled
to send an output report to the device.
and IsTapEnabled have all been set to False.)
Figure 15 SetReportIntervalAsync Wrapping Up
First I gave you a quick look at building a HID device that moni-
private async Task SetReportIntervalAsync(Byte valueToWrite)
{ tors the voltage emitted by a simple sensor. For an example of how
var outputReport = you could monitor a sensor that toggles a digital I/O pin (rather
EventHandlerForDevice.Current.Device.CreateOutputReport();
var dataWriter = new DataWriter(); than emitting a range of voltages), see the motion-sensor sample
on MSDN at bit.ly/1gWOlcC.
// First byte contains the report id
dataWriter.WriteByte((Byte)outputReport.Id); Then you took a quick look at writing a simple app that moni-
dataWriter.WriteByte((Byte)valueToWrite); tors and controls a HID device. For more information about the
outputReport.Data = dataWriter.DetachBuffer();
HID WinRT API, visit bit.ly/1aot1by. Q
uint bytesWritten =
await EventHandlerForDevice.Current.Device.
SendOutputReportAsync(outputReport); DONN MORSE is a senior content developer at Microsoft. Reach him
at [email protected].
rootPage.NotifyUser("Bytes written: " + bytesWritten.ToString() + ";
Value Written: " + valueToWrite.ToString(), NotifyType.StatusMessage);
}
THANKS to the following technical expert for reviewing this article:
Arvind Aiyar (Microsoft)
msdnmagazine.com March 2014 61
THE WORKING PROGRAMMER TED NEWARD

Getting Started with Oak:


Data Validation and Wrapping Up
For three columns now, I’ve been exploring the “dynamic-y” object application, but the need is much higher in any dynamically typed
approach that Oak brings to the Web application space, and it’s environment), however you choose to test.
been an interesting ride, complete with a few challenges to long- Meanwhile, the AddComment method is still missing.
held beliefs about how Web applications need to be built (or about
the platform on which they’re built). But every ride has to come to Comment Away
an end sometime, and it’s about time to wrap up my exploration of In this particular case, when the user types a comment into the
Oak. I’ve got to figure out how to ensure data put into the system view, it POSTs to the HomeController Comments method, which
by the user is actually good data, for starters. looks like so:
But first ... [HttpPost]
public ActionResult Comments(dynamic @params)
{
Commentary dynamic blog = blogs.Single(@params.BlogId);
If you go back and look at the system as I left off last time, trying blog.AddComment(@params);
to add a comment yields another of those helpful errors, this time
return RedirectToAction("Index");
informing you that “Blog.Controllers.Blog does not contain a defi- }
nition for ‘AddComment.’” Contrary to what might happen in a As you can see, the controller is first obtaining the blog ID tucked
statically typed system—where the lack of this method would trip a away in the BlogId parameter coming from the form, then using
compilation error at the front of the compile/deploy/run cycle—in it to find the corresponding blog entry via the Single method on
a dynamic system, these errors won’t be seen until they’re actually the DynamicRepository, then calling blog.AddComment to store
attempted. Some dynamic-language proponents claim this is part of the comment. (Again, just to make the points, both “pro” and
the charm of dynamic languages, and certainly not having to worry “con”: This code has been in place since the second part of this
about keeping everything consistent across the entire system can be series, and I’m just now running into the fact that the AddComment
a great boon when the mind is on fire with an idea and you just need method hasn’t existed until now.)
to get that idea out into the world. But most Ruby-on-Rails devel- Defining this method is pretty straightforward; on the Blog class,
opers I know who’ve done a project larger than your typical Todo add this method:
list application will be the first to admit that in a dynamic-language void AddComment(dynamic comment)
application, comprehensive tests are critical to keeping the project’s {
// Ignore addition if the body is empty
quality high and the developer’s sanity strong. So testing has to be a if (string.IsNullOrEmpty(comment.Body)) return;
part of any serious effort with Oak.
// Any dynamic property on this instance can be accessed
// through the "_" property

Notice how the dynamic


var commentToSave = _.NewComment(comment);

comments.Insert(commentToSave);

nature of the system lets you be


}
The only real question mark in this method is the use of the

extremely frugal in the code.


underscore (_.NewComment(comment)), which is a placeholder
for the “this” reference. The underscore has full awareness of the
dynamic nature of this object, which the “this” reference wouldn’t;
Unfortunately, as soon as I start talking about testing, I start rather than having to worry about the differences, the underscore
getting into several areas of discussion (unit tests, behavior tests, lets you use everything “this” would, plus more.
integration tests, Test-Driven Development and so on) that could Notice how the dynamic nature of the system lets you be
easily consume another half-dozen magazine issues on their own, extremely frugal in the code. The form parameters are captured
and I don’t want to crack that Pandora’s box here. Whatever your in a named bundle in “@params” coming in to the controller, and
testing methodology or preference, suffice it to say that you must those are passed without unpacking any of them directly into Add-
have some kind of testing presence in an Oak application (or any Comment, which in turn passes them into NewComment, which

62 msdn magazine
Untitled-1 1 11/26/12 3:02 PM
constructs a dynamic object out of them, and the resulting object • Format: The general-purpose regular-expression (using
just gets inserted into the comments DynamicRepository. Where the Microsoft .NET Framework Regex class) validation.
do the names of the object’s properties come from? From the HTML • Numericality: Pretty much everything to do with numeric
form that originated all of this. values, including marking a field as “integer-only,” greater-
Wacky, huh? Almost feels like you’re being lazy or something. than and less-than restrictions, or simple even/odd tests.
Anyway, give it a run, and sure enough, now comments are • Conditional: The catch-all “escape hatch” for any
being added in. validation not covered elsewhere—a field must satisfy
a condition described using a lambda function.
Validate Me The last one, Conditional, isn’t actually a validation type in
As written, though, the system has a major flaw (well, according and of itself, but a feature present on most (if not all) of the other
to user requirements, anyway): It’s perfectly permissible for two validation types, and therefore deserves a little more explanation.
blog entries to have the exact same title, and that’s not OK. (Read- Imagine an Order object, for orders in a traditional e-commerce
ers might get confused as to which one to read, the one titled “LOL system. For said systems, a credit card number is only necessary
Kittehs” or the other one titled “LOL Kittehs.”) Thus, you need a if the user wants to use a credit card for payment. Similarly, an
way of enforcing some kind of uniqueness on the code, so users address to which to ship the product is only necessary if the user
can’t accidentally put in duplicate-titled blog entries. purchased anything other than a digital download. These two con-
In a traditional Web framework, this would be a two-part job. tingencies are neatly expressed using two conditional validations,
First, the model object (Blog) would have to define some kind of as shown in Figure 1.
“ask me if I’m valid” method, which I’ll call IsValid for lack of any-

While it’s usually better to use


thing really original, and a definition of said validation within the
object, which I’ll call Validates. And, as one might suspect from

Oak as a whole, it does support


the way the Associates method worked to help define the link
between Blog and Comment objects (meaning, it’s a predefined

the idea of ripping out bits of it.


name for which Oak knows to look), the Validates method works
in the same way—if an object defines a Validates method, then Oak
will call the already-defined IsValid method on the object, which
in turn will look for a Validates method and ask it for all the con- Each of the Conditional objects is making use of a property on the
ditions that make this object valid. Presence object—along with a lambda that yields a true/false value—to
In code, that looks like this: indicate whether the Presence validates successfully. In the first case,
IEnumerable<dynamic> Validates() Presence returns true (pass) if d.PaidWithCard, a local method that
{ returns true if the PaymentType field equals “Card,” returns true.
// And define the association
// For other examples of validations, check out the Oak wiki In the second case, Presence returns true unless isDigitalPurchase
yield return new Uniqueness("Name", blogs); returns true, meaning that if it’s a digital item, no address is necessary.
}
Again, you see the use of a stream of objects that describe the val- All of these are ready for use with any Oak DynamicModel-derived
idation requirements, handed back as an IEnumerable<dynamic> object, and, as noted in the prior column (msdn.microsoft.com/magazine/
dn519929) and in the introduction of this one, the DynamicModel-
to the stream, generated through the use of the “yield return”
facility of C#. And, as with the Schema class from last time, the derived object needn’t explicitly define the fields that these valida-
way to extend this is to just tack on additional elements that are tions reference. Should these validations not be sufficient to the task,
“yield returned,” like so: by the way, these are all defined in the Validations.cs file inside the
Oak folder of the scaffolded project. It’s pretty straightforward to
IEnumerable<dynamic> Validates()
{ define a new one if desired: Just inherit from Oak.Validation and
// And define the association define at minimum a Validate method that returns true/false. The
// For other examples of validations check out the Oak wiki
yield return new Uniqueness("Name", blogs); Exclusion validation, for example, is this:
yield return new Length("Name") { Minimum=10, Maximum=199 }; public class Exclusion : Validation
} {
The Oak Wiki defines the full list and usage of validation objects, public Exclusion(string property)
: base(property)
but some of the notable ones are: {
• Presence: A field is not optional and must be present on }
the object. public dynamic[] In { get; set; }
• Acceptance: A field on the object must contain a par- public bool Validate(dynamic entity)
ticular value, such as a LegalDocument object contain- {
return !In.Contains(PropertyValueIn(entity) as object);
ing a TypedOutAcceptance field in which the user typed }
the string “I Accept” in order to indicate he accepted the }
legal restrictions. The In property in this code is the field in which the excluded
• Exclusion: A field can’t include certain values. values are stored; beyond that, this is pretty straightforward. If a
• Inclusion: A field must be one of a set of certain values. descriptive error message needs to be included, Validation provides
64 msdn magazine The Working Programmer
a base property, ErrorMessage, in which a descriptive message can DynamicModel, an altered version of Massive (Dynamic-
be stored for use if validation fails. Repository) and the Oak core dynamic construct Gemini.
(For those who are curious about the “associations” from • install-package seed: This is the schema generation of
the database discussions last time, these are defined in Oak. This NuGet package also includes the altered version
Association.cs in the same folder, derive from Oak.Association, of Massive (used to insert sample data). It doesn’t contain
and—as one might expect—are a little bit trickier to explain. any of the MVC model binders, or the Oak DynamicModel
Fortunately, Oak has most of the traditional relational associations or supporting classes.
already defined, so there shouldn’t be much need to customize here.) • install-package gemini: This will install just the core
dynamic construct upon which all the dynamic goodness
Pieces of Oak in Oak is built.
Sometimes parts of a library seem really cool, but obstacles stand in

This is one of those


the way of adopting the whole thing, and you just wish you could
rip out a small part of it and keep going. While it’s usually better

times when a whole lot of


to use Oak as a whole, it does support the idea of ripping out bits
of it (such as the dynamic database portions, or perhaps just the

interesting functionality and


dynamic object portions, called Gemini, which I covered in the
August 2013 issue, at msdn.microsoft.com/magazine/dn342877) and using
them standalone, without the rest of the system. The Oak GitHub
page on the subject (bit.ly/1cjGuou) has the NuGet packages for each ideas come out of a pretty small
of the standalone Oak parts, reproduced here (as of this writing)
for your convenience: (relatively speaking) package.
• install-package oak: This is the full Oak suite, it includes
MVC model binders, schema generation, the Oak Before trying out any of them in pieces, I’d suggest trying the
DynamicModel and supporting classes, an altered whole experience to get a feel for how each of them fit into the
version of Massive (DynamicRepository), and the Oak larger picture.
core dynamic construct Gemini.
• install-package oak-json: This is the part of Oak with Benefits, Cost and Pain
regard to JSON serialization (can be used in REST APIs). As might be inferred from these four columns, there are definite
• install-package cambium: This is the part of Oak that benefits to being able to just “wing it” and work with a more dynam-
excludes the MVC-specific components and excludes schema ically typed system. Without question, costs and pain will raise their
generation. Cambium includes the Oak DynamicDb, ugly heads in such a system (particularly for the unwary, and those
unused to writing tests), but even those who are the most diehard
Figure 1 Conditional Validation statically typed bigots can learn some valuable ideas from a system
public class Order : DynamicModel
like Oak. More important, Oak can be a hugely valuable tool for
{ prototyping the early development of a system, when the object
public Order()
{
model is still highly mutable and undefined. Best of all, thanks to
the underlying platform of Oak (that is, .NET), it becomes quite
}
feasible to suggest building an MVC app in Oak in the early stages,
public IEnumerable<dynamic> Validates() then slowly flipping parts of it over to a more statically typed (and,
{
yield return new Presence("CardNumber") {
thus, compiler-checked and compiler-enforced) approach as the
If = d => d.PaidWithCard() details of the application get more tightly locked down.
};
Personally speaking, without a doubt, Oak is a cool little project.
yield return new Presence("Address") { To my mind, this is one of those times when a whole lot of interesting
Unless = d => d.IsDigitalPurchase()
};
functionality and ideas come out of a pretty small (relatively speaking)
} package. Oak definitely goes into my personal toolbox of tricks.
public bool PaidWithCard()
Happy coding!
{
// Could use This().PaymentType instead
return _.PaymentType == "Card"; T ED N EWARD is the principal of Neward & Associates LLC. He has written
more than 100 articles and authored and coauthored a dozen books, including
}
“Professional F# 2.0” (Wrox, 2010). He’s an F# MVP and speaks at confer-
public bool IsDigitalPurchase() ences around the world. He consults and mentors regularly—reach him at
[email protected] if you’re interested in having him come work with your team,
{ or read his blog at blogs.tedneward.com.
// Could use This().ItemType instead
return _.ItemType == "Digital";

}
}
THANKS to the following technical expert for reviewing this article:
Amir Rajan (Oak project creator)
msdnmagazine.com March 2014 65
MODERN APPS RACHEL APPEL

A Look at the Hub Project and Control in


Windows Store Apps
When it comes to development on Windows with Visual Studio, sample data, and \js\navigator.js, which performs navigation. For a
the built-in project templates are a good place to start. If you’re new refresher on navigation, see my August 2013 column, “Navigation
to Windows Store (or any Microsoft stack) development, the tem- Essentials in Windows Store Apps,” at msdn.microsoft.com/magazine/
plates can serve as a learning tool. In this article, I’ll look at the Hub dn342878. In short, the Hub project template, like other templates,
control, but in context of the Hub project template. I’ll examine all is a quick way to publish visually interesting modern apps.
the important things to know about the Hub project and control
for both HTML and XAML apps.
The Hub project in particular enables you to deliver a large
volume of content to the user while using a modern UX. This is
because you can break the app’s content into parts called HubSections,
so the app doesn’t overwhelm the user visually with large amounts
of data. While this is just my opinion, I find the Hub project to be
the most aesthetically interesting of all the Windows Store app
templates. The content layout is in distinct sections that are easy
to digest. You can parade a favorite piece of content in the front-
and-center “hero” section of the hub, while the remaining content
items are easily accessible in groups.

The Hub control is what you


use to create a modern layout
that’s more than just boring
groups of squares.
Of course, it’s not mandatory that you use the templates—you can
start from a blank project. However, for many developers, it’s far easier to
customize and expand upon the templates, as the code is set up for you.

The Hub Project Template


Visual Studio 2013 contains Hub project templates for both
HTML and XAML. Upon creating a new HTML project using the
template, you’ll see some familiar project folders such as the css,
images and js folders. In addition to the customary folders are the
Hub-specific folders: pages\hub, pages\item and pages\section.
As you might expect, each of these folders contains files that
correspond to their purpose in the app. In the project root is the file
for the package manifest as well as default.html, the app’s starting
point, which loads default.js and performs functions related to the
app and lifecycle management. Default.html contains references Figure 1 The Hub Control at Run Time for Both HTML and
to not just the \js\default.js file but also \js\data.js, which contains XAML Apps

66 msdn magazine
expertise worth sharing
Washington State Convention Center s April 1-3, 2014

Register now at alm-forum.com follow us on

keynote speakers
Scott Ambler Steve Denning
ALM Thought Leader and Co-creator of DAD Framework Award-winning Author

Disciplined Agile Delivery: The Foundation for Scaling Agile Transforming Management through Agile

Ken Schwaber Sam Guckenheimer


Industry Legend and Co-Creator of Scrum Product Owner, Microsoft Visual Studio

The State of Agile Transforming software development in a world of devices


and services

plenary speakers
Mike Brittain James Whittaker Dave West
Director of Engineering, Etsy Distinguished Technical Evangelist, Microsoft Chief Product Officer, Tasktop

Principles and Practices of Continuous Intent Commerce Lean ALM


Deployment

sponsors

diamond platinum

Untitled-1 1 2/10/14 4:04 PM


Of course, the centerpiece of the Hub project is the Hub control. Assets, Common, DataModel and Strings. These folders contain what
While default.html is the project starting point in an app built with the you might expect: assets such as graphics, data in the DataModel
Windows Library for JavaScript (WinJS), once it loads, it immediately folder and localized strings in the Strings folder. In the root of the
navigates to the hub.html file. Hub.html contains the Hub control project lies the following working files needed so the app can run:
and lives in the \pages\hub directory. The Hub control is what you • App.xaml/.cs: This is the XAML equivalent of
use to create a modern layout that’s more than just boring groups of default.html. It has a tiny bit of code that assists in
squares. Instead, the Hub control, coupled with asynchronous data navigation and general tasks.
fetching, enables you to present large amounts of data—or data that • HubPage.xaml/.cs: This is the crowning jewel of the app,
has distinct groups—in an organized yet fashionable manner. containing the Hub control.
The Hub template implements the hub, or hierarchical, naviga- • ItemPage.xaml/.cs: This contains the individual items
tional pattern. This means that from the starting point (that is, hub you can navigate to from the hub or section pages.
page), the user can navigate to a page containing all the members • SectionPage.xaml/.cs: This shows all individual data
of a particular section, or the user can navigate to an individual members that belong to a particular group.
item from the hub page. The template also contains navigation • Package.appmanifest: This contains the app settings.
to an item page from a section page. While the template contains

In HTML apps, the Hub


navigation code only between section 3 and its groups and items
(see Figure 1), you can use the ListView or Repeater controls to

control works just like any other


do the same type of navigation for other sections if it makes sense
for your app. Figure 1 illustrates what the default Hub app with

WinJS control.
sample data looks like at run time.
With the reimagining of Windows came the notion of putting
content front and center, and, as you can see, this template does
just that. The XAML Hub project template’s HubPage.xaml file reveals the
The XAML Hub template project works the same conceptually Hub control firmly seats itself in a Grid control that serves as the
as does the HTML template, relying on the hub as the main entry root container for the page and Hub.
point, being navigable to sections and details. Of course, the In the DataModel folder is a file named SampleData.json con-
implementation is different, and you can see this by examining taining sample data. Also in the folder is a SampleDataSource.cs file
the folder structure, which reveals the following directories: that transforms the JSON data into usable classes for C# or Visual
Figure 2 The HTML that Creates the Hub Control
<div class="hub" data-win-control="WinJS.UI.Hub"> "data-win-options="{ onheaderinvoked:
<div class="hero" data-win-control="WinJS.UI.HubSection"></div> select('.pagecontrol').winControl.section3HeaderNavigate }">
<div class="section1" data-win-control="WinJS.UI.HubSection" <div class="itemTemplate" data-win-control="WinJS.Binding.Template">
data-win-options="{ isHeaderStatic: true }" <img src="#" data-win-bind="src: backgroundImage; alt: title" />
data-win-res="{ winControl: {'header': 'Section1'} }"> <div class="win-type-medium" data-win-bind="textContent: title"></div>
<img src="/images/gray.png" width="420" height="280" /> <div class="win-type-small"
<div class="subtext win-type-x-large" data-win-res=" data-win-bind="textContent: description"></div>
{ textContent: 'Section1Subtext' }"></div> </div>
<div class="win-type-medium" <div class="itemslist win-selectionstylefilled" data-win-control=
data-win-res="{ textContent: 'DescriptionText' }"></div> "WinJS.UI.ListView" data-win-options=
<div class="win-type-small"> "{layout: {type: WinJS.UI.GridLayout},
<span data-win-res="{ textContent: 'Section1Description' }"></span> selectionMode: 'none',
<span data-win-res="{ textContent: 'Section1Description' }"></span> itemTemplate: select('.section3 .itemTemplate'), itemDataSource:
<span data-win-res="{ textContent: 'Section1Description' }"></span> select('.pagecontrol').winControl.section3DataSource, oniteminvoked:
</div> select('.pagecontrol').winControl.section3ItemNavigate
</div> }">
<div class="section2" data-win-control="WinJS.UI.HubSection" </div>
data-win-options="{ isHeaderStatic: true }" </div>
data-win-res="{ winControl: {'header': 'Section2'} }"> <div class="section4" data-win-control="WinJS.UI.HubSection"
<div class="item-title win-type-medium" data-win-options="{ isHeaderStatic: true }"
data-win-res="{ textContent: 'Section2ItemTitle' }"></div> data-win-res="{ winControl: {'header': 'Section4'} }">
<div class="article-header win-type-x-large" <div class="top-image-row">
data-win-res="{ textContent: 'Section2Subtext' }"></div> <img src="/images/gray.png" />
<div class="win-type-xx-small" </div>
data-win-res="{ textContent: 'Section2ItemSubTitle' }"></div> <div class="sub-image-row">
<div class="win-type-small"> <img src="/images/gray.png" />
<span data-win-res="{ textContent: 'Section2Description' }"></span> <img src="/images/gray.png" />
<span data-win-res="{ textContent: 'Section2Description' }"></span> <img src="/images/gray.png" />
<span data-win-res="{ textContent: 'Section2Description' }"></span> </div>
<span data-win-res="{ textContent: 'Section2Description' }"></span> <div class="win-type-medium"
<span data-win-res="{ textContent: 'Section2Description' }"></span> data-win-res="{ textContent: 'DescriptionText' }"></div>
<span data-win-res="{ textContent: 'Section2Description' }"></span> <div class="win-type-small">
</div> <span data-win-res="{ textContent: 'Section4Description' }"></span>
</div> <span data-win-res="{ textContent: 'Section4Description' }"></span>
</div>
<div class="section3" data-win-control="WinJS.UI.HubSection" </div>
data-win-res="{ winControl: {'header': 'Section3'} } </div>

68 msdn magazine Modern Apps


Basic .NET consumption and XAML data binding. You can replace The Hub Control
this with your own data, much like the data.js file in WinJS apps. Both HTML and XAML project templates use the Hub control.
The Common folder contains several files that perform a variety In HTML apps, the Hub control works just like any other WinJS
of tasks such as navigation and other generally app-related tasks for control. Use the data-win-control attribute of an HTML element,
working with data in view models. In addition, the Common folder usually a <div>, to define it as a Hub control, as this code shows:
contains the SuspensionManager.cs file, which performs process <div class="hub" data-win-control="WinJS.UI.Hub"></div>
lifecycle tasks. Finally, the Strings folder contains localized strings This means the WinJS.UI.Hub object is the brains behind the
for publishing in different locales. Hub control. The Hub control acts as a container for the HubSection
Figure 3 The XAML for a Hub Control
<Hub SectionHeaderClick="Hub_SectionHeaderClick"> lorem dolor amet sed consectetuer ising elit, sed diam non
<Hub.Header> my nibh uis mod wisi quip."/>
<!-- Back button and page title --> <TextBlock Style="{StaticResource SubtitleTextBlockStyle}"
<Grid> Grid.Row="2" Margin="0,20,0,0" x:Uid="ItemSubTitle"
<Grid.ColumnDefinitions> Text="Item Sub Title"/>
<ColumnDefinition Width="80"/> <TextBlock Style="{StaticResource BodyTextBlockStyle}" Grid.Row="3"
<ColumnDefinition Width="*"/> x:Uid="LongText" Text="Lorem ipsum dolor sit amet..."/>
</Grid.ColumnDefinitions> </Grid>
<Button x:Name="backButton" Style= </DataTemplate>
"{StaticResource NavigationBackButtonNormalStyle}" </HubSection>
Margin="-1,-1,39,0" <HubSection IsHeaderInteractive="True"
VerticalAlignment="Top" DataContext="{Binding Section3Items}" d:DataContext="{Binding Groups[3],
Command="{Binding NavigationHelper.GoBackCommand, Source={d:DesignData Source=/DataModel/SampleData.json,
ElementName=pageRoot}" Type=data:SampleDataSource}}" x:Uid="Section3Header" Header="Section 3"
AutomationProperties.Name="Back" Padding="40,40,40,32">
AutomationProperties.AutomationId="BackButton" <DataTemplate>
AutomationProperties.ItemType="Navigation Button"/> <GridView
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" x:Name="itemGridView"
Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" ItemsSource="{Binding Items}"
VerticalAlignment="Top" IsHitTestVisible="false" Margin="-9,-14,0,0"
TextWrapping="NoWrap" /> AutomationProperties.AutomationId="ItemGridView"
</Grid> AutomationProperties.Name="Items In Group"
</Hub.Header> ItemTemplate="{StaticResource Standard310x260ItemTemplate}"
<HubSection Width="780" Margin="0,0,80,0"> SelectionMode="None"
<HubSection.Background> IsSwipeEnabled="false"
<ImageBrush ImageSource="Assets/MediumGray.png" IsItemClickEnabled="True"
Stretch="UniformToFill" /> ItemClick="ItemView_ItemClick">
</HubSection.Background> </GridView>
</HubSection> </DataTemplate>
<HubSection Width="500" x:Uid="Section1Header" Header="Section 1"> </HubSection>
<DataTemplate> <HubSection x:Uid="Section4Header" Header="Section 4">
<Grid> <DataTemplate>
<Grid.RowDefinitions> <!-- width of 400 -->
<RowDefinition Height="Auto" /> <StackPanel Orientation="Vertical">
<RowDefinition Height="Auto" /> <Grid>
<RowDefinition Height="Auto" /> <Grid.ColumnDefinitions>
<RowDefinition Height="*" /> <ColumnDefinition Width="130"/>
</Grid.RowDefinitions> <ColumnDefinition Width="5"/>
<Image Source="Assets/MediumGray.png" Stretch="Fill" <ColumnDefinition Width="130"/>
Width="420" Height="280"/> <ColumnDefinition Width="5"/>
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" <ColumnDefinition Width="130"/>
Grid.Row="1" Margin="0,10,0,0" TextWrapping="Wrap" </Grid.ColumnDefinitions>
x:Uid="Section1Subtitle" Text="Lorem ipsum dolor sit nonumy <Grid.RowDefinitions>
sed consectetuer ising elit, sed diam"/> <RowDefinition Height="270"/>
<TextBlock Style="{StaticResource TitleTextBlockStyle}" <RowDefinition Height="95"/>
Grid.Row="2" Margin="0,10,0,0" x:Uid="DescriptionHeader" <RowDefinition Height="Auto" />
Text="Description text:"/> <RowDefinition Height="*" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" </Grid.RowDefinitions>
Grid.Row="3" <Image Source="Assets/MediumGray.png"
x:Uid="Section1DescriptionText" Grid.ColumnSpan="5" Margin="0,0,0,10" Stretch="Fill" />
Text="Lorem ipsum dolor sit amet... "/> <Image Source="Assets/MediumGray.png" Grid.Row="1" Stretch="Fill"/>
</Grid> <Image Source="Assets/MediumGray.png" Grid.Row="1"
</DataTemplate> Grid.Column="2" Stretch="Fill"/>
</HubSection> <Image Source="Assets/MediumGray.png" Grid.Row="1"
<HubSection Width="520" x:Uid="Section2Header" Header="Section 2"> Grid.Column="4" Stretch="Fill"/>
<DataTemplate> <TextBlock Style="{StaticResource TitleTextBlockStyle}"
<Grid> Grid.Row="2" Grid.ColumnSpan="5" Margin="0,15,0,0"
<Grid.RowDefinitions> x:Uid="DescriptionHeader" Text="Description text:"/>
<RowDefinition Height="Auto" /> <TextBlock Style="{StaticResource BodyTextBlockStyle}"
<RowDefinition Height="Auto" /> Grid.Row="3" Grid.ColumnSpan="5" x:Uid="LongText"
<RowDefinition Height="Auto" /> Text="Lorem ipsum dolor sit amet...."/>
<RowDefinition Height="*" /> </Grid>
</Grid.RowDefinitions> </StackPanel>
<TextBlock Style="{StaticResource TitleTextBlockStyle}" </DataTemplate>
Margin="0,0,0,10" x:Uid="ItemTitle" Text="Item Title" /> </HubSection>
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" </Hub>
Grid.Row="1" x:Uid="Section2UnderTitle" Text="Quisque in porta

msdnmagazine.com March 2014 69


Figure 4 The CSS That Shapes and Styles the HTML Hub Control
.hubpage header[role=banner] { .hubpage .hub .section2 .article-header {
position: relative; margin-bottom: 15px;
z-index: 2; }
} .hubpage .hub .section3 {
.hubpage section[role=main] { }
-ms-grid-row: 1; .hubpage .hub .section3 .itemslist {
-ms-grid-row-span: 2; height: 100%;
z-index: 1; margin-left: -10px;
} margin-right: -10px;
.hubpage .hub .win-hub-surface { margin-top: -5px;
height: 100%; }
} .hubpage .hub .section3 .win-container {
.hubpage .hub .hero { margin-bottom: 36px;
-ms-high-contrast-adjust: none; margin-left: 10px;
background-image: url(/https/www.scribd.com/images/gray.png); margin-right: 10px;
background-size: cover; }
margin-left: -80px; .hubpage .hub .section3 .win-item {
margin-right: 80px; height: 229px;
padding: 0; width: 310px;
width: 780px; }
} .hubpage .hub .section3 .win-item img {
.hubpage .hub .hero:-ms-lang( height: 150px;
ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, margin-bottom: 10px;
syr, ug, ur, qps-plocm) { width: 310px;
margin-left: 80px; }
margin-right: -80px; .hubpage .hub .section4 {
} width: 400px;
.hubpage .hub .hero .win-hub-section-header { }
display: none; .hubpage .hub .section4 .win-hub-section-content {
} overflow-y: hidden;
.hubpage .hub .section1 { }
width: 420px; .hubpage .hub .section4 .top-image-row {
} height: 260px;
.hubpage .hub .section1 .win-hub-section-content { margin-bottom: 10px;
overflow-y: hidden; width: 400px;
} }
.hubpage .hub .section1 .subtext { .hubpage .hub .section4 .top-image-row img {
margin-bottom: 7px; height: 100%;
margin-top: 9px; width: 100%;
} }
.hubpage .hub .section2 { .hubpage .hub .section4 .sub-image-row {
width: 440px; margin-bottom: 20px;
} display: -ms-flexbox;
.hubpage .hub .section2 .win-hub-section-content { -ms-flex-flow: row nowrap;
overflow-y: hidden; -ms-flex-pack: justify;
} }
.hubpage .hub .section2 .item-title { .hubpage .hub .section4 .sub-image-row img {
margin-top: 4px; height: 95px;
margin-bottom: 10px; width: 130px;
} }

controls, which define sections or groups of data. HubSections can As you can see, XAML syntax is a bit more verbose than HTML.
contain any valid HTML tags, such as <div> or <img>, or a WinJS That’s because you code layout and styles right in the XAML page
control, such as the ListView control. By default, the hub.html file’s (though XAML styles can be placed in a resource dictionary), while
Hub control encloses five sections, one named hero and four more in HTML the layout and style rules are CSS (more on styling later).
designated by their class attributes (such as section1, section2 and
so on). In the HubSections, the <div> and <img> tags are the most Data Binding and the Hub Control
common child elements, but any valid HTML or WinJS controls Arrays or JSON (which usually serializes to an array anyway) are
will work to display data in a different layout. Changing the layout the customary ways to work with data in WinJS, as well as in many
is a great way to personalize your app, but don’t forget to adhere other Web or client languages. This is no different with the Hub
to the Windows UX guidelines at bit.ly/1gBDHaW. Figure 2 shows a project. You can replace the data in \js\data.js with custom data
complete sample of the necessary HTML (you’ll see its CSS later) broken into however many groups you plan to use. You’ll find two
to create a Hub control with five sections. Inspecting the code in arrays as sample data in the data.js file, one for grouping and one
Figure 2 shows section 3 is the navigable section, while the rest for individual items that tie into a specific group. If you’re familiar
are not navigable. with some of the other WinJS project templates, then you’ll notice
In XAML, the Hub control uses a <Hub> element that contains this is the same sample data.
<Hub.Header> and <HubSection> elements. In turn, the child head- In the \pages\hub\hub.js file are the calls to the members of the
ings and sections contain Grid and other XAML controls, such as the Data namespace that obtain group and item data:
StackPanel, as well as text blocks. Figure 3 shows the XAML required var section3Group = Data.resolveGroupReference("group4");
var section3Items = Data.getItemsFromGroup(section3Group);
to create the Hub control used in the Visual Studio templates.
70 msdn magazine Modern Apps
The section3Group and section3Items are global objects. of the Hub control. The hero fills roughly half of the left side of
Figure 2 shows the data-binding syntax for the ListView control. In the viewable part of screen with a light gray background and, of
hub.js, after the ready function, the code sets section3DataSource, course, it’s a great way to put a special piece of content where no
a property of the Hub control: user can miss it! You can fill it with lots of data, and graphic data or
section3DataSource: section3Items.dataSource, multimedia works quite nicely to show off here.
The Hub control uses the preceding code to data bind to the As you can see, the CSS in Figure 4 shapes and styles the Hub
ListView (Figure 2 shows the data-bound ListView code). control, and most of it deals with the layout and sizing of the
HubSections. Elements and WinJS controls inside the HubSections

In Windows Store apps built with apply the styles from ui-light.css or ui-dark.css, until you overwrite
them with your own styles.

JavaScript, you can style the Hub HTML apps rely on CSS for styling. XAML apps rely on XAML
for styling. This means that XAML has several attributes you

control with CSS. apply to tags to enforce styling definitions called resources. For
example, the code that styles a TextBlock is the Style attribute and
it references a built-in (static resource dictionary) style named
In XAML apps using C#, you have the same basic occurrences, SubheaderTextBlockStyle:
as code from the HubPage.xaml.cs file indicates the following <TextBlock Style="{StaticResource SubheaderTextBlockStyle} />
declaration for a view model of type ObservableDictionary, along The layout of a page is also XAML, as all the Hubs, Grids and other
with its corresponding property declaration (this is where you can elements contain inline coordinates for their on-screen position as
return your own data): well as size. You can see throughout Figure 3 there are margins, posi-
private ObservableDictionary defaultViewModel = new ObservableDictionary(); tioning, and row and column settings that position elements, all inline
public ObservableDictionary DefaultViewModel
{ in the XAML. HTML is originally a Web technology, and conserving
get { return this.defaultViewModel; } bandwidth by using CSS instead of HTML is a real benefit. Here in
}
the land of XAML, it’s all client-side, so UI caching isn’t so much of
Later in the file, code sets a page-level view model by calling
an issue and styles can go inline. A nice upside of XAML is that you
GetGroupAsync, which, as its name implies, runs asynchronously:
need to do very little to ensure a responsive design. Just be sure to set
var sampleDataGroup = await SampleDataSource.GetGroupAsync("Group-4");
two <RowDefinition> elements to a height of “Auto” and “*”:
Although the call obtains Group-4 data, you assign it to a view
<Grid.RowDefinitions>
model named Section3Items to assign it to those items. Consider <RowDefinition Height="Auto"/>
the hero section as Section 0, meaning the Section 3 items will align <RowDefinition Height="*"/>
</Grid.RowDefinitions>
with the Group-4 data:
The rows will automatically respond to app view state changes,
this.DefaultViewModel["Section3Items"] = sampleDataGroup;
making the layout fluid while saving extra code. Figure 3 shows a
This is all you need in the codebehind. In XAML, notice the
few references to auto-height row definitions.
DataContext attribute binds to Section3Items.The other attributes
aren’t necessary for data binding, but act as an aid for the design
tools in Visual Studio or Blend, as designated by the “d” namespace:
Samples Available
<HubSection IsHeaderInteractive="True" DataContext="{Binding Section3Items}"
Once you’ve modified the Hub control, performed data retrieval
d:DataContext="{Binding Groups[3], Source={d:DesignData and binding, and set styles, you’re good to go. Don’t forget to add
Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"
x:Uid="Section3Header" Header="Section 3" Padding="40,40,40,32">
modern touches such as tiles, search and other Windows integra-
While working with local sample data, you have many options tion to your app. The Hub project template is an easy way to build
for data access, including File IO, SQLite, Web Storage, IndexedDB, and publish apps quickly, whether in HTML or XAML. Using the
REST services and Windows Azure, to name a few. If you want to hub navigational pattern with the Hub control enables you to build
review what data options are available, see my March 2013 arti- an effective and rich UX that adheres to modern UI principles.
cle, “Data Access and Storage Options in Windows Store Apps,” You can download Hub control samples covering many aspects of
at msdn.microsoft.com/magazine/jj991982. Windows app development at the following locations:
• HTML sample: bit.ly/1m0sWTE
Styling the Hub Control • XAML sample: bit.ly/1eGsVAH Q

In Windows Store apps built with JavaScript, you can style the Hub
control with CSS. The \hub\hub.css file contains all the default RACHEL APPEL is a consultant, author, mentor and former Microsoft employee with
CSS related to the Hub control. Feel free to add your own styles to more than 20 years of experience in the IT industry. She speaks at top industry
conferences such as Visual Studio Live!, DevConnections, MIX and more. Her
change the size of the elements or their layout. Figure 4 shows the
expertise lies within developing solutions that align business and technology
complete CSS in hub.css. Notice there’s a .hubpage class selector that focusing on the Microsoft dev stack and open Web. For more about Appel, visit
uses HTML5 semantic role attributes such as header[role=banner] her Web site at rachelappel.com.
and section[role=main] to designate the general styles for the hub.
After that, the CSS in Figure 4 shows the “.hubpage .hub .hero” THANKS to the following technical expert for reviewing this article:
descendant selector, which creates the featured (hero) section Frank La Vigne (Microsoft)

msdnmagazine.com March 2014 71


March 10 - 14, 2014 | Las Vegas, NV Planet Hollywood Resort & Casino

The Developer World is always changing; new technologies emerge,


current ones evolve and demands on your time grow. Live! 360 DEV
offers comprehensive training through 5 co-located events on the
most relevant and leading edge technologies in your world today.
You'll learn from pre-eminent experts in the industry, network with
like-minded peers, and return home with the knowledge and
solutions you need to tackle your biggest development challenges.

Untitled-4 2 1/27/14 11:20 AM


live360events.com/lasvegas

Live! 360 DEV Explores:


£Visual Studio Live! – May the Code be with you. The most trusted
source in the universe for .NET Training for 21 years and counting.

£Web Dev Live! – NEW EVENT! Web Dev Live! will dive deep to
explore all that is HTML5, JavaScript and ASP.NET.

£Modern Apps Live! – Launch your mobile, cross-device & cloud development
training here.

£SharePoint Live! – Set your course for collaboration with these sessions
designed strictly for devs.

£SQL Server Live! – Your Mission? Conquering coding against SQL Server.


This means you have Àve events with over a hundred sessions to choose from –
mix and match sessions to create your own, custom event line-up - it’s like no
other dev conference available today.

Scan the QR code to


register or for more
CONNECT WITH LIVE! 360
event details.
ttwitter.com/live360events
ffacebook.com/live360events
JJoin the "Live! 360" Group

SESSIONS ARE FILLING UP QUICKLY


REGISTER TODAY!
Use promo code DEVMAR2

PLATINUM SPONSOR SUPPORTED BY PRODUCED BY

magazine

Untitled-4 3 1/27/14 11:20 AM


DIRECTX FACTOR CHARLES PETZOLD

Triangles and Tessellation

The triangle is the most basic two-dimensional figure. It’s nothing In contrast, triangles aren’t found at all in most 2D graphics pro-
more than three points connected by three lines, and if you try to gramming interfaces, where the most common two-dimensional
make it any simpler, it collapses into a single dimension. On the primitives are lines, curves, rectangles and ellipses. So it’s somewhat
other hand, any other type of polygon can be decomposed into a surprising to find triangles pop up in a rather obscure corner of
collection of triangles. Direct2D. Or maybe it’s really not that surprising: Because Direct2D
Even in three dimensions, a triangle is always flat. Indeed, one is built on top of Direct3D, it seems reasonable for Direct2D to
way to define a plane in 3D space is with three non-collinear points, take advantage of the triangle support in Direct3D and the GPU.
and that’s a triangle. A square in 3D space isn’t guaranteed to be The triangle structure defined in Direct2D is simple:
flat because the fourth point might not be in the same plane as struct D2D1_TRIANGLE
{
the other three. But that square can be divided into two triangles, D2D1_POINT_2F point1;
each of which is flat, although not necessarily on the same plane. D2D1_POINT_2F point2;
D2D1_POINT_2F point3;
};

In Direct2D, tessellation is the


As far as I can determine, this structure is used in Direct2D only
in connection with a “mesh,” which is a collection of triangles stored

process of decomposing a two-


in an object of type ID2D1Mesh. The ID2D1RenderTarget (from
which ID2D1DeviceContext derives) supports a method named

dimensional area into triangles.


CreateMesh that creates such an object:
ID2D1Mesh * mesh;
deviceContext->CreateMesh(&mesh);
(To keep things simple, I’m not showing the use of ComPtr or
In 3D graphics programming, triangles form the surfaces of solid checking HRESULT values in these brief code examples.) The
figures, starting with the simplest of all three-dimensional figures, ID2D1Mesh interface defines a single method named Open. This
the triangular pyramid, or tetrahedron. Assembling a seemingly method returns an object of type ID2D1TessellationSink:
ID2D1TessellationSink * tessellationSink;
solid figure from triangle “building blocks” is the most fundamental mesh->Open(&tessellationSink);
process in 3D computer graphics. Of course, the surfaces of In general, “tessellation” refers to the process of covering a surface
real-world objects are often curved, but if you make the triangles with a mosaic pattern, but the term is used somewhat differently in
small enough, they can approximate curved surfaces to a degree
sufficient to fool the human eye. Figure 1 The Relevant Code of InterrogableTessellationSink
The illusion of curvature is enhanced by exploiting another
// ID2D1TessellationSink methods
characteristic of triangles: If the three vertices of a triangle are void InterrogableTessellationSink::AddTriangles(_In_ const D2D1_TRIANGLE *triangles,
associated with three different values—for example, three different UINT trianglesCount)
{
colors or three different geometric vectors—these values can be for (UINT i = 0; i < trianglesCount; i++)
interpolated over the surface of the triangle and used to color that {
m_triangles.push_back(triangles[i]);
surface. This is how triangles are shaded to mimic the reflection of }
light seen in real-world objects. }

Triangles in Direct2D
HRESULT InterrogableTessellationSink::Close()
{
// Assume the class accessing the tessellation sink knows what it's doing
Triangles are ubiquitous in 3D computer graphics. Much of the return S_OK;
work performed by a modern graphics processing unit (GPU) }
involves rendering triangles, so of course Direct3D programming // Method for this implementation
involves working with triangles to define solid figures. std::vector<D2D1_TRIANGLE> InterrogableTessellationSink::GetTriangles()
{
return m_triangles;
Code download available at msdn.microsoft.com/magazine/msdnmag0314. }

74 msdn magazine
profound difference between ID2D1Geometry and ID2D1Mesh
objects that’s revealed by how you create these two objects.
Geometries are mostly just collections of coordinate points, so
geometries are device-independent objects. You can create various
types of geometries by calling methods defined by ID2D1Factory.
A mesh is a collection of triangles, which are just triplets of
coordinate points, so a mesh should be a device-independent
object as well. But you create an ID2D1Mesh object by calling a
method defined by ID2D1RenderTarget. This means the mesh is
a device-dependent object, like a brush.
This tells you the triangles that comprise the mesh are stored
in a device-dependent manner, most likely in a form suitable for
Figure 2 A Rounded Rectangle Decomposed into Triangles processing by the GPU, or actually on the GPU. And this means
that FillMesh should execute much faster than FillGeometry for
Direct2D and Direct3D programming. In Direct2D, tessellation is the equivalent figure.
the process of decomposing a two-dimensional area into triangles. Shall we test that hypothesis?
The ID2D1TessellationSink interface has just two methods: Add- Among the downloadable code for this article is a program
Triangles (which adds a collection of D2D1_TRIANGLE objects to named MeshTest that creates a path geometry for a 201-point star,
the collection) and Close, which makes the mesh object immutable. and slowly rotates it while calculating and displaying the frame rate.
Although your program can call AddTriangles itself, often it will When the program is compiled in Debug mode for the x86 and
pass the ID2D1TessellationSink object to the Tessellate method runs on my Surface Pro, I get a frame rate of less than 30 frames
defined by the ID2D1Geometry interface: per second (FPS) when rendering the path geometry (even if the
geometry->Tessellate(IdentityMatrix(), tessellationSink); geometry is outlined to eliminate overlapping areas and flattened
tessellationSink->Close();
to eliminate curves), but the frame rate leaps up to 60FPS when
The Tessellate method generates triangles that cover the areas
rendering the mesh.
enclosed by the geometry. After you call the Close method, the sink
can be discarded and you’re left with an ID2D1Mesh object. The
process of generating the contents of an ID2D1Mesh object using
an ID2D1TessellationSink is similar to defining an ID2D1Path-
Geometries are mostly
Geometry using an ID2D1GeometrySink. just collections of coordinate
You can then render this ID2D1Mesh object using the FillMesh
method of ID2D1RenderTarget. A brush governs how the mesh points, so geometries are
is colored:
deviceContext->FillMesh(mesh, brush);
Keep in mind that these mesh triangles define an area and not
device-independent objects.
an outline of an area. There is no DrawMesh method.
Conclusion: For complex geometries, it makes sense to convert
FillMesh has a limitation: Anti-aliasing can’t be enabled when
them to meshes for rendering. If the need to disable anti-aliasing
FillMesh is called. Precede FillMesh with a call to SetAntialiasMode:
deviceContext->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
to render this mesh is a deal-breaker, you might want to check out
You might wonder: What’s the point? Why not just call ID2D1GeometryRealization, introduced in Windows 8.1. This
FillGeometry on the original geometry object? The visuals should combines the performance of ID2D1Mesh but allows anti-aliasing.
be the same (aside from the anti-aliasing). But there’s actually a Keep in mind meshes and geometry realizations must be recreated if
the display device is recreated, just as with other device-dependent
resources such as brushes.

Examining the Triangles


I was curious about the triangles generated by the tessellation
process. Could they actually be visualized? The ID2D1Mesh object
doesn’t allow you to access the triangles that comprise the mesh,
but it’s possible to write your own class that implements the
ID2D1TessellationSink interface, and pass an instance of that class
to the Tessellate method.
I called my ID2D1TessellationSink implementation Interrogable-
TessellationSink, and it turned out to be embarrassingly simple. It
contains a private data member for storing triangle objects:
Figure 3 Text Decomposed into Triangles std::vector<D2D1_TRIANGLE> m_triangles;

msdnmagazine.com March 2014 75


Most of the code is dedicated to implementing the IUnknown I was also curious about the order in which these triangles were
interface. Figure 1 shows the code required to implement the two generated, and by clicking the Gradient Fill option at the bottom
ID2D1TessellationSink methods and obtain the resultant triangles. left of the window, you can find out. When this option is checked,
I incorporated this class in a project named Tessellation- the program calls FillGeometry for each of the triangle geometries.
Visualization. The program creates geometries of various sorts— A solid color brush is passed to FillGeometry but the color depends
everything from a simple rectangle geometry to geometries on the triangle’s index in the collection.
generated from text glyphs—and uses InterrogableTessellationSink What you’ll find is that the FillGeometry option renders some-
to obtain the collection of triangles created by the Tessellate method. thing akin to a top-down gradient brush, which means that triangles
Each triangle is then converted into an ID2D1PathGeometry are stored in the collection in a visual top-down order. It appears
object consisting of three straight lines. These path geometries are the tessellation algorithm attempts to maximize the width of hor-
then rendered using DrawGeometry. izontal scan lines in the triangles, which probably maximizes the
As you might expect, an ID2D1RectangleGeometry is tessellated rendering performance.
into just two triangles, but the other geometries are more interesting. Although I clearly recognize the wisdom of this approach, I must
Figure 2 shows the triangles that comprise an ID2D1Rounded- confess I was a little disappointed. I was hoping that a widened
RectangleGeometry. Bézier curve (for example) might be tessellated beginning at one
This isn’t the way a human being would tessellate the rounded end of the line and continuing to the other, so the triangles could
rectangle. A human being would probably divide the rounded be rendered with a gradient from one end to the other, which is
rectangle into five rectangles and four quarter-circles, and tessel- not a type of gradient commonly seen in a DirectX program! But
late each of those figures separately. In particular, a human would this was not to be.
slice the four quarter-circles into pie wedges. Interestingly, I needed to turn off anti-aliasing before the Fill-
In other words, a human being would define several more points Geometry calls in TessellationVisualization or faint lines appeared
in the interior of the geometry to aid in the tessellation. But the between the rendered triangles. These faint lines result from the
tessellation algorithm defined by the geometry object doesn’t use anti-aliasing algorithm, which involves partially transparent pixels
any points beyond those created by the flattening of the geometry. that don’t become opaque when overlapped. This leads me to suspect
Figure 3 shows two characters rendered with the Pescadero font that using anti-aliasing with FillMesh isn’t a hardware or software
decomposed into triangles. limitation, but a restriction mandated to avoid visual anomalies.
Figure 4 Tessellation and Rendering Code in SparklingTextRenderer
void SparklingTextRenderer::Tessellate() geometrySink->Close();
{
... m_triangleGeometries.push_back(triangleGeometry);
}
// Tessellate geometry into triangles }
ComPtr<InterrogableTessellationSink> tessellationSink = }
new InterrogableTessellationSink();
pathGeometry->Tessellate(IdentityMatrix(), tessellationSink.Get()); void SparklingTextRenderer::Render()
std::vector<D2D1_TRIANGLE> triangles = tessellationSink->GetTriangles(); {
...
if (m_useMeshesNotGeometries)
{ Matrix3x2F centerMatrix = D2D1::Matrix3x2F::Translation(
// Generate a separate mesh from each triangle (logicalSize.Width - (m_geometryBounds.right + m_geometryBounds.left)) / 2,
ID2D1DeviceContext* context = m_deviceResources->GetD2DDeviceContext(); (logicalSize.Height - (m_geometryBounds.bottom + m_geometryBounds.top)) / 2);

for (D2D1_TRIANGLE triangle : triangles) context->SetTransform(centerMatrix *


{ m_deviceResources->GetOrientationTransform2D());
ComPtr<ID2D1Mesh> triangleMesh; context->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
context->CreateMesh(&triangleMesh);
ComPtr<ID2D1TessellationSink> sink; if (m_useMeshesNotGeometries)
triangleMesh->Open(&sink); {
sink->AddTriangles(&triangle, 1); for (ComPtr<ID2D1Mesh>& triangleMesh : m_triangleMeshes)
sink->Close(); {
float gray = (rand() % 1000) * 0.001f;
m_triangleMeshes.push_back(triangleMesh); m_solidBrush->SetColor(ColorF(gray, gray, gray));
} context->FillMesh(triangleMesh.Get(), m_solidBrush.Get());
} }
else }
{ else
// Generate a path geometry from each triangle {
for (D2D1_TRIANGLE triangle : triangles) for (ComPtr<ID2D1PathGeometry>& triangleGeometry : m_triangleGeometries)
{ {
ComPtr<ID2D1PathGeometry> triangleGeometry; float gray = (rand() % 1000) * 0.001f;
d2dFactory->CreatePathGeometry(&triangleGeometry); m_solidBrush->SetColor(ColorF(gray, gray, gray));
ComPtr<ID2D1GeometrySink> geometrySink; context->FillGeometry(triangleGeometry.Get(), m_solidBrush.Get());
triangleGeometry->Open(&geometrySink); }
geometrySink->BeginFigure(triangle.point1, D2D1_FIGURE_BEGIN_FILLED); }
geometrySink->AddLine(triangle.point2);
geometrySink->AddLine(triangle.point3); ...
geometrySink->EndFigure(D2D1_FIGURE_END_CLOSED); }

76 msdn magazine DirectX Factor


Untitled-1 1 10/13/11 1:15 PM
of three straight lines? Or a FillMesh call with an ID2D1Mesh
containing a single triangle?
I assumed that FillMesh would be more efficient than FillGeom-
etry only if the mesh contained multiple triangles, and it would be
slower for one triangle, so I originally wrote the program to gener-
ate path geometries from the tessellated triangles. Only later did I
add a CheckBox labeled “Use a Mesh for each triangle instead of a
PathGeometry” and incorporated that logic as well.
The strategy in the SparklingTextRenderer class of SparklingText
is to use the GetGlyphRunOutline method of ID2D1FontFace to
obtain a path geometry for the character outlines. The program
then calls the Tessellate method on this geometry with the Inter-
Figure 5 The SparklingText Display rogableGeometrySink to get a collection of D2D1_TRIANGLE
objects. These are then converted into path geometries or meshes
Triangles in 2D and 3D (depending on the CheckBox value) and stored in one of two vector
After working just a little while with ID2D1Mesh objects, I began collections named m_triangleGeometries and m_triangleMeshes.
visualizing all two-dimensional areas as mosaics of triangles. This

After working just a little while


mindset is normal when doing 3D programming, but I had never
extended such a triangle-centric vision to the 2D world.

with ID2D1Mesh objects, I began


The documentation of the Tessellate method indicates the gener-
ated triangles are “clockwise-wound,” which means that the point1,

visualizing all two-dimensional


point2 and point3 members of the D2D1_TRIANGLE structure are
ordered in a clockwise direction. This isn’t very useful information

areas as mosaics of triangles.


when using these triangles in 2D graphics programming, but it
becomes quite important in the 3D world, where the ordering of the
points in a triangle usually indicates the front or back of the figure.
Of course, I’m very interested in using these two-dimensional Figure 4 shows a pertinent chunk of the Tessellate method that
tessellated triangles to break through the third dimension, where fills these collections, and the Render method that renders the
triangles are most comfortably at home. But I don’t want to be in resultant triangles. As usual, HRESULT-checking has been removed
such a rush that I neglect to explore some interesting effects with to simplify the code listings.
tessellated triangles in two dimensions. Based on the video frame rate (which the program displays), my
Surface Pro renders the meshes faster than the path geometries,
Coloring Triangles Uniquely despite the fact that each mesh contains just a single triangle.
For me, the biggest thrill in graphics programming is creating images The animation of the colors is unnervingly reminiscent of a scin-
on the computer screen of a sort I’ve never seen before, and I don’t tillating migraine aura, so you might want to exercise some caution
think I’ve ever seen text tessellated into triangles whose colors change when viewing it. Figure 5 shows a still image from the program,
in a random manner. This happens in a program I call SparklingText. which should be much safer.
Keep in mind that both FillGeometry and FillMesh involve only
a single brush, so if you need to render hundreds of triangles with Moving the Tessellated Triangles
different colors, you’ll need hundreds of FillGeometry or FillMesh The remaining two programs use a strategy similar to Sparkling-
calls, each rendering a single triangle. Which is more efficient? A Text to generate a collection of triangles to form glyph outlines,
FillGeometry call to render an ID2D1PathGeometry that consists but then move the little triangles around the screen.

Figure 6 A Still from the OutThereAndBackAgain Program Figure 7 The TextMorphing Display
78 msdn magazine DirectX Factor
For OutThereAndBackAgain, I envisioned text that would fly contains a single-triangle ID2D1Mesh object, as well as informa-
apart into its composite triangles, which would then come back to tion necessary to take that triangle on a journey outward and back
form the text again. Figure 6 shows this process at 3 percent into again. This journey takes advantage of a feature of geometries you
the flying-apart animation. can use independently of rendering. The ComputeLength method
The CreateWindowSizeDependentResources method in the in ID2D1Geometry returns the total length of a geometry, while
OutThereAndBackAgainRenderer class assembles information ComputePointAtLength returns a point on the curve and a tangent
about each triangle in a structure I call TriangleInfo. This structure to the curve at any length. From that information you can derive
translate and rotate matrices.
Figure 8 Update and Render in TextMorphing As you can see in Figure 6, I used a gradient brush for the text
void TextMorphingRenderer::Update(DX::StepTimer const& timer)
so that triangles of slightly different colors would cross paths
{ and intermingle a bit. Even though I’m using only one brush, the
...
desired effect requires the Render method to call SetTransform and
// Calculate an interpolation factor FillMesh for every single-triangle mesh. The gradient brush is applied
float t = (float)fmod(timer.GetTotalSeconds(), 10) / 10;
t = std::cos(t * 2 * 3.14159f); // 1 to 0 to -1 to 0 to 1
as if the mesh were in its original position prior to the transform.
t = (1 - t) / 2; // 0 to 1 to 0 I wondered if it would be efficient for the Update method to
// Two functions for interpolation
transform all the individual triangles “manually” with calls to the
std::function<D2D1_POINT_2F(D2D1_POINT_2F, D2D1_POINT_2F, float)> TransformPoint method of the Matrix3x2F class, and to consol-
InterpolatePoint =
[](D2D1_POINT_2F pt0, D2D1_POINT_2F pt1, float t)
idate these in a single ID2D1Mesh object, which would then be
{ rendered with a single FillMesh call. I added an option for that, and
return Point2F((1 - t) * pt0.x + t * pt1.x,
(1 - t) * pt0.y + t * pt1.y);
sure enough, it was faster. I woudn’t have imagined that creating
}; an ID2D1Mesh in each Update call would work well, but it does.
std::function<D2D1_TRIANGLE(D2D1_TRIANGLE, D2D1_TRIANGLE, float)>
The visuals are slightly different, however: The gradient brush is
InterpolateTriangle = applied to the transformed triangles in the mesh, so there’s no
[InterpolatePoint](D2D1_TRIANGLE tri0, D2D1_TRIANGLE tri1, float t)
{
intermingling of colors.
D2D1_TRIANGLE triangle;
triangle.point1 = InterpolatePoint(tri0.point1, tri1.point1, t);
triangle.point2 = InterpolatePoint(tri0.point2, tri1.point2, t);
Text Morphing?
triangle.point3 = InterpolatePoint(tri0.point3, tri1.point3, t); Suppose you tessellate the glyph outline geometries of two text
return triangle;
};
strings—for example, the words “DirectX” and “Factor” that make
up the name of this column—and pair up the triangles for inter-
// Interpolate the triangles
int count = m_triangleInfos.size();
polation. An animation could then be defined that transforms one
std::vector<D2D1_TRIANGLE> triangles(count); word into the other. It’s not exactly a morphing effect, but I don’t
for (int index = 0; index < count; index++)
know what else to call it.
{ Figure 7 shows the effect midway between the two words, and
triangles.at(index) =
InterpolateTriangle(m_triangleInfos.at(index).triangle[0],
with a little imagination you can almost make out either “DirectX”
m_triangleInfos.at(index).triangle[1], t); or “Factor” in the image.
}
Optimally, each pair of morphing triangles should be spatially
// Create a mesh with the interpolated triangles close, but minimizing the distances between all the pairs of trian-
m_deviceResources->GetD2DDeviceContext()->CreateMesh(&m_textMesh);
ComPtr<ID2D1TessellationSink> tessellationSink;
gles is akin to the Traveling Salesman Problem. I took a relatively
m_textMesh->Open(&tessellationSink); simpler approach by sorting the two collections of triangles by
tessellationSink->AddTriangles(triangles.data(), triangles.size());
tessellationSink->Close();
the X coordinates of the triangle center, and then separating the
} collections into groups representing ranges of X coordinates, and
// Renders a frame to the screen
sorting those by the Y coordinates. Of course, the two triangle col-
void TextMorphingRenderer::Render() lections are different sizes, so some triangles in the word “Factor”
{
...
correspond to two triangles in the word “DirectX.”
Figure 8 shows the interpolation logic in Update and the
if (m_textMesh != nullptr)
{
rendering logic in Render.
Matrix3x2F centerMatrix = D2D1::Matrix3x2F::Translation( With that, I think I’ve satisfied my curiosity about 2D triangles
(logicalSize.Width - (m_geometryBounds.right + m_geometryBounds.left)) / 2,
(logicalSize.Height - (m_geometryBounds.bottom + m_geometryBounds.top)) / 2);
and I’m ready to give those triangles a third dimension. Q

context->SetTransform(centerMatrix *
m_deviceResources->GetOrientationTransform2D());
context->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); CHARLES PETZOLD is a longtime contributor to MSDN Magazine and the author
context->FillMesh(m_textMesh.Get(), m_blueBrush.Get()); of “Programming Windows, 6th edition” (Microsoft Press, 2012), a book about
} writing applications for Windows 8. His Web site is charlespetzold.com.
...
}
THANKS to the following Microsoft technical experts for reviewing this article:
Jim Galasyn and Mike Riches
msdnmagazine.com March 2014 79
DON’T GET ME STARTED DAVID S. PLATT
Illustration: Reprinted with permission of John Hart Studios Inc.

The Peasants Are Revolting!

I’ve always enjoyed the comic strip, “Wizard You’d think that enterprise developers by
of Id,” which is set in medieval times. Its now would’ve realized the importance of
creators died in 2008, but their descendants usability, as they directly benefit from greater
have kept the strip current for today’s Inter- user productivity, fewer catastrophic errors,
net age (see bit.ly/1d7eIYK). Peasants (known, of and lower training and support costs. But the
course, as Idiots) rampage through the town strongest bastions of bad usability are places
waving signs that read, “The king is a fink!” where users are locked in and can’t choose.
Figure 1 shows the king’s response. Cormac Herley of Microsoft Research,
That same scenario is now exploding in the investigating the burden security policies
field of enterprise software. Last December, place on users, found them highest not where
Avon (the makeup guys) pulled the plug on a data was most sensitive, but rather in captive
new version of their order management soft- situations, especially governments and uni-
ware based on SAP. The Wall Street Journal in versities, where the enterprise didn’t suffer
December reported the company’s sales force the market consequences of its bad usability
of independent reps “found the new system (see bit.ly/1eK6Dhu). Avon is the tipping point
so burdensome and disruptive to their daily where this phenomenon starts to change.
routine that many left Avon.” Whether you’re dealing with the enter-
A spokesman for SAP was later quoted prise or consumer sector, UX design has
saying that Avon’s order management system Figure 1 Avon’s management was slow to happen before anything else can. To
“is working as designed, despite any issues to recognize unrest in the ranks. meet today’s standard of care, you can’t wait
with the implementation of this project.” until your program works and then throw it
Really? That means unless Avon’s goal was to reduce its workforce over the fence for the decorators to pretty up. The decorators can
through bad software instead of layoffs, the company implemented round off the corners of the File Open/Save dialog box and give it
a terrible design. And that weasel spokesman (but, like Mark Twain, nice color gradients. But the UX interaction designer determines
I repeat myself) should read my column about the word “issue.” whether to make the user save documents manually (a la Word),
(See msdn.microsoft.com/magazine/ff955613.) or implement automatic saving (a la OneNote). That choice very
As smoking in public was once common, it was once common much dictates the code to write. So UX design has to come first.
to force users to contort themselves into five-dimensional hyper- And with Avon, clearly, it didn’t.
pretzels to match their software—to become “computer literate,” That needs to change. As Steve Rosenbush wrote in his CIO
in the term of that day. UX guru Alan Cooper wrote that a com- Journal blog on wsj.com: “People who are accustomed to using
puter literate user is one who “has been hurt so often that the scar simple, well-designed applications in their personal lives have no
tissue is so thick that he no longer feels the pain.” Users accepted patience for disappointing technology at work.” Amen.
this as the price of getting their computing jobs done. That attitude And so, my friends, when you work on your enterprise apps,
doesn’t cut it anymore. you had better start paying attention to usability. Because the
Success in consumer-sector software and hardware has been enterprise-sector peasants are indeed revolting. And there’s no
driven by usability for seven years now, since the first Apple iPhone. stopping them. If your boss won’t let you put UX first, ask him how
But it’s taken much longer for that requirement to cross over into he feels about wearing tar and feathers. Q
the enterprise sector. The whole bring-your-own-device move-
ment arose from early adopter iPhone and iPad users wanting DAVID S. PLATT teaches programming .NET at Harvard University Extension School
their enterprise software to work as easily as their consumer apps. and at companies all over the world. He’s the author of 11 programming books, including
“Why Software Sucks” (Addison-Wesley Professional, 2006) and “Introducing
And now, like the Wizard’s newspaper pagemate Popeye the Sailor, Microsoft .NET” (Microsoft Press, 2002). Microsoft named him a Software Legend
enterprise users have stood up and roared, “That’s all I can stands! in 2002. He wonders whether he should tape down two of his daughter’s fingers so
I can’t stands no more!” (See bit.ly/1a7BiWZ.) she learns how to count in octal. You can contact him at rollthunder.com.

80 msdn magazine
Untitled-2 1 5/31/13 10:57 AM
Untitled-5 1 2/4/14 1:21 PM
Power
fulFi
leAPI
sthatar
eeasyandi
ntui
ti
vet
ouse

Nat
iveAPI
sfor
.
NET,
Java,
Andr
oid&Cl
oud

AsposeAPIshelpdevel opers
wit
hallf
iler
elat
edt asks,fr
om
conver
sionstoreporti
ng.

DOC,XLS,
JPG,PNG, PDF
BMP,
MSG, PPT,VSD,XPS
&manyotherf
ormats.

Al
soPower
ing

www.
aspose.
com
US Sal
es:+18882776734 EU Sales:+441414161112 AU Sal
es:+61280035926
sal
es@aspose.
com sal
es.europe@aspose.
com sales.
asi
apaci
fi
c@aspose.
com
CONVERT
PRINT
CREATE
COMBINE
MODIFY
100%S
tandal
one-NoOf
fi
ceAut
omat
ion
ASPOSE.
TOT
AL

As
pos
e.Tot
alf
or.
NET As
pos
e.Tot
alf
orCl
oud
As
pos
e.Tot
alf
orJ
ava As
pos
e.Tot
alf
orAndr
oid

.
NET J
ava oud Andr
Cl oid
pa
ge4
As
pos
e.Cel
l
s

XLS XLSX TXT PDF HTML CSV TI


FF PNG J
PG BMP
Spreadsheet
MLandmanyothers.

Aspose.Cell
sletsyoucr
eate,impor
t,andexpor
tspreadsheet
s
andalsoal l
owsyout omanipul
atecont
ent
s,cel
lfor
mat ti
ng,
andfil
epr otect
ion.

Aspose.
Cell
scomeswit
hcompletesuppor
tforchar
tingand
support
sallst
andar
dchar
ttypes.Al
so,youcanconvertchar
ts
toimages.

Easi
lyconver
twor
ksheet
stoi
magesaswel
lasaddi
ngi
mages
toworksheet
satr
unti
me.

GetyourFREETr ialat
US Sal
es:+18882776734
FAX:+18668109465 htt
p://
www. aspose.com
sal
es@aspose.
com
EU Sales:+441414161112
sal
es.europe@aspose.
com
NoOf
ficeAut
omat
ion
Aspose.Cell
sdoesnotr
equir
eMicrosof
tOff
icet
o
beinstall
edonthemachineinor
dert owor
k.
pa
ge6
pa
ge7
pa
ge10
pa
ge11
pa
ge13
pa
ge14
Wantt
owor
kwi
thr
ealbusi
nessdocument
s?

Nowy
ouc
an!

Gety
ourFREETRI
ALatwww.
aspos
e.c
om
pa
ge18
pa
ge19
As
pos
ehas4Suppor
tSer
vicest
obes
tsui
tyourneeds

Fr
eeSuppor
t Suppor
tFor
umswi
thnoChar
ge T
echni
calSuppor
tisanissue
thatAsposetakesveryseriousl
y.
24hourresponsetimeint
he
Pr
ior
itySuppor
t week,i
s s
uees cal
ati
on, Soft
war emustwor kquicklyand
dedi
catedforum
dependably.Whenpr oblemsar i
s e,
Ent
erpr
is t C
eSuppor ommuni
manager
s
cat
,i
ewi
nfl
thpr
oduct
uencetheroadmap
developer
sneedans wersi nahur ry
.
Weens urethatourcli
ent srecei
ve
Spons
oredSuppor
t Getthefeatureyouneedbui
ltnow usefulans
wer sandsoluti
onsqui ckl
y.

Emai
l•Li
veChat•For
ums

CONTACTUS

US Sal
es:+18882776734
sal
es@aspose.
com
EU Sales:+441414161112
sal
es.europe@aspose.
com
AU Sales:+61280035926
sal
es.asiapaci
fi
c@aspose.
com

You might also like