0% found this document useful (0 votes)
151 views102 pages

MSDN Magazine 11-03

Uploaded by

Scri Bd
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)
151 views102 pages

MSDN Magazine 11-03

Uploaded by

Scri Bd
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/ 102

THE MICROSOFT JOURNAL FOR DEVELOPERS MARCH 2011 VOL 26 NO 3

PROCESS AND DATA INTEGRATION COLUMNS


TOOLBOX
Cloud-Based Collaboration with SharePoint Online Data Integration Tools and Resources
Chris Mayo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 Terrence Dorsey page 6

CUTTING EDGE
Processing Health Care Claims with Application Extensibility:
BizTalk Server 2010 MEF vs. IoC
Dino Esposito page 10
Mark Beckner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
DATA POINTS
Tips and Tricks for Loading Silverlight Server-Side Paging with the Entity
Framework and ASP.NET MVC 3
Locale Resources Julie Lerman page 16
Matthew Delisle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
FORECAST: CLOUDY
Writing a Debugging Tools for Windows Extension Cloud Services Mashup
Joseph Fultz page 22
Andrew Richards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
MOBILE MATTERS
Building Data-Centric Web Apps with Windows Phone Navigation:
The Basics
ASP.NET MVC and Ext JS Yochay Kiriaty &
Juan Carlos Olamendy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Jaime Rodriguez page 82

Building and Using Custom OutputCache TEST RUN


Diffusion Testing
Providers in ASP.NET James McCaffrey page 86
Brandon Satrom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 THE WORKING PROGRAMMER
Multiparadigmatic .NET, Part 6:
Reflective Metaprogramming
Ted Neward page 88

UI FRONTIERS
Touch Gestures on Windows Phone
Charles Petzold page 92

DON’T GET ME STARTED


Missing the (Power) Point
David Platt page 96
...

............................................................................................
Using Quince™, you and your team can
collaborate on the user interface using
wireframes, designs and examples.

.......................................................................
Then use NetAdvantage® UI controls,
like the map control used here, to bring
the application to life quickly & easily.

Untitled-7 2 11/10/10 10:59 AM


..............................................................................................................................
From start to finish, Infragistics gives you the tools to create
impressive user experiences that'll make end users happy!

...

SEE HOW WE USE THE TOOLS


TO CREATE THIS KILLER APP AT
INFRAGISTICS.COM/IMPRESS
Infragistics Sales 800 231 8588 • Infragistics Europe Sales +44 (0) 800 298 9055 • Infragistics India +91 80 4151 8042 • @infragistics

Untitled-7 3 11/10/10 10:59 AM


MARCH 2011 VOLUME 26 NUMBER 3 magazine
LUCINDA ROWLEY Director
KIT GEORGE Editorial Director/mmeditor@microsoft.com
KERI GRASSL Site Manager

KEITH WARD Editor in Chief/mmeditor@microsoft.com


TERRENCE DORSEY Technical Editor
DAVID RAMEL Features Editor
WENDY GONCHAR Managing Editor
KATRINA CARRASCO Associate Managing Editor

SCOTT SHULTZ Creative Director


JOSHUA GOULD Art Director
CONTRIBUTING EDITORS K. Scott Allen, Dino Esposito, Julie Lerman, Juval
Lowy, Dr. James McCaffrey, Ted Neward, Charles Petzold, David S. Platt

Henry Allain President, Redmond Media Group


Matt Morollo Vice President, Publishing
Doug Barney Vice President, Editorial Director
Michele Imgrund Director, Marketing
Tracy Cook Online Marketing Director
ADVERTISING SALES: 508-532-1418/mmorollo@1105media.com
Matt Morollo VP, Publishing
Chris Kourtoglou Regional Sales Manager
William Smith National Accounts Director
Danna Vedder Microsoft Account Manager
Jenny Hernandez-Asandas Director Print Production
Serena Barnes Production Coordinator/msdnadproduction@1105media.com

Neal Vitale President & Chief Executive Officer


Richard Vitale Senior Vice President & Chief Financial Officer
Michael J. Valenti Executive Vice President
Abraham M. Langer Senior Vice President, Audience Development & Digital Media
Christopher M. Coates Vice President, Finance & Administration
Erik A. Lindgren Vice President, Information Technology & Application Development
Carmel McDonagh Vice President, Attendee Marketing
David F. Myers Vice President, Event Operations
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
$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 MSDNmag@1105service.com or call (847) 763-9560.
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, 16261 Laguna Canyon Road, Ste. 130, Irvine, CA 92618.

Legal Disclaimer: The information in this magazine has not undergone any formal testing by 1105 Media,
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,
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.

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),
508-875-6622 (fax), mmorollo@1105media.com

Reprints: For single article reprints (in minimum quantities of 250-500), e-prints, plaques and posters contact:
PARS International, Phone: 212-221-9595, E-mail: 1105reprints@parsintl.com, www.magreprints.com/
QuickQuote.asp

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, Merit Direct. Phone: 914-368-1000;
E-mail: 1105media@meritdirect.com; Web: www.meritdirect.com/1105

All customer service inquiries should be sent to MSDNmag@1105service.com or call 847-763-9560.

Printed in the USA


Your best source for
software development tools!
®

LEADTOOLS Document UltraEdit VMware vSphere


Imaging SDK v17.0 The #1 Best Selling Text Editor Essentials Kit Bundle
by LEAD Technologies in the World vSphere Essentials provides an all-in-one
LEADTOOLS Document Imaging has every compo- solution for small offices to virtualize three
by IDM
nent you need to develop powerful image-enabled physical servers for consolidating and
UltraEdit is the world’s standard in text managing applications to reduce hardware
business applications including specialized bi-tonal editors. Millions use UltraEdit as the
image processing, document clean up, annota- and operating costs with a low up-front
ideal text/hex/programmers editor investment. vSphere Essentials includes:
tions, high-speed scanning, advanced compression on any platform — Windows, Mac,
(CCITT G3/G4, JBIG2, MRC, ABC), and or Linux! • VMware ESXi and VMware ESX
Win32/64 binaries for C/C++, .NET, Silverlight, (deployment-time choice)
Features include syntax highlighting Named User
WPF, WCF, & WF. Available add-ons include: • VMware vStorage VMFS
for nearly any programming language; 1-24 Users for 3 hosts
• Multi-threaded OCR/ICR/OMR/MICR/ • Four-way virtual SMP
Barcodes (1D/2D) powerful Find, Replace, Find in Files, Paradise # Paradise #
and Replace in Files; FTP support, sort, I84 01201A01 • VMware vCenter Server Agent V55 85101C02
Paradise # • Forms Recognition/Processing
column mode, hex, macros/scripting, • VMware vStorage APIs/VCB
L05 03301A01 • Print Capture and Document Writers large file handling (4+ GB), projects,
$
59.95 • VMware vCenter Update Manager
$
446.99
99 • PDF, PDF/A and XPS templates, Unicode, and more.
$
2,007. programmers.com/LEAD programmers.com/idm • VMware vCenter Server for Essentials programmers.com/vSphere

Embarcadero RAD Studio XE Enterprise Architect VMware Workstation 7


by Embarcadero Corporate Edition VMware Workstation 7 is the gold-standard
virtualization software for desktop and laptop
Embarcadero® RAD Studio XE is a comprehen- Visualize, Document and computers, with the richest feature set and
sive application development suite and the Control Your Software Project broadest platform support available. VMware
fastest way to visually build GUI-intensive, by Sparx Systems Workstation enables users to create and host
data-driven applications for Windows, .NET, Enterprise Architect is a comprehensive, multiple virtual machines on a single desktop,
PHP and the Web. RAD Studio includes Delphi®, integrated UML 2.1 modeling suite expanding the power of desktop systems for
C++Builder®, Delphi Prism™, and RadPHP™. The providing key benefits at each stage of IT administrators; software development and
suite provides powerful compiled, managed system development. Enterprise Architect test engineers; technical sales, training and
and dynamic language support, heterogeneous 7.5 supports UML, SysML, BPMN and support staff; and PC enthusiasts.
NEW for Linux &
database connectivity, rich visual component
RELEASE! other open standards to analyze, design, VMware Workstation transforms the way Windows
frameworks and a vast third-party ecosystem 1-4 Licenses
test and construct reliable, well under- technical professionals develop, test, demo, Paradise #
that enable you to deliver applications up to stood systems. Additional plug-ins are Paradise #
5x faster across multiple Windows, Web, and and deploy software. Workstation’s innovative V55 22301A04
Paradise # also available for Zachman Framework, SP6 03101A02
database platforms! features for VMware environments help to
CGI 15401A01 MODAF, DoDAF and TOGAF, and to
integrate with Eclipse and Visual Studio
$
182. 99
reduce hardware cost, save time, minimize $
153.
99
risk, and streamline tasks that save time
$
1,383.99 programmers.com/embarcadero 2005/2008. programmers.com/sparxsystems and improve productivity. programmers.com/vmware

Spread for Windows Forms 5 Mindjet® MindManager TX Text Control 16.0


by GrapeCity version 9 for Windows® Word Processing Components
• World’s best selling .NET Spreadsheet Every Successful Project Starts TX Text Control is royalty-free, robust and
• Import/export native Microsoft Excel files with a Good Plan. powerful word processing software
with full formatting in reusable component form. New
by Mindjet® Version
• Extremely flexible printing and export options • .NET WinForms and WPF rich Released!
Mindjet MindManager® is information
including PDF text box for VB.NET and C#
mapping software that gives business
• Extensible formula support, including • ActiveX for VB6, Delphi, VBScript/HTML, ASP
professionals a better way to conquer
Microsoft Excel functions
information overload, brainstorm • File formats DOCX, DOC, RTF, HTML, Professional Edition
• Hundreds of chart styles for enhanced data
concepts, develop strategies, simplify XML, TXT
visualization Paradise #
project planning, and communicate 1 User • PDF and PDF/A export, PDF text import
• Powerful user interface and flexible data T79 12101A01
results. MindManager® maps provide Paradise #
Upgrade connectivity • Tables, headers & footers, text frames,
Paradise # • WYSIWYG spreadsheet designers, quick-start
an intuitive visual framework for
planning successful projects.
F15 17401A01 bullets, structured numbered lists, multiple
$
1,109. 99

wizard and chart designers undo/redo, sections, merge fields, columns


F02 01101A01
• Royalty-free licensing
$
293. 98
• Ready-to-use toolbars and dialog boxes
Download a demo today.
$
936.99 programmers.com/grapecity programmers.com/mindjet programmers.com/textcontrol

New Intel Visual Microsoft SQL Server


Fortran Compiler Developer Edition 2008 R2 Win an iPad!
by Intel by Microsoft
Intel® Visual Fortran Composer XE SQL Server 2008 Developer enables
Place an Order for Software
2011 includes the latest generation of Intel® developers to build and test applications (or Hardware) with
Fortran compilers, Intel® Visual Fortran Compiler that run on SQL Server on 32-bit, ia64, and Programmer’s Paradise
XE 12.0 for Windows. Intel® Fortran Composer x64 platforms. SQL Server 2008 Developer
XE is available for Linux and Mac OS X. This includes all of the functionality of Enterprise and You’ll be Entered
NEW package delivers advanced capabilities for Edition, but is licensed only for development, for a Drawing to Win
RELEASE! development of application parallelism and test, and demo use. The license for SQL 2-bit/x64 an iPad Wi-Fi 32GB.
winning performance for the full range of Intel® Server 2008 Developer entitles one developer IA64 DVD
for Windows processor-based platforms and other compatible to use the software on as many systems
Single (SSR) Paradise #
platforms. It includes the compiler’s breadth of as necessary. For rapid deployment into Just Use the Offer Code TRWD03
Paradise # M47 31101A04
advanced optimization, multithreading, and production, instances of SQL Server 2008
I23 86101E03 processor support, as well as automatic proces- Developer can easily be upgraded to SQL When You Place Your Order Online or with
sor dispatch, vectorization, and loop unrolling. Server 2008 Enterprise without reinstallation. 41.
$ 99
Your Programmer’s Paradise Representative.
$
263.99 programmers.com/intel programmers.com/microsoft

866-719-1528 programmersparadise.com
Prices subject to change. Not responsible for typographical errors.

Untitled-11 1 2/2/11 3:41 PM


EDITOR’S NOTE KEITH WARD

How to Get Rejected

Last month, I talked about some of the best ways to get published Now, a few words about how to get your article rejected after it’s
in MSDN Magazine. That naturally led me to think of some equally been accepted for publication. The first, best (worst?) thing you can
good ways to not get published in the magazine. If February was do is not communicate with the staff, beginning with me. It’s hap-
the list of dos, this is the list of don’ts. Follow these rules (or is that pened numerous times that an author is late with an article. Hey, stuff
“don’t follow these rules”—I get confused with negatives) to give happens, and delays can be caused by any number of circumstances.
your query the best shot at not getting a positive response from What drives me to distraction is when an author doesn’t inform me
me and the team. that an article or requested bit of information will be late. I can almost
The best way to ensure your query doesn’t get accepted, or even always work around delays; to do that, however, I need to hear from
considered, is to put it in the wrong format. We’ve written a guide to you. Even if you come to the conclusion that you won’t be able to turn
the proper formatting of queries, which you can find at bit.ly/eZcovQ. in an article at all (it happens sometimes), let me know so I can make
One of the critical pieces of information in the document is the title alternative arrangements. If I don’t know, however, it throws a shiny
of your query. It should state “MSDN Article Query” in the subject steel monkey wrench right into the middle of our processes, and
line—and nothing else. Please don’t get cute with the title; clever makes me (and my staff ) miserable. Please don’t make us miserable.
subject lines don’t thrill me. Like you, I get tons of e-mail each day, Other reasons your submitted article can be rejected:
and I need to quickly separate the wheat from the chaff. If you’ve • Plagiarism. You can’t copy and paste from published docu-
got the right title, I’ll look over the query; if not, it’s ye olde Delete ments. At all. Not nowhere, not nohow. “That’s obvious!” you
key for your e-mail. say. You’d be surprised at how un-obvious it is to some writers.
Another way to hurt your chances: Make your query novella • Sloppy writing. See my section about the query. You may
length. My eyes glaze over when I open an article query and notice not be the Shakespeare of dev authors, but strive to turn in
that it will take quite a lot of scrolling to get through it all. Brevity is your best work. That means after you write the article, get
key here—short, sweet and clear. If you can’t do this in your query, away from it for a day and go back and edit it. Then do it
I’ll have serious doubts about whether you can do it in an article. again. If information is unclear or incomplete, fix it. Don’t
That brings up another crucial point: Your query, if I haven’t expect us to do it all for you. If we do that, and still publish
worked with you before, is your initial writing audition. Writing your article anyway because we love the topic, be assured
for MSDN Magazine isn’t like writing a blog entry—you don’t need you won’t get assigned a second article.
to have been previously published, but you do need to demonstrate • Failure to follow writer’s guidelines. All authors get a copy of
at least basic writing chops. If your query is full of misspellings, our writing guidelines. I suspect some of them barely glance
sloppy grammar and missing information, you’ll get turned down, at the document. Don’t let this be you. There are specific
guaranteed. After all, if I can’t trust you to write a proper query, I’m rules that need to be followed. Learn them. Love them.
surely not going to trust you to write an entire article. One of the best parts of my job is working with authors. I try to
One last item about queries: Please don’t make your pitch in make it a relaxed, enjoyable process. You can do your part to help me
an attached Word document. I’m not keen on downloading and by avoiding these don’ts. Send your
opening attachments just to read a query. Include the query in the (properly formatted!) article ideas to
body of the e-mail—please! me at mmeditor@microsoft.com.

Visit us at msdn.microsoft.com/magazine. Questions, comments or suggestions for MSDN Magazine? Send them to the editor: mmeditor@microsoft.com.

© 2011 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-1 1 2/9/11 10:43 AM
TOOLBOX TERRENCE DORSEY

Data Integration Tools and Resources


It seems that all but the most trivial applications these days deal with (albahari.com) and supported by a huge community of programmers
data. Often, lots of data. So I guess that means, as a developer, you and data experts.
need to add “Database Expert” to the many hats you wear. Or maybe If you’re just getting started with LINQ, check out Dan Wahlin’s
not: What if there were tools and resources out there that gave you a blog post, “Learn How to Use LINQ with LINQPad” (bit.ly/hlOyMh),
leg up and did some of the heavy lifting for you? and Al Tenhundfeld’s article, “Master LINQ with LINQPad” (bit.ly/
A good place to start is the MSDN Data Developer Center fSUij4). Both will get you up and running quickly.
(msdn.microsoft.com/data), where you’ll find links to a huge selection of Not only is LINQPad a great tool for LINQ queries, it also lets you
Microsoft tools and technologies for integrating data access into your interactively run and verify functions in C# and Visual Basic. Rich
apps. From SQL Server to ADO.NET to MSXML, you’ll find it all there. Strahl demonstrates this handy feature in his article, “LINQPad as a
In fact, it’s such a comprehensive resource I probably could leave it Code Snippet Execution Engine” (bit.ly/eCD60C).
at that. Happy databinding!
But wait! There’s more ...
LINQPad has become a crucial
Data Basics
What’s that? You’re not already savvy in the ways of SQL Server? Tables, tool for developers to learn
rows and records sounds more like party planning than dev speak? If
you’re just getting started with data-centric development—or need a
refresher—here are a few resources that will get you up to speed.
LINQ, prototype queries,
Geekgirl’s Plain-English Computing database guides (geekgirls.com/
category/office/databases) provide a series of “Databasics” tutorials
or interactively query a wide
explaining the core concepts of database design and use. If you’re
starting from scratch, you can’t do much better than this. variety of data sources.
Once you understand those basics, a solid next step would be
something like the Code Project article “SQL for Developers: Entity Framework
Basic Data Retrieval” (bit.ly/gurX8Y). Here you’ll learn how to use The ADO.NET Entity Framework is a .NET object-relational mapping
simple SQL queries to get selected data out of a database and into (O/RM) framework meant to simplify access to relational databases
your app, where you can do something useful with it. (That task is left from your code. Simply stated, the Entity Framworok lets you map
to the reader ... but read on for some tips.) your database schema to programmatic entities that you can query
For a deeper look at the syntax for communicating with a database via properties. To learn more about the Entity Framework, see the
engine like SQL Server 2008, check out the Transact-SQL Reference ADO.NET Entity Framework Developer Center (bit.ly/eOmtC1).
(bit.ly/hDhdvz) in SQL Server Books Online. If you’re new to the Entity Framework, get up to speed by working
Once you get coding, you’ll want to check frequently for tips and through the Microsoft “Getting Started with Entity Framework”
tricks at SQL for Programmers, the .NET Answers archive of SQL tutorials (bit.ly/gcrXyU) for WebForms. If you prefer to use
programming tips (bit.ly/ejD7Zg). ASP.NET MVC, there’s also a “Creating Model Classes with
the Entity Framework” tutorial (bit.ly/dXJAjx).
LINQ Julie Lerman, our regular Data Points columnist, is an Entity Framework
Language Integrated Query (LINQ) is a feature of the Microsoft .NET expert, having written the comprehensive “Programming Entity
Framework that extends data access using native language constructs Framework, Second Edition” (O’Reilly,
in C# and Visual Basic (and F# to some extent, as well). Learn more 2010). Learn more about her book at
about it at the LINQ Developer Center (bit.ly/fl9xpg). learnentityframework.com. Want to get a taste of
One of the strengths of LINQ is that it enables you to write SQL-like some advanced tasks you can achieve with
queries using strongly typed syntax. Then, LINQ providers such as the Entity Framework? Read Lerman’s
LINQ to SQL or LINQ to Objects can handle the fine details of the December 2010 column, “Profiling
actual data source. For a practical overview of how this works, see Database Activity in the Entity
the Data Points column by John Papa, “Standard Query Operators Framework” (bit.ly/flLwdw).
with LINQ” (bit.ly/huKhxa). Of course, LINQPad is a great tool for
LINQPad (linqpad.net) has become a crucial tool for developers to learning the Entity Framework, too. Check
learn LINQ, prototype queries, or interactively query a wide variety of out “Using LINQPad with Entity Frame-
data sources. LINQPad is a free tool written by Joseph Albahari work,” (bit.ly/hUBRu0) for a full tutorial. Julie Lerman’s Book
6 msdn magazine
Untitled-5 1 2/2/11 1:18 PM
TOOLBOX

WCF Soap Services (bit.ly/icbLnR) that will help you understand the
strengths of each approach.
How about putting a bunch of these technologies together into
one interesting sample? Shawn Wildermuth, in his article “WCF Data
Services and jQuery” (bit.ly/hVCMWd), builds a Web app that uses
jQuery to retrieve data in JSON format, exposes the data as entities
via Entity Framework, and then uses WCF Data Services to expose
the entities via REST. I think we have a “Buzzword Bingo” winner here.
A highly rated tool for making sense of your data sources is the
Open Data Protocol Visualizer extension for Visual Studio 2010
(bit.ly/dWt19X), which displays the types and relationships provided by
WCF Data Services in simplified diagram form. Read Audrey Petit’s
“Open Data Protocol Visualizer Extension for Visual Studio
2010” blog post (bit.ly/hKSKRx) to learn how it works.

NHibernate
NHibernate (nhforge.org) is an open source O/RM framework for
development with the .NET Framework. Like the Entity Framework,
LINQPad NHibernate lets you map databases to entities and allows simplified
programmatic access to the data.
WCF Data Services and OData Because it’s a community-driven effort, there are lots of useful
WCF Data Services—formerly known as ADO.NET Data Services—lets resources available for learning and using NHibernate in your
you share, consume and update data via HTTP using the OData projects. One great way is to jump in and start coding. Gabriel
protocol (odata.org). Like the Entity Framework, WCF Data Services Schenker’s “Your Very First NHibernate Application” series of
uses an Entity Data Model (EDM) to bridge between data source and articles on dotnetslackers.com (direct link: bit.ly/exFATb) is one such
program entities. You can read more about WCF Data Services on tutorial. Another is Mitch Fincher’s “Learning with Code Samples”
MSDN at bit.ly/hnuvwv. for NHibernate (bit.ly/e91Nzv).
To get you started, Shayne Burgess walks you through the basics Would you rather see the movie? Well then, check out the
of using OData and Data Services in the article, “Building Rich Summer of NHibernate (summerofnhibernate.com) screencast series,
Internet Apps with the Open Data Protocol,” in the June 2010 which walks you step-by-step from getting set up to writing your first
issue of MSDN Magazine (bit.ly/gPZGDc). queries to advanced topics like modeling inheritance and managing
Not sure which WCF services to use for your data-centric app? session state. There’s a lot to watch, so get your popcorn ready and
Tony Sneed wrote an in-depth appraisal of WCF Data Services vs. settle in for a few evenings of learning.

Zentity
Many business and social apps run
on data, but researchers are
increasingly creating and sorting
through huge data sources from
clinical studies, experimental results
and even celestial observations. In
response, Microsoft Research
released Zentity 2.0 (bit.ly/fiFPb3),
a data library framework that can
be used for storing, accessing and
analyzing data using SQL Server
2008. The new version of Zentity
leverages the .NET Framework 4
with support for WCF Data
Services and OData, LINQ, Entity
Framework, Windows PowerShell
and more. „

TERRENCE DORSEY is the technical editor


of MSDN Magazine. You can read his
blog at terrencedorsey.com or follow him
Open Data Protocol Visualizer on Twitter: @tpdorsey.
8 msdn magazine Toolbox
Untitled-5 1 2/2/11 1:18 PM
CUTTING EDGE DINO ESPOSITO

Application Extensibility: MEF vs. IoC


There’s an interesting new component in the Microsoft .NET of specific types so that developers can pre- and post-process
Framework 4 specifically designed to provide an effective answer the execution of methods. I covered interception in Unity 2.0 in
to an evergreen question: How would you write extensible appli- January (msdn.microsoft.com/magazine/gg535676).
cations that can discover at run time all the parts they’re made of? The MEF, in a way, can serve as the factory of a graph of objects,
As Glenn Block explained in his February 2010 article, “Build- meaning that it can recognize and handle members on a class that
ing Composable Apps in .NET 4 with the Managed Extensibility need be resolved at run time. The MEF also provides minimal
Framework” ( msdn.microsoft.com/magazine/ee291628 ), the Managed support for caching instances, meaning that some caching capa-
Extensibility Framework (MEF) can be used to streamline building bilities exist, but they’re not as functionally rich as in some other
composable and plug-in-based applications. As one who started IoC frameworks. Finally, in the version shipped with the .NET
approaching the problem back in 1994 (yes, it was one of my first Framework 4, the MEF lacks interception capabilities entirely.
real challenges as a developer), I definitely welcome any proposed Having said that, when should you use the MEF? If you’ve never
solutions in this problem space. used an IoC framework and just need to clean up the design of your
The MEF doesn’t require you to buy, download and reference any system by adding a bit of dependency injection, then the MEF can
additional libraries, and it offers a simple programming interface be an easy start. As long as you can quickly achieve your goals with
because it’s focused on solving the problem of general, third-party it, the MEF is preferable to an IoC framework.
extensibility of existing applications. Glenn’s article is an excellent On the other hand, if you’ve spent years working with one or
introduction to the MEF and should be considered required read- more IoC frameworks and can squeeze any bit of functionality
ing if you’re thinking about plug-in-based applications. out of them, then there’s probably nothing that the MEF can give
In this article, I’ll walk through the steps required to build an you except, perhaps, its ability to scan various types of catalogs to
extensible application using the MEF as the underlying glue to find matching types. It should be noted, however, that some IoC
keep the main body and external parts of the application together. frameworks such as StructureMap ( structuremap.net/structuremap/
ScanningAssemblies.htm) already offer to scan directories and assemblies
From IoC to the MEF and Back to look for specific types or implementations of given interfaces.
Before I get to the sample application, however, I’d like to share With the MEF, this is probably easier and more direct to do than
some thoughts about the MEF and another popular family of with StructureMap (and a few others).
frameworks: Inversion of Control (IoC). In summary, the first question to answer is whether you’re
In a nutshell, it’s correct to say that the functionality of the looking for general extensibility. If the answer is yes, then the MEF
MEF and of a typical IoC framework overlap, but don’t coincide. must be considered—perhaps in addition to an IoC tool if you also
With most IoC frameworks you can perform tasks that the MEF need to handle dependencies, singletons and interception. If the
just doesn’t support. You could probably employ a functionally
rich IoC container and, with some effort on your own, emulate Figure 1 Definitions for the Application SDK
some MEF-specific capabilities. In light of this, the question that public interface IFindTheNumberPlugin {
I’m frequently asked when I mention the MEF in classes and void ShowUserInterface(GuessTheNumberSite site);
void NumberEntered(Int32 number);
everyday work is: What’s the difference between the MEF and an void GameStarted();
IoC tool? And when do I really need the MEF? void GameStopped();
}
My thought is that, at its core, the MEF is an IoC framework
built right into the .NET Framework. It’s not as powerful as many public interface IFindTheNumberApi {
Int32 MostRecentNumber { get; }
of the popular IoC frameworks today, but it can perform the basic Int32 NumberOfAttempts { get; }
tasks of a typical IoC container quite well. Boolean IsUserPlaying { get; }
Int32 CurrentLowerBound { get; }
Today, IoC frameworks have three typical capabilities. First, they Int32 CurrentUpperBound { get; }
can act as the factory of a graph of objects and walk through the Int32 LowerBound { get; }
Int32 UpperBound { get; }
chain of object relationships and dependencies to create an instance void SetNumber(Int32 number);
of any required and registered type. Second, an IoC framework }

can manage the lifetime of created instances and offer caching public class FindTheNumberFormBase : Form, IFindTheNumberApi {
and pooling capabilities. Third, most IoC frameworks support in- ...
}
terception and offer to create dynamic proxies around instances
10 msdn magazine
Untitled-5 1 2/2/11 1:19 PM
answer is no, then the best approach is using an IoC framework Figure 2 The Site Object for Plug-Ins
unless you have basic needs that the MEF can address as well. All public class FindTheNumberSite {
things being equal, the MEF is preferable to an IoC framework private readonly FindTheNumberFormBase _mainForm;
because it’s built right into the .NET Framework and you don’t need public FindTheNumberSite(FindTheNumberFormBase form) {
to take any additional dependencies. _mainForm = form;
}

The MEF and Extensible Applications public T FindElement<T>(String name) where T:class { ... }
public void AddElement(Control element) { ... }
While the MEF helps in the building of an extensible application,
the most delicate part of the job is designing the application for public Int32 Height {
get { return _mainForm.Height; }
extensibility. This is design and has little to do with the MEF, IoC set { _mainForm.Height = value; }
or other technologies. In particular, you must figure out which }
parts of your application you intend to make available to plug-ins. public Int32 Width { ... }
A plug-in is often a visual element and needs to interact with the public Int32 NumberOfAttempts { ... }
public Boolean IsUserPlaying { ... }
UI of the main application, add or extend menus, create panes, dis- public Int32 LowerBound { ... }
play dialog boxes, or even add or resize the main windows. Depend- public Int32 UpperBound { ... }
public void SetNumber(Int32 number) { ... }
ing on how you envision the plug-ins of your specific application, }
the amount of information to share with plug-ins may consist of
just business data (essentially a segment of the application’s current submitted for a new try. Finally, ShowUserInterface is invoked
state) or reference to visual elements such as containers, menus, when the plug-in must show up in the window. In this case, a site
toolbars and even specific controls. You group this information object is passed, as defined in Figure 2.
in a data structure and pass it down to the plug-in at initialization The site object represents the point of contact between the
time. Based on that information, the plug-in should be able to plug-in and the host application. The plug-in must gain some
adjust its own UI and implement its own additional custom logic. visibility of the host state and must even be able to modify the
Next comes the interface for the plug-ins. The interface depends host UI, but it never gains knowledge of the host’s internal details.
on the injection points you’ve identified in your main application. By That’s why you might want to create an intermediate site object
“injection point” I mean the places in the application’s code from which (part of your SDK assembly) that plug-in projects must reference.
you’d invoke plug-ins to give them a chance to kick in and operate. I omitted for brevity the implementation of most methods in
As an example of an injection point, consider Windows Explorer. Figure 2, but the constructor of the site object receives a refer-
As you may know, Windows Explorer allows you to extend its UI ence to the application’s main window, and using helper methods
via shell extensions. These plug-ins are invoked at very specific in Figure 1 (exposed by the main window object) it can read and
moments—for example, when the user right-clicks to display the write the application’s state and visual elements. For example, the
properties of a selected file. As the application’s architect, it’s your Height member shows how the plug-in may read and write the
responsibility to identify these injection points and what data you height of the host window.
intend to pass to registered plug-ins at that point. In particular, the FindElement method allows the plug-in (in the
Once every design aspect has been cleared up, you can look sample application) to retrieve a particular visual element in the
around for frameworks that can simplify the task of building a form. It’s assumed that you unveil as part of your SDK some tech-
plug-in-based application. nical details of how to access certain containers such as toolbars,
menus and the like. In such a simple application, it’s assumed
A Sample Plug-In-Based Application that you document the ID of the physical controls. Here’s the
Even a simple application such as “Find the number” can be made implementation of FindElement:
richer and functionally appealing using plug-ins.
You might want to create a separate project to define the SDK Figure 3 Initializing the MEF
of your application. It will be a class library where you define all private void InitializeMef() {
classes and interfaces required to implement plug-ins. Figure 1 try {
_pluginCatalog = new DirectoryCatalog(@"\My App\Plugins");
shows an example. var filteredCatalog = new FilteredCatalog(_pluginCatalog,
All plug-ins are required to implement the IFindTheNumber- cpd => cpd.Metadata.ContainsKey("Level") &&
!cpd.Metadata["Level"].Equals("Basic"));
Plugin interface. The main application form will inherit from the
specified form class, which defines a list of public helper members // Create the CompositionContainer with the parts in the catalog
_container = new CompositionContainer(filteredCatalog);
useful to pass information down to plug-ins. _container.ComposeParts(this);
As you may guess from IFindTheNumberPlugin, registered }
catch (CompositionException compositionException) {
plug-ins are invoked when the application displays its UI, when ...
the user makes a new attempt to guess the number, and when the }
catch (DirectoryNotFoundException directoryException) {
game is started and stopped. GameStarted and GameStopped are ...
just notification methods and don’t need any input. NumberEntered }
}
is a notification that brings in the number the user just typed and

12 msdn magazine Cutting Edge


Untitled-5 1 2/2/11 1:20 PM
public T FindElement<T>(String name) where T:class { Figure 4 The Counter Plug-In
var controls = _mainForm.Controls.Find(name, true);
if (controls.Length == 0) [Export(typeof(IFindTheNumberPlugin))]
return null; [PartMetadata("Level", "Advanced")]
var elementRef = controls[0] as T; public class AttemptCounterPlugin : IFindTheNumberPlugin {
return elementRef ?? null; private FindTheNumberSite _site;
} private Label _attemptCounterLabel;
With the design of the application’s extensibility model completed, public void ShowUserInterface(FindTheNumberSite site) {
we’re now ready to introduce the MEF. _site = site;
var numberToGuessLabelRef = _host.FindElement<Label>("NumberToGuess");

Defining Imports for Plug-ins


if (numberToGuessLabelRef == null)
return;
The main application will certainly expose a property that lists all // Position of the counter label in the form
currently registered plug-ins. Here’s an example: _attemptCounterLabel = new Label {
Name = "plugins_AttemptCounter",
public partial class FindTheNumberForm :
Left = numberToGuessLabelRef.Left,
FindTheNumberFormBase {
Top = numberToGuessLabelRef.Top + 50,
public FindTheNumberForm() {
Font = numberToGuessLabelRef.Font,
InitializeMef();
Size = new Size(150, 30),
...
BackColor = Color.Yellow,
}
Text = String.Format("{0} attempt(s)", _host.NumberOfAttempts)
};
[ImportMany(typeof(IFindTheNumberPlugin)]
_site.AddElement(_attemptCounterLabel);
public List<IFindTheNumberPlugin> Plugins {
}
get; set;
} public void NumberEntered(Int32 number = -1) {
... var attempts = _host.NumberOfAttempts;
} _attemptCounterLabel.Text = String.Format("{0} attempt(s)", attempts);
Initializing the MEF means preparing the composition container return;
}
specifying the catalogs you intend to use and optional export
public void GameStarted() {
providers. A common solution for plug-in-based applications is NumberEntered();
loading plug-ins from a fixed folder. Figure 3 shows the startup }

code of the MEF in my example. public void GameStopped() {


}
You use a DirectoryCatalog to group available plug-ins and use }
the FilteredCatalog class (which is not in the MEF, but an example
is shown in the MEF documentation at bit.ly/gf9xDK) to filter out must do is prepare its own UI, bind it to the number of attempts
some of the selected plug-ins. In particular, you can request that and attach it to the main window.
all loadable plug-ins have a metadata attribute that indicates the Plug-ins of the sample application will create new controls in the
level. Missing the attribute, the plug-in is ignored. UI of the main window. Figure 4 shows a sample plug-in.
The call to ComposeParts has the effect of populating the Plugins The plug-in creates a new Label control and places it just below
property of the application. The next step is just invoking plug-ins from an existing UI element. Next, whenever the plug-in receives the
the various injection points. The first time you invoke plug-ins is right notification that a new number has been entered, the counter is
after the application loads to give them a chance to modify the UI: updated to show the current number of attempts according to the
void FindTheNumberForm_Load(Object sender, EventArgs e) {
// Set up UI state of the business logic.
UserIsPlaying(false);

// Stage to invoke plugins Plugging In


NotifyPluginsShowInterface(); At the end of the day, the most delicate task of designing extensible
}
apps is the design of the host and the interface of plug-ins. This is a pure
void NotifyPluginsShowInterface() { design task and has to do with the feature list and user’s requirements.
var site = new FindTheNumberSite(this);
if (Plugins == null) When it comes to implementation, though, you have quite a few
return; practical tasks to accomplish regardless of the plug-in interface,
foreach (var p in Plugins) { such as selecting, loading and verifying plug-ins. In this regard,
p.ShowUserInterface(site);
}
the MEF gives you significant help in simplifying creation of the
} catalog of plug-ins to load, and automatically loading them up in
Similar calls will appear in the event handlers that signal when much the same way an IoC framework would do.
the user just started a new game, quit the current game or just made Note that the MEF is under continual development, and you can find
a new attempt to guess the mysterious number. the latest bits, documentation and example code at mef.codeplex.com. „

Writing a Sample Plug-In D INO E SPOSITO is the author of “Programming Microsoft ASP.NET MVC”
A plug-in is just a class that implements your app’s extensibility (Microsoft Press, 2010) and coauthored “Microsoft .NET: Architecting Applications
interface. An interesting plug-in for the application is one that for the Enterprise” (Microsoft Press, 2008). Based in Italy, Esposito is a frequent
shows the number of attempts the user made so far. The number speaker at industry events worldwide. You can join his blog at weblogs.asp.net/despos.
of attempts is being tracked by the business logic of the applica-
tion, and it’s exposed to plug-ins via the site object. All a plug-in THANKS to the following technical expert for reviewing this article: Glenn Block
14 msdn magazine Cutting Edge
Untitled-5 1 2/2/11 1:20 PM
DATA POINTS JULIE LERMAN

Server-Side Paging with the


Entity Framework and ASP.NET MVC 3
In my January Data Points column, I showed off the jQuery Data-
Tables plug-in and its ability to seamlessly handle huge amounts
of data on the client side. This works well with Web applications
where you want to slice and dice large amounts of data. This month,
I’ll focus on using queries that return smaller payloads to enable
a different type of interaction with the data. This is especially
important when you’re targeting mobile applications.
I’ll take advantage of features introduced in ASP.NET MVC 3
and demonstrate how to use these together with efficient server-
side paging against the Entity Framework. There are two challenges
with this task. The first is to provide an Entity Framework query
with the correct paging parameters. The second is to mimic a fea-
ture of client-side paging by providing visual clues to indicate that
there’s more data to retrieve, as well as links to trigger the retrieval.
ASP.NET MVC 3 has a slew of new features, such as the new
Razor view engine, validation improvements and a ton more
JavaScript features. The launch page for MVC is at asp.net/mvc, where
you can download ASP.NET MVC 3 and find links to blog posts Figure 1 Providing Edit ActionLinks in the WebGrid
and training videos to help you get up to speed. One of the new GetPagedCustomers method provides server-side paging. If the
features that I’ll use is the ViewBag. If you’ve used ASP.NET MVC goal of the ASP.NET MVC application was to allow the user to in-
previously, ViewBag is an enhancement to the ViewData class and teract with all of the customers, that would be a lot of customers
lets you use dynamically created properties. returned in a single query and managed in the browser. Instead,
Another new element that ASP.NET MVC 3 brings to the table we’ll let the app present 10 rows at a time and the GetPaged-
is the specialized System.Web.Helpers.WebGrid. Although one of Customers will provide that filter. The query that I’ll eventually
the grid’s features is paging, I’ll use the new grid but not its paging in need to execute looks like this:
this example, because that paging is client-side—in other words, it context.Customers.Where(c =>
c.SalesOrderHeaders.Any()).Skip(skip).Take(take).ToList()
pages through a set of data provided to it, similar to the DataTables
plug-in. I’m going to be using server-side paging instead. The view will know which page to request and give that informa-
For this little app, you’ll need an Entity Data Model to work with. tion to the controller. The controller will be in charge of knowing
I’m using one created from the Microsoft AdventureWorksLT sam- how many rows to supply per page. The controller will calculate the
ple database, but I only need the Customer and SalesOrderHeaders “skip” value using the page number and the rows per page. When
brought into the model. I’ve moved the Customer rowguid, Password- the controller calls the GetPagedCustomers method, it will pass in
Hash and PasswordSalt properties into a separate entity so that I don’t the calculated skip value as well as the rows per page, which is the
have to worry about them when editing. Other than this small change, “take” value. So if we’re on page four and presenting 10 rows per
I haven’t modified the model from its default. page, skip will be 40 and take will be 10.
I created a project using the default ASP.NET MVC 3 project The paging query first creates a filter that requests only those
template. This prepopulates a number of controllers and views, customers who have any SalesOrders. Then, using LINQ Skip and
and I’ll let the default HomeController present the Customers. Take methods, the resulting data will be a subset of those custom-
I’ll use a simple DataAccess class to provide interaction with the ers. The full query, including the paging, is executed in the data-
model, context and, subsequently, the database. In this class, my base. The database returns only the number of rows specified by
the Take method.
The query is composed of a few parts to enable some tricks I’ll
add down the road. Here’s a first pass at the GetPagedCustomers
Code download available at code.msdn.microsoft.com/mag201103DataPoints.
method that will be called from the HomeController:
16 msdn magazine
Untitled-5 1 2/2/11 1:21 PM
Figure 2 The New Version of GetPagedCustomers The result is shown in Figure 1.
public static PagedList<Customer> GetPagedCustomers(int skip, int take)
So far, so good. But this doesn’t provide a way for the user to
{ navigate to another page of data. There are a number of ways to
using (var context = new AdventureWorksLTEntities())
{
achieve this. One way is to specify the page number in the URI—
var query = context.Customers.Include("SalesOrderHeaders") for example, http://adventureworksmvc.com/Page/3. Surely you
.Where(c => c.SalesOrderHeaders.Any())
.OrderBy(c => c.CompanyName + c.LastName + c.FirstName);
don’t want to ask your end users to do this. A more discoverable
mechanism is to have paging controls, such as page number links
var customerCount = query.Count();
“1 2 3 4 5 …” or links that indicate forward and backward, for
var customers = query.Skip(skip).Take(take).ToList(); example, “<< >>.”
return new PagedList<Customer>
The current roadblock to enabling the paging links is that the
{ Index view page has no knowledge that there are more Customers
Entities = customers,
HasNext = (skip + 10 < customerCount),
to be acquired. It knows only that the universe of customers is the
HasPrevious = (skip > 0) 10 that it’s displaying. By adding some additional logic into the data-
};
}
access layer and passing it down to the view by way of the controller,
} you can solve this problem. Let’s begin with the data-access logic.
In order to know if there are more records beyond the current
public static List<Customer> GetPagedCustomers(int skip, int take) set of customers, you’ll need to have a count of all of the possible
{ customers that the query would return without paging in groups of
using (var context = new AdventureWorksLTEntities())
{ 10. This is where composing the query in the GetPagedCustomers
var query = context.Customers.Include("SalesOrderHeaders") will pay off. Notice that the first query is returned into _customer-
.Where(c => c.SalesOrderHeaders.Any())
.OrderBy(c => c.CompanyName + c.LastName + c.FirstName); Query, a variable that’s declared at the class level, as shown here:
_customerQuery = context.Customers.Where(c => c.SalesOrderHeaders.Any());
return query.Skip(skip).Take(take).ToList();
}
You can append the Count method to the end of that query to
} get the count of all of the Customers that match the query before
The controller Index method that calls this method will determine paging is applied. The Count method will force a relatively simple
the number of rows to return using a variable I’ll call pageSize, query to be executed immediately. Here’s the query executed in SQL
which becomes the value for Take. The Index method will also Server, from which the response returns a single value:
specify where to begin based on a page number that will be passed SELECT
[GroupBy1].[A1] AS [C1]
in as a parameter, as shown here: FROM ( SELECT
public ActionResult Index(int? page) COUNT(1) AS [A1]
{ FROM [SalesLT].[Customer] AS [Extent1]
const int pageSize = 10; WHERE EXISTS (SELECT
var customers=DataAccess.GetPagedCustomers((page ?? 0)*pageSize, pageSize); 1 AS [C1]
return View(customers); FROM [SalesLT].[SalesOrderHeader] AS [Extent2]
} WHERE [Extent1].[CustomerID] = [Extent2].[CustomerID]
)
This gets us a good part of the way. The server-side paging is ) AS [GroupBy1]
completely in place. With a WebGrid in the Index view markup, we Once you have the count, you can determine if the current page
can display the customers returned from the GetPagedCustomers of customers is the first page, the last page or something in between.
method. In the markup, you need to declare and instantiate the Then you can use that logic to decide which links to display. For
grid, passing in Model, which represents the List<Customer> that example, if you’re beyond the first page of customers, then it’s
was provided when the controller created the view. Then, using the logical to display a link to access earlier pages of customer data
WebGrid GetHtml method, you can format the grid, specifying with a link for the previous page, for example, “<<.”
which columns to display. I’ll only show three of the Customer We can calculate values to represent this logic in the data-access
properties: CompanyName, FirstName and LastName. You’ll be class and then expose it in a wrapper class along with the customers.
happy to find full IntelliSense support as you type this markup Here’s the new class I’ll be using:
whether you use syntax associated with ASPX views or with the public class PagedList<T>
new MVC 3 Razor view engine syntax (as with the following {
public bool HasNext { get; set; }
example). In the first column, I’ll provide an Edit ActionLink so public bool HasPrevious { get; set; }
that the user can edit any of the Customers that are displayed: public List<T> Entities { get; set; }
}
@{
var grid = new WebGrid(Model);
}
<div id="customergrid">
@grid.GetHtml(columns: grid.Columns(
grid.Column(format: (item) => Html.ActionLink
("Edit", "Edit", new { customerId = item.CustomerID })),
grid.Column("CompanyName", "Company"),
grid.Column("FirstName", "First Name"),
grid.Column("LastName", "Last Name")
)) Figure 3 ViewBag Properties Aren’t Available Through
</div> IntelliSense Because They’re Dynamic
18 msdn magazine Data Points
Install LEADTOOLS to eliminate months of research and programming time while maintaining high
levels of quality, performance and functionality. LEADTOOLS provides developers easy access to
decades of expertise in color, grayscale, document, medical, vector and multimedia imaging development.

‡Silverlight:  SXre Silverlight ,  and WindoZs Phone ,maging S'..


‡Image Formats & Compression: SXSSorts  image Iormats and comSressions inclXding 7,FF, (X,F, P'F,
-P(*, -%,* and CC,77 **.
‡Scanning: 7WA,1 W,A  bit , aXtodetect oStimXm driver settings Ior high sSeed scanning.
‡Viewer Controls: Win Forms, Web Forms, WPF, Silverlight, ActiveX and COM.
‡Image Processing:  ¿lters, transIorms, color conversion and draZing IXnctions
sXSSorting region oI interest and e[tended gra\scale data. 'eveloS \oXr
‡Document Cleanup/Preprocessing: AXtodesNeZ, desSecNle, hole SXnch, line and border aSSlication
removal, inverted te[t correction and more Ior oStimXm resXlts in OC5 and %arcode recognition.
Zith the
‡Barcode: AXtodetect, read and Zrite ' and ' barcodes Ior mXltithreaded
  bit develoSment. same robXst
‡OCR/ICR/OMR: FXll Sage or ]onal recognition Ior mXltithreaded  and  bit imaging
develoSment Zith P'F, P'FA, XPS, 'OC, XM/ and 7e[t oXtSXt. technologies
‡Forms Recognition & Processing: AXtomaticall\ identiI\ and classiI\ Iorms and Xsed b\
e[tract Xser ¿lled data.
Microsoft, HP,
‡PDF & PDF/A: 5ead, Zrite and vieZ searchable P'F Zith te[t, images, booNmarNs
and annotations.
Sony, Canon,
‡Annotations: ,nteractive 8, Ior docXment marNXS, redaction and image measXrement Kodak, GE,
inclXding sXSSort Ior ',COM annotations . Siemens, the US
‡DICOM: FXll sXSSort Ior all ,O' classes and modalities de¿ned in the ',COM standard Air Force and
inclXding (ncaSsXlated P'FC'A and 5aZ 'ata . Veterans Affairs
‡PACS: FXll sXSSort Ior ',COM messaging and secXre commXnication enabling TXicN
Hospitals.
imSlementation oI an\ ',COM SC8 and SCP services.
‡Medical Image Viewer: +igh level disSla\ control Zith bXiltin tools Ior image
marNXS, ZindoZ level, measXrement, ]oomSan, cine, and /87 maniSXlation.
‡Medical Web Viewer Framework: PlXgin enabled IrameZorN to TXicNl\ bXild highTXalit\, IXllIeatXred, Zebbased
medical image deliver\ and vieZer aSSlications.
‡Medical Workstation Framework: Set oI .1(7 medical and PACS comSonents that can be Xsed to bXild a IXll
IeatXred PACS WorNstation aSSlication.
‡3D: ConstrXct ' volXmes Irom ' ',COM medical images and visXali]e Zith a variet\ oI methods inclXding M,P,
Min,P, M5P, 957 and SS'.
‡Multimedia: CaStXre, Sla\, stream and convert MP(*, A9,, WM9, MP, MP, O**, ,SO, '9' and more. Stream
Irom 57SP Servers.
‡DVD: Pla\, create, convert and bXrn '9' images.
‡DVR: PaXse, reZind and IastIorZard live caStXre and 8'P or 7CP,P streams.
‡MPEG Transport Stream: With '95 Ior 8'P and 7CP,P streams aXtolive sXSSort.
Document Barcode Multimedia

Silverlight, .NET,
Windows Phone,
WPF, WCF, WF,
C API, C++ Class
Lib, COM & more!

Free 60 Day Evaluation! DICOM Medical Form Recognition & Processing Vector

www.leadtools.com/msdn
(800) 637-1840

Untitled-1 1 2/10/11 9:04 AM


It’s important to understand that the ViewBag is dynamic, not
strongly typed. ViewBag doesn’t really come with HasPrevious and
HasMore. I’ve just made them up as I’m typing the code. So don’t
be alarmed that IntelliSense doesn’t suggest this to you. You can
create any dynamic properties you’d like.
If you’ve been using the ViewPage.ViewData dictionary and are
curious how this is different, ViewBag does do the same job. But
in addition to making your code a little prettier, the properties are
typed. For example, HasNext is a dynamic{bool} and CurrentPage
is a dynamic{int}. You won’t have to cast the values when you
retrieve them later.
In the markup, I still have the customer list in the Model variable,
but there’s a ViewBag variable available as well. You’re on your own
as you type in the dynamic properties into the markup. A tooltip
reminds you that the properties are dynamic, as shown in Figure 3.
Here’s the markup that uses the ViewBag variables to determine
whether or not to display the navigation links:
Figure 4 The First Page of Customer Has Only a Link to @{ if (ViewBag.HasPrevious)
{
Navigate to the Next Page @Html.ActionLink("<<", "Index", new { page = (ViewBag.CurrentPage - 1) })
}
}
GetPagedCustomers method will now return a PagedList class rather
than a List. Figure 2 shows the new version of GetPagedCustomers. @{ if (ViewBag.HasMore)
{ @Html.ActionLink(">>", "Index", new { page = (ViewBag.CurrentPage + 1) })
With the new variables populated, let’s take a look at how the }
Index method in the HomeController can push them back to the }
View. Here’s where you can use the new ViewBag. We’ll still return This logic is a twist on markup used in the NerdDinner Application
the results of the customers query in a View, but you can additionally Tutorial, which you can find at nerddinnerbook.s3.amazonaws.com/Intro.htm.
stuff the values to help determine what the markup will look like Now when I run the app, I have the ability to navigate from one
for the next and previous links in the ViewBag. They will then be page of customers to the next.
available to the View at run time: When I’m on the very first page, I have a link to navigate to the
public ActionResult Index(int? page) next page but nothing to go to a previous page because there is
{
const int pageSize = 10; none (see Figure 4).
var customers=DataAccess.GetPagedCustomers((page ?? 0)*pageSize, pageSize); When I click the link and navigate to the next page, you can see that
ViewBag.HasPrevious = DataAccess.HasPreviousCustomers;
ViewBag.HasMore = DataAccess.HasMoreCustomers; there are now links to go to the previous or next page (see Figure 5).
ViewBag.CurrentPage = (page ?? 0); The next step, of course, will be to work with a designer to make
return View(customers);
} this paging more attractive.

Critical Piece of Your Toolbox


Summing up, while there are a number of tools to streamline
client-side paging, such as the jQuery DataTables extension and
the new ASP.NET MVC 3 WebGrid, your application needs may
not always benefit from bringing back large amounts of data.
Being able to perform efficient server-side paging is a critical piece
of your toolbox. The Entity Framework and ASP.NET MVC work
together to provide a great user experience and at the same time
simplify your development task to pull this off. „

J ULIE L ERMAN is a Microsoft MVP, .NET mentor and consultant who lives
in the hills of Vermont. You can find her presenting on data access and other
Microsoft .NET topics at user groups and conferences around the world. Lerman
blogs at thedatafarm.com/blog and is the author of the highly acclaimed book,
“Programming Entity Framework” (O’Reilly Media, 2009). You can follow her
at Twitter.com/julielerman.

Figure 5 A Single Page of Customers with Navigation Links to THANKS to the following technical expert for reviewing this article:
Go to Previous or Next Page of Customers Vishal Joshi

20 msdn magazine Data Points


© 2011 ComponentOne LCC. All rights reserved. All other product
and brand names are trademarks and/or registered trademarks of
their respective holders.

Untitled-8 1 2/8/11 2:16 PM


FORECAST: CLOUDY JOSEPH FULTZ

Cloud Services Mashup

Up until now, I’ve spent time on solutions using Microsoft Windows Figure 1 Cloud Services and Their Functionalities
Azure or SQL Azure to augment solution architecture. This month Service Functionality
I’m taking a look at how to combine multiple cloud services into a
Windows Azure Host my site and serve pages
single app. My example will combine Windows Azure, Windows
AppFabric Manage and negotiate authentication between my site
Azure AppFabric Access Control, Bing Maps and Facebook to
Access Control and Facebook
provide an example of composing cloud services.
Facebook Authenticate users and provide social network services
For those who are a little put off when thinking about federated
Bing Maps Visualize friends’ hometowns
identity or the real-world value of the social network, I’d like to
introduce Marcelus. He’s a friend of mine who owns a residential
and commercial cleaning company. Similar to my father in his This summary page is important, as I’ll need to use information
business and personal dealings, he knows someone to do or get just from it in my configuration of Facebook as an Identity Provider in
about anything you want or need, usually in some form of barter. ACS. In particular, I’ll need the Application ID and the Application
Some might recognize this as the infamous good ol’ boys’ network, secret as can be seen in the configuration information from ACS
but I look at Marcelus and I see a living, breathing example of the shown in Figure 3.
Windows Azure AppFabric Access Control service (or ACS for Note that I’ve added friends_hometown to the Application
short) combined with a powerful social network. In real life I can permissions text box. I’ll need that address to map it, and without
leverage Marcelus and others like him to help me. specifying it here I wouldn’t get it back by default. If I wanted some
However, in the virtual world, when I use a number of cloud other data to be returned about the user by the Graph API calls, I’d
services they often need to know who I am before they allow me to
access their functionalities. Because I can’t really program Marcelus
to serve Web pages, I’m going to use the cloud services in Figure 1
to provide some functionality.
The scenario is that navigation to my site’s homepage will be
authenticated by Facebook and the claims will be passed back to
my site. The site will then pull that user’s friends from Facebook
and subsequently fetch information for a selected friend. If the
selected friend has a hometown specified, the user may click on
the hometown name and the Bing Map will show it.

Configuring Authentication Between Services


The December 2010 issue of MSDN Magazine had a good overview
article for ACS, which can be found at msdn.microsoft.com/magazine/gg490345.
I’ll cover the specific things I need to do to federate my site with Face-
book. To get this going properly, I’m using AppFabric Labs, which is
the developer preview of Windows Azure AppFabric. Additionally,
I’m using Windows Azure SDK 1.3 and I’ve installed Windows Iden-
tity Foundation SDK 4.0. To get started, I went to portal.appfabriclabs.com
and registered. Once I had access to ACS, I followed the first part of Figure 2 Facebook Application Configuration Summary
the directions found at the ACS Samples and Documentation (Labs)
CodePlex page (bit.ly/fuxkbl) to get the service namespace set up. The
This article discusses a prerelease version of AppFabric Labs.
next goal was to get Facebook set up as an Identity Provider, but in All information is subject to change.
order to do that I had to first create a Facebook application (see direc- Code download available at code.msdn.microsoft.com/mag201103Cloudy.
tions at bit.ly/e9yE3I), which results in a summary like that in Figure 2.

22 msdn magazine
www.devart.com

Untitled-13 1 2/2/11 5:30 PM


at bit.ly/ew6K5z. Once installed, there
will be a right-click menu option
available to Add STS reference, as
illustrated in Figure 5.
In my sample I used a default
ASP.NET site created in Visual Studio
by selecting a new Web Role project.
Once it’s created, I right-click on the
site and go about stepping through
the wizard. I’ll configure the site to
use an existing Security Token Ser-
vice (STS) by choosing that option
in the wizard and providing a path
to the WS-Federation metadata. So,
for my access control namespace,
the path is:
jofultz.accesscontrol.appfabriclabs.com/
FederationMetadata/2007-06/
FederationMetadata.xml

Figure 3 ACS Facebook Identity Provider Configuration Using this information, the
wizard will add the config section
need to look it up at the Facebook Developers site (bit.ly/c8UoAA) and <microsoft.identityModel/> to the site configuration. Once this
include the item in the Application permissions list. is done, add <httpRuntime requestValidationMode=“2.0” />
Something worth mentioning when working with ACS: You spec- underneath the <system.web/> element. Providing that I specified
ify the Relying Parties that will use each Identity Provider. If my site localhost as a relying party, I should be able to run the application,
exists at jofultz.cloudapp.net, it will be specified as a relying party on and upon startup be presented with an ACS-hosted login page that
the Identity Provider configuration. This is also true for my localhost. will present Facebook—or Windows Live or Google, if so config-
So, in case I don’t want to push to the cloud to test it, I’ll need to con- ured. The microsoft.identityModel element is dependent upon
figure a localhost relying party and select it, as illustrated in Figure 4. existence of the Microsoft.Identity assembly, so you have to be
sure to set that DLL reference in the site to Copy Always. If it isn’t,

Something worth mentioning


once it’s pushed to Windows Azure it won’t have the DLL and
the site will fail to run. Referring to my previous statement about

when working with ACS:


needing to have configuration for localhost and the Windows Azure
hosted site, there’s one more bit of configuration once the wizard

You specify the


is complete. Thus, if the wizard was configured with the localhost
path, then a path for the Windows Azure site will need to be added

Relying Parties that will use


to the <audienceUris> element as shown here:
<microsoft.identityModel>
<service>

each Identity Provider. <audienceUris>


<add value="http://jofultz.cloudapp.net/" />
<add value="http://localhost:81/" />
</audienceUris>
Figure 3 and Figure 4 are both found on the same page for Additionally, the realm attribute of the wsFederation element in
configuring the Identity Provider. By the same token, if I only had the config will need to reflect the current desired runtime location.
it configured for localhost, but then attempted to authenticate from Thus, when deployed to Windows Azure, it looks like this for me:
my Web site, it wouldn’t work. I can create a custom login page, <federatedAuthentication>
<wsFederation passiveRedirectEnabled="true" issuer=
and there’s guidance and a sample for doing so under Application "https://jofultz.accesscontrol.appfabriclabs.com/v2/wsfederation"
Integration in the ACS management site. In this sample, I’m just realm="http://jofultz.cloudapp.net/" requireHttps="false" />
<cookieHandler requireSsl="false" />
taking the default ACS-hosted page. </federatedAuthentication>
So far I’ve configured ACS and
my Facebook application to get
them talking once invoked. The
next step is to configure this Iden-
tity Provider for my site as a means
of authentication. The easiest way
to do this is to install the Windows
Identity Foundation SDK 4.0 found Figure 4 ACS Facebook Identity Provider Configuration: Relying Parties
24 msdn magazine Forecast: Cloudy
1210msdn_GrapeCity_Insert.indd 1 1/11/11 2:16 PM
1210msdn_GrapeCity_Insert.indd 2 1/11/11 2:16 PM
However, if I want to debug this and and use. In order to make the requests,
have it work properly at run time on my I’ll need an Access Token. Fortunately, it
localhost (for local debugging), I’ll change was passed back in the claims, and with
the realm to represent where the site is the help of the Windows Identity Foun-
hosted locally, such as the following: dation SDK, the claims have been placed
<federatedAuthentication> into the principal identity. The claims look
<wsFederation passiveRedirectEnabled="true"
issuer="https://jofultz.accesscontrol. something like this:
appfabriclabs.com/v2/wsfederation" http://schemas.xmlsoap.org/ws/2005/05/
realm="http://localhost:81/" identity/claims/nameidentifier
requireHttps="false" /> http://schemas.microsoft.com/ws/2008/06/
<cookieHandler requireSsl="false" /> identity/claims/expiration
</federatedAuthentication> http://schemas.xmlsoap.org/ws/2005/05/
identity/claims/emailaddress
With everything properly configured, http://schemas.xmlsoap.org/ws/2005/05/
I should be able to run the site, and upon identity/claims/name
http://www.facebook.com/claims/AccessToken
attempting to browse to the default page http://schemas.microsoft.com/
I’ll be redirected to the ACS-hosted login accesscontrolservice/2010/07/claims/
identityprovider
page, where I can choose Facebook as the
Identity Provider. Once I click Facebook, What I really want out of this is the last
I’m sent to the Facebook login page to be part of the full name (for example, name-
authenticated (see Figure 6). identifier, expiration and so on) and the
Because I haven’t used my application related value. So I create the ParseClaims
before, Facebook presents me with the method to tease apart the claims and place
Request Permission dialog for my appli- them and their values into a hash table for
Figure 5 Add STS Reference Menu Option further use, and then call that method in
cation, as seen in Figure 7.
the page load event:
protected void ParseClaims()

For my application, I need


{
string username = default(string);
username = Page.User.Identity.Name;

to fetch the friends that IClaimsPrincipal Principal = (IClaimsPrincipal) Thread.CurrentPrincipal;


IClaimsIdentity Identity = (IClaimsIdentity) Principal.Identity;

comprise my social network foreach (Claim claim in Identity.Claims)


{

and then subsequently retrieve string[] ParsedClaimType = claim.ClaimType.Split('/');


string ClaimKey = ParsedClaimType[ParsedClaimType.Length - 1];

information about those friends. }


_Claims.Add(ClaimKey, claim.Value);

}
I create an FBHelper class where I’ll create the methods to
Not wanting to be left out of the inner circle of those who use access the Facebook information that I desire. To start, I create
such a fantastic app, I quickly click Allow, after which Facebook, a method to help make all of the needed requests. I’ll make each
ACS and my app exchange information
(via browser redirects) and I’m finally
redirected to my application. At this
point I’ve simply got an empty page,
but it does know who I am and I have
a “Welcome Joseph Fultz” message at
the top right of the page.

Facebook Graph API


For my application, I need to fetch the
friends that comprise my social net-
work and then subsequently retrieve
information about those friends. Face-
book has provided the Graph API to
enable developers to do such things.
It’s pretty well-documented, and best
of all, it’s a flat and simple implemen-
tation, making it easy to understand Figure 6 Facebook Login
msdnmagazine.com March 2011 25
The deserialization of the friends list
response results in a nested structure of
Hashtable->Hashtable->Dictionary. Thus
I have to do a little work to pull the infor-
mation out and then place it into my own
hash table. Once it’s in place, I switch to my
default.aspx page, add a ListBox, write a little
code to grab the friends and bind the result
to my new ListBox:
protected void GetFriends()
{
_Friends = FBHelper.GetFBFriends((string)_
Claims["AccessToken"]);
this.ListBox1.DataSource = _Friends;
ListBox1.DataTextField = "value";
ListBox1.DataValueField = "key";
ListBox1.DataBind();
}
If I run the application at this point, once
I’m authenticated I’ll see a list of all of my
Facebook friends. But wait—there’s more!
I need to get the available information for
Figure 7 Application Permission Request any selected friend so that I can use that to
show me their hometown on a map. Flipping
request using the WebClient object and parse the response with back to my FBHelper class, I add a simple method that will take the
the JavaScript Serializer: Access Token and the ID of the selected friend:
public static Hashtable MakeFBRequest(string RequestUrl) public static Hashtable GetFBFriendInfo(string AccessToken, string ID)
{ {
Hashtable ResponseValues = default(Hashtable); Hashtable FriendInfo =
MakeFBRequest(String.Format(_fbFriendInfoQuery, ID) +
WebClient WC = new WebClient(); "?access_token={0}", AccessToken);
Uri uri = new Uri(String.Format(RequestUrl, fbAccessToken)); return FriendInfo;
}
string WCResponse = WC.DownloadString(uri);
JavaScriptSerializer JSS = new JavaScriptSerializer(); Note that in both of the Facebook helper methods I created, I ref-
ResponseValues = JSS.Deserialize<Hashtable>(WCResponse); erence a constant string that contains the needed Graph API query:
public const string _fbFriendsListQuery =
return ResponseValues;
"https://graph.facebook.com/me/friends?access_token={0}";
}
public const string _fbFriendInfoQuery = "https://graph.facebook.com/{0}/";
With my final Facebook method in place, I’ll add a GridView to

Using ACS, I was able to create


the page and set it up to bind to a hash table, and then—in the code-
behind in the SelectedIndexChanged method for the ListBox—I’ll

a sample application from a


bind it to the Hashtable returned from the GetFBFriendInfo method,
as shown in Figure 8.

composite of cloud technology.


Now that I’ve got my friends and their info coming back from Face-
book, I’ll move on to the part of showing their hometown on a map.

Figure 8 Adding a GridView


As seen in this code snippet, each request will need to have
the Access Token that was passed back in the claims. With my protected void ListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
reusable request method in place, I create a method to fetch my Debug.WriteLine(ListBox1.SelectedValue.ToString());
friends and parse them into a hash table containing each of their Hashtable FriendInfo =
FBHelper.GetFBFriendInfo((string)_Claims["AccessToken"],
Facebook IDs and names: ListBox1.SelectedValue.ToString());
public static Hashtable GetFBFriends(string AccessToken) GridView1.DataSource = FriendInfo;
{ GridView1.DataBind();
Hashtable FinalListOfFriends = new Hashtable(); try
Hashtable FriendsResponse = MakeFBRequest(_fbFriendsListQuery, AccessToken); {
object[] friends = (object[])FriendsResponse["data"]; Dictionary<string, object> HometownDict =
(Dictionary<string, object>) FriendInfo["hometown"];
for (int idx = 0; idx < friends.Length;idx++ ) _Hometown = HometownDict["name"].ToString();
{ }
Dictionary<string, object> FriendEntry = catch (Exception ex)
(Dictionary<string, object>)friends[idx]; {
FinalListOfFriends.Add(FriendEntry["id"], FriendEntry["name"]); _Hometown = "";//Not Specified";
} }
return FinalListOfFriends; }
}

26 msdn magazine Forecast: Cloudy


LEARN TODAY. DEVELOP TODAY.
MOVE YOUR LIBRARY TO THE CLOUD

SIGN UP
FOR THE SAFARI
BOOKS ONLINE
OPEN HOUSE
Find all the latest and
most relevant resources
for Microsoft Developers
and IT developers at
Safari Books Online.

LEARN MORE AT safaribooksonline.com/msdnmag

UNLIMITED ACCESS. Sign up today for the Safari Books Online Open House and get your team or
workgroup access to the world’s most popular, fully searchable digital library.
UNLIMITED VALUE.
See why more than 15 million business and IT professionals, developers
and web designers from corporations, government agencies and academic
institutions access Safari Books Online for research, problem solving, just-in-
time learning, professional development and certification training.

Untitled-4 1 2/3/11 11:01 AM


hometown when I select it. During the
SelectedIndexChanged event discussed
earlier, I also bound a label in the page to
the name and added a client-side click event
to have the map find a location based on
the value of the label:
onclick="map.Find(null, hometown.innerText,
null, null, null, null, true, null, true);
map.SetZoomLevel(6);"
In the map.Find call, most of the trailing
parameters could be left off if so desired.
The reference for the Find method can be
found at msdn.microsoft.com/library/bb429645.
That’s all that’s needed to show and inter-
act with the map in this simple example.
Now I’m ready to run it in all of its glory.
If I’ve configured the identityModel
properly to work with my localhost as
Figure 9 Demo Homepage mentioned earlier, I can press F5 and run it
locally in debug. So, I hit F5, see a browser
window pop up, and there I’m presented
with my login options. I choose Facebook
and I’m taken to the login page shown in
Figure 6 . Once logged in, I’m directed
back to my default.aspx page, which now
displays my friends and a default map like
that in Figure 9.
Next, I’ll browse through my friends and
click one. I’ll get the information available
to me based on his security settings and the
application permissions I requested when
I set up the Identity Provider as seen in
Figure 2. Next, I’ll click in the hometown
name located above the map and the map
will move to center on the hometown, as
Figure 10 Hometown in Bing Maps seen in Figure 10.

There’s No Place Like Home Final Thoughts


For those of my friends who have specified their hometown, I want I hope I’ve clearly articulated how to bring together several aspects of
to be able to click on the hometown name and have the map navi- the Windows Azure platform, Bing Maps and Facebook—and that
gate there. The first step is to add the map to the page. This is a pretty I’ve shown how easy it is. Using ACS, I was able to create a sample
simple task and, to that end, Bing provides a nice interactive SDK application from a composite of cloud technology. With a little more
that will demonstrate the functionality and then allow you to look work, it’s just as easy to tie in your own identity service to serve as
at and copy the source. It can be found at microsoft.com/maps/isdk/ajax/. it’s needed. The beauty in this federation of identity is that using
To the default.aspx page, I add a div to hold the map, like this: Windows Azure enables you to develop against and incorporate
<div id="myMap" style="position:relative; width:400px; height:400px;" ></div> services from other vendors and other platforms—versus limiting
However, to get the map there, I add script reference and a little you into a single choice of provider and that provider’s services, or
bit of script to the SiteMaster page: having to figure out a low-fidelity integration method. There’s power
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/ in the Microsoft Windows Azure platform, and part of that power is
mapcontrol/mapcontrol.ashx?v=6.2"></script>
<script type="text/javascript"> how easily it can be mashed together with other cloud services. „
var map = null;
function GetMap() {
map = new VEMap('myMap'); JOSEPH FULTZ is an architect at the Microsoft Technology Center in Dallas, where
map.LoadMap(); he works with both enterprise customers and ISVs designing and prototyping
} software solutions to meet business and market demands. He has spoken at events
</script>
such as Tech·Ed and similar internal training events.
With that in place, when I pull up the page I’ll be presented with
a map on the default position—but I want it to move to my friend’s THANKS to the following technical expert for reviewing this article: Steve Linehan
28 msdn magazine Forecast: Cloudy
New Touchscreen Tablet
v4.5! High-Performance PDF Printer Driver
for Mobile Development!
■ Create accurate PDF documents in a fraction of the time needed
with other tools
■ WHQL tested for all Windows 32 and 64-bit platforms
■ Produce fully compliant PDF/A documents
■ Standard PDF features included with a number of unique features
■ Interface with any .NET or ActiveX programming language

v4.5! PDF Editor for .NET, now Webform Enabled


■ Edit, process and print PDF 1.7 documents programmatically
The DevTouch Pro is a new color
■ Fast and lightweight 32 and 64-bit managed code assemblies touchscreen tablet designed to provide
for Windows, WPF and Web applications mobile application developers with a
■ Support for dynamic objects such as edit-fields and sticky-notes customizable development, testing
■ Save image files directly to PDF, with optional OCR and deployment platform.
■ Multiple image compression formats such as PNG, JBIG2 and TIFF

■ Fully open customizable tablet

New! PDF Integration into Silverlight Applications ■ Develop with .NET, Java or C++
■ Server-side PDF component based on the robust Amyuni PDF ■ Unrestricted development and
Creator ActiveX or .NET components flexible quantities
■ ■ Fully supported in North America
Client-side C# Silverlight 3 control provided with source-code
■ Optimization of PDF documents prior to converting them into XAML
■ Conversion of PDF edit-boxes into Silverlight TextBox objects
■ Support for other document formats such as TIFF and XPS Learn more at www.devtouchpro.com

More Development Tools Available at:

www.amyuni.com
All trademarks are property of their respective owners. © 1999-2010 AMYUNI Technologies. All rights reserved.

USA and Canada Europe


Toll Free: 1 866 926 9864 Sales: (+33) 1 30 61 07 97
Support: (514) 868 9227 Support: (+33) 1 30 61 07 98
Info: sales@amyuni.com Customizations: management@amyuni.com

Untitled-3 1 11/8/10 2:53 PM


Untitled-3 2 12/2/10 10:47 AM
Untitled-3 3 12/2/10 10:47 AM
SHAREPOINT ONLINE

Cloud-Based
Collaboration with
SharePoint Online
Chris Mayo

With the release of Office 365, Microsoft will present ment is similar to and different from SharePoint 2010 development
the next version of Microsoft Online Services, a cloud-based col- by building solutions that run in SharePoint Online.
laboration and communication service based on SharePoint 2010, With the next release of SharePoint Online, SharePoint developers
Exchange 2010 and Lync Server 2010. Office 365, currently in beta, will be able to develop collaboration solutions using the same skills
will provide SharePoint, Exchange and Lync as a subscription-based and tools they use in developing for SharePoint 2010, including
Software as a Service (SaaS) offering, hosted in cloud datacenters Visual Studio 2010, SharePoint Designer 2010, C# or Visual Basic
managed by Microsoft. and the SharePoint APIs and SDKs. There are many similarities
SharePoint Online—the cloud-based version of SharePoint 2010— between developing for SharePoint on-premises and in the cloud,
will provide users with many of the same features of SharePoint but there are also significant differences that will impact how you
2010, but without the need to manage the hardware or software build solutions.
required for a scalable and secure collaboration solution. In this Understanding these differences will help you understand what
article, I’ll provide an overview of how SharePoint Online develop- solutions can be created to run in SharePoint Online and how to
develop those solutions.
This article is based on prerelease versions of Office 365 and
SharePoint Online. All information is subject to change. SharePoint Online Customization Similarities
In SharePoint 2010 development, you have the ability to customize
This article discusses: SharePoint using the browser and SharePoint Designer 2010 and
• SharePoint Online customization similarities and differences by building solutions using Visual Studio 2010. With SharePoint
• Developing for SharePoint Online with sandboxed solutions Online, customization with the browser and SharePoint Designer
• Creating client-side solutions with Silverlight 2010 is largely the same as SharePoint 2010 (given the feature dif-
ferences mentioned in the next section). Developing SharePoint
Technologies discussed:
Online solutions using Visual Studio 2010 is also largely the same.
SharePoint Online, SharePoint 2010 Development is done in Visual Studio 2010 against a local instance of
Code download available at: SharePoint 2010 (either running locally in Windows 7 or Windows
code.msdn.microsoft.com/mag201102SPOnline
Server 2008 R2 or in a virtual machine, or VM), leveraging the
integrated debugging experience for iterative development. When
32 msdn magazine
where multiple tenancies run on a shared datacenter infrastruc-
ture, so it make sense that solutions with Farm scope (where a
feature is activated for the entire farm) aren’t supported. Likewise,
in SharePoint Online, the highest level of access to your SharePoint
tenancy is at the site-collection level, so WebApplication-scoped
features (where a feature runs in every Web site in a Web application)
aren’t supported either.
Second, only partial-trust solutions are supported in SharePoint
Online. Full-trust solutions, where your solution would have access
beyond the site-collection level or could be granted permission to
run with admin-level privileges on the farm, also aren’t supported.
Finally, although SharePoint Online is based on SharePoint 2010,
it doesn’t have 100 percent feature parity with its on-premises coun-
terpart. For a complete feature-by-feature comparison between
SharePoint 2010 and SharePoint Online, refer to the Microsoft
SharePoint Online Beta Service Description, available from the Office
Figure 1 Specify the Site and Trust Level for PurchasingMgr 365 Beta Service Descriptions page at bit.ly/bBckol.
The feature-by-feature list shows that a majority of SharePoint
development is complete, the solution is uploaded to SharePoint customization features are supported. The lack of support for Busi-
Online using the same Solution Gallery provided in SharePoint 2010. ness Connectivity Services (BCS), External Lists and the ability to
call Web services outside SharePoint Online (which isn’t supported
SharePoint Online Customization Key Differences in partial-trust solutions) will have a significant impact on building
Although SharePoint Online is based on SharePoint 2010, there solutions that run in SharePoint Online. BCS support is planned
are some key differences to keep in mind as you develop solutions for a future release, however.
that will run in the former. First, SharePoint Online only supports With these similarities and differences in mind, let’s look at some
Site- and Web-scoped solutions. It runs in a multi-tenant cloud, examples of the types of solutions you can build to run in SharePoint

Figure 2 Item Templates Supported in Sandboxed Solutions Figure 3 NonStandBusPurchaseRequestsCT Definition


Sandbox via Elements.xml
Item Template Compatible? Notes <?xml version="1.0" encoding="utf-8"?>
Visual Web Part No Requires ASCX file be installed on <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
SharePoint Servers <Field SourceID="http://schemas.microsoft.com/sharepoint/v3"
Visual Web Part Yes Provided by installing the Visual ID="{A74E67E5-8905-4280-90C9-DEBFFC30D43D}"
Name="RequestDescription"
(Sandboxed) Studio 2010 SharePoint Power Tools DisplayName="Description"
Web Part Yes Group="Purchasing Manager Custom Columns"
Type="Note"
Sequential Workflow No Requires workflow solution be DisplaceOnUpgrade="TRUE" />
deployed as Farm Solution <Field SourceID="http://schemas.microsoft.com/sharepoint/v3"
ID="{CB5054F5-0C60-4DBE-94D2-CEFBFB793C7F}"
State Machine No Requires workflow solution be Name="Price"
Workflow deployed as Farm Solution DisplayName="Price"
Group="Purchasing Manager Custom Columns"
Business Data No Requires BCS solution be Type="Currency"
Connectivity Model deployed as a full-trust solution; DisplaceOnUpgrade="TRUE" />
feature not supported in <!-- Parent ContentType: Item (0x01) -->
SharePoint Online <ContentType ID="0x010078a81c8413f54917856495e56e7c09ed"
Application Page No Requires ASPX page be deployed Name="Purchasing Manager - Non-Standard Business Purchase Requests Content Type"
Group="Purchasing Manager Content Types"
to SharePoint Server Description=
Event Receiver Yes "Non-Standard Business Purchase Requests Content Type
for the Purchasing Manager Solution"
Module Yes Inherits="TRUE"
Version="0">
Content Type Yes <FieldRefs>
List Definition from Yes <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title"
DisplayName="Title" />
Content Type <FieldRef ID="{A74E67E5-8905-4280-90C9-DEBFFC30D43D}"
List Definition Yes Name="RequestDescription"
Required="TRUE" />
List Instance Yes <FieldRef ID="{CB5054F5-0C60-4DBE-94D2-CEFBFB793C7F}" Name="Price"
Required="TRUE" />
Empty Element Yes
</FieldRefs>
User Control No Requires ASCX file to be installed </ContentType>
on SharePoint Servers </Elements>

msdnmagazine.com March 2011 33


Figure 4 NonStandBusPurchaseRequestsListDefn Figure 5 Adding Custom Columns to the
Definition via Elements.xml NonStandBusPurchaseRequestsListDefn Default View
<?xml version="1.0" encoding="utf-8"?> <View BaseViewID="1" Type="HTML" WebPartZoneID="Main"
<Elements xmlns="http://schemas.microsoft.com/sharepoint/"> DisplayName="$Resources:core,objectiv_schema_mwsidcamlidC24;"
<!-- Do not change the value of the Name attribute below. DefaultView="TRUE" MobileView="TRUE" MobileDefaultView="TRUE"
If it does not match the folder name of the List Definition project item, SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/generic.png"
an error will occur when the project is run. --> Url="AllItems.aspx">
<ListTemplate <Toolbar Type="Standard" />
Name="NonStandBusPurchaseRequestsListDefn" <XslLink Default="TRUE">main.xsl</XslLink>
Type="10051" <RowLimit Paged="TRUE">30</RowLimit>
BaseType="0" <ViewFields>
OnQuickLaunch="TRUE" <FieldRef Name="Attachments">
SecurityBits="11" </FieldRef>
Sequence="410" <FieldRef Name="LinkTitle">
DisplayName="Purchasing Manager – </FieldRef>
Non-Standard Business Purchase Requests List Definition" <FieldRef ID="{A74E67E5-8905-4280-90C9-DEBFFC30D43D}"
Description= Name="RequestDescription" />
"Non-Standard Business Purchase Requests List Definition <FieldRef ID="{CB5054F5-0C60-4DBE-94D2-CEFBFB793C7F}" Name="Price" />
for the Purchasing Manager Solution" </ViewFields>
Image="/_layouts/images/itgen.png"/> <Query>
</Elements> <OrderBy>
<FieldRef Name="ID">
</FieldRef>
Online, including sandboxed solutions and the SharePoint client </OrderBy>
</Query>
object model (OM). Other solution types, such as automating busi- <ParameterBindings>
ness processes via declarative workflow solutions, will be covered <ParameterBinding Name="NoAnnouncements"
Location="Resource(wss,noXinviewofY_LIST)" />
in future articles. <ParameterBinding Name="NoAnnouncementsHowTo"
Location="Resource(wss,noXinviewofY_DEFAULT)" />
Developing for SharePoint Online </ParameterBindings>
</View>
with Sandboxed Solutions
From the previous section, you know that SharePoint Online solu- http://o365dpe.contoso.com/sites/spomsdnmag/purchasing. My
tions must be scoped to site or Web features, are restricted to data first solution will deploy the list used to track these non-standard
in the site collection and must run in partial trust. Developing purchases. I’ll open Visual Studio 2010, select File | New Project,
solutions that run as sandboxed solutions meet all these criteria and in the New Project dialog I’ll select Empty SharePoint Project
while letting SharePoint Online administrators easily deploy a and name the project PurchasingMgr.
solution by uploading it directly to the Solution Gallery. In the SharePoint Customization Wizard dialog, for “What local
Visual Studio 2010 provides great support for sandboxed solutions, site … ,” I’ll enter the URL of my site, http://o365dpe.contoso.com/
including project template and project item template support, the sites/spomsdnmag/Purchasing/, select “Deploy as a sandboxed
SharePoint Customization Wizard for creating new projects as sand- solution” and click Finish as seen in Figure 1.
boxed solutions, IntelliSense support for the site collection-scoped Next, I’ll select the PurchasingMgr project in Solution Explorer,
SharePoint APIs, and debugging and packaging support. To get right-click and select Add | New Item. In the Add New Item dialog, I’ll
started building a solution for SharePoint Online, you’ll develop and select SharePoint 2010 in the Installed Templates node to the supported
debug the solution locally against SharePoint 2010.
You’ll need either Windows 7 64-bit or Windows
Server 2008 R2 installed along with SharePoint
2010 and Visual Studio 2010. Another great way to
get started is to use the 2010 Information Worker
Demonstration and Evaluation Virtual Machine
(RTM), which provides a virtualized SharePoint 2010
development environment (download it from bit.ly/
ezfe2Y). I also recommend the Visual Studio 2010
SharePoint Power Tools (bit.ly/azq882), which add
compile-time support for the sandbox and a
sandboxed Visual Web Part project item template.
In the examples in this article, I’ll build a solu-
tion using the simple scenario of providing the
employees of the fictional Contoso Corp. with the
ability to request purchases that aren’t supported
in their procurement system. To get started, I’ll
create a site collection and site in my on-premises
SharePoint 2010 development environment. I’m
using the VMs mentioned previously, so I’ve created Figure 6 Debugging the PurchasingMgr Solution
34 msdn magazine SharePoint Online
Untitled-8 1 2/8/11 1:33 PM
SharePoint item templates. Not all of these templates
are supported under sandboxed solutions and there-
fore in SharePoint Online. Figure 2 shows the item
templates supported under sandboxed solutions.
To build my list, I’ll define Site Columns and a
Content Type for the list by selecting the Content
Type item template and entering NonStandBus-
PurchaseRequestsCT for the name.
In the SharePoint Customization Wizard, I’ll
select Item as the base content type and click Fin-
ish. The Content Type will have a Title column, a
Description column and a Price column, which I’ll
define declaratively by replacing the contents of the
Elements.xml created with the XML in Figure 3.
Next, I’ll define a list based on that content
type by right-clicking PurchasingMgr in Solution
Explorer and selecting Add New Item. I’ll select the
List Definition from Content Type item template
and name the list definition NonStandBusPurchase- Figure 7 PurchasingMgr Solution Deployed to SharePoint Online
RequestsListDefn and click Add.
In the SharePoint Customization Wizard, I’ll select the content my site Collection and select Site Actions | Site Settings, and then
type created earlier and check the “Add a list instance” box. The Solutions to access the Solutions Gallery. In the Solution Gallery,
Elements.xml created for the NonStandBusPurchaseRequestsList- I’ll click the Solutions tab and select Upload Solution in the
Defn is shown in Figure 4. ribbon, then browse to the PurchasingMgr.wsp file in bin\Debug
Note that each list definition in my feature needs to be identified and click OK and then Activate. You’ll see your solution in the
by a unique Type value greater than 10,000 (to avoid conflicts with Solution Gallery as seen in Figure 7.
lists defined by SharePoint), and that I use that value in defining Next, to activate the feature that contains my site columns,
any list instance based on that definition. content type and list, I’ll navigate to the Purchasing site and select
To add the custom columns to the list view, I’ll open Schema.xml Site Actions | Site Settings | Manage Site Features. I’ll select the
created and add the FieldRef elements to the default view, as seen
in Figure 5. Figure 8 NonStandBusPurchaseReqsSLOM MainPage.xaml
Finally, I’ll define an instance of the list by selecting ListInstance1 <UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/
under NonStandBusPurchaseRequestsListDefn and rename it presentation/sdk"
x:Class="NonStandBusPurchaseReqsSLOM.MainPage"
NonStandBusPurchaseRequestsListInstance. I’ll open Elements.xml xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
and add the following XML to base the list on the content type and xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
to provide helpful descriptions for users: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
<?xml version="1.0" encoding="utf-8"?> mc:Ignorable="d"
<Elements xmlns="http://schemas.microsoft.com/sharepoint/"> d:DesignHeight="300" d:DesignWidth="400">
<ListInstance Title="Non-Standard Business Purchase Requests"
OnQuickLaunch="TRUE" <Grid x:Name="LayoutRoot" Background="White">
TemplateType="10051" <Grid.ColumnDefinitions>
Url="Lists/NonStandBusPurchaseRequestsListInstance" <ColumnDefinition />
Description= <ColumnDefinition />
"Non-Standard Business Purchase Requests List </Grid.ColumnDefinitions>
for the Purchasing Manager Solution"> <Grid.RowDefinitions>
</ListInstance> <RowDefinition Height="Auto"/>
</Elements> <RowDefinition Height="Auto"/>
In Visual Studio 2010, I’ll select Debug and then Start Debugging <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
to test the solution. The solution is packaged and deployed to my </Grid.RowDefinitions>
on-premises site, as seen in Figure 6.
<sdk:Label Content="Title:" Grid.Column="0" Grid.Row="0" Margin="3"/>
Now that I’ve tested the PurchasingMgr solution, I’m ready <sdk:Label Content="Description:" Grid.Column="0" Grid.Row="1" Margin="3"/>
to deploy it to SharePoint Online. I’ll create a new site collection <sdk:Label Content="Price:" Grid.Column="0" Grid.Row="2" Margin="3"/>

in SharePoint Online named Purchasing using the Team Site <TextBox Name="Title" Grid.Column="1" Grid.Row="0" Margin="3"/>
template. Back in Visual Studio 2010, I’ll package the solution by <TextBox Name="Description" Grid.Column="1" Grid.Row="1" Margin="3"/>
<TextBox Name="Price" Grid.Column="1" Grid.Row="2" Margin="3"/>
right-clicking on the PurchasingMgr project in the Solution Explorer
and selecting Package. To deploy the solution to SharePoint Online, <Button Content="Add" Grid.Column="1" Grid.Row="3" Margin="3"
Name="addNonStanPurchaseReq" HorizontalAlignment="Right"
I’ll just need to upload it to the Solution Gallery and activate the Height="25" Width="100" Click="addNonStanPurchaseReq_Click" />
site features (I’ll need site collection administrator privileges to </Grid>
</UserControl>
do so). To do this, I’ll log in to SharePoint Online and navigate to
36 msdn magazine SharePoint Online
Untitled-8 1 2/8/11 1:34 PM
Creating Client-Side Solutions
with Silverlight
The client OM, also introduced with SharePoint
2010, provides an object-oriented, client-side API
for SharePoint clients built using the Microsoft
.NET Framework, Silverlight and ECMAScript (in-
cluding JavaScript and JScript) that run on remote
computers (including the browser for Silverlight
and ECMAScript). The API is consistent with the
Microsoft.SharePoint server-side namespace, so
it’s easy to learn. The API is also consistent across
the supported client types, so it’s easy to apply that
knowledge across different client solutions. The
client OM API is supported in SharePoint Online
and is a valuable tool for cloud development.
For example, I can use the client OM to create
a Silverlight 4 application to add items to my list
and host the application in a sandboxed Web Part.
Figure 9 MainPage.xaml in Designer To do this, I’ll open Visual Studio 2010, select File |
New Project and in the New Project dialog select
Purchasing Manager - Content Types and Lists feature and select Empty SharePoint Project. I’ll name the project PurchasingMgrWP
Activate. At this point you should see the Non-Standard Business and click OK. Again, I’ll create the solution as a sandboxed solu-
Purchase Requests lists in your SharePoint Online site. tion and point it at my on-premises Purchasing site. To create the
The Purchasing Manager is just one example of what you can ac- Silverlight 4 application, I’ll right-click on the PurchasingMgrWP
complish in SharePoint Online with sandboxed solutions. Keep in solution, select Silverlight under Installed Templates, select
mind the limitations in sandboxed solutions and in the SharePoint Silverlight Application and name the solution NonStandBus-
Online supported features, and you can create solutions that will PurchaseReqsSLOM. In the New Silverlight Application dialog,
run in SharePoint 2010 or SharePoint Online. I’ll uncheck “Host the Silverlight application in a new Web Site”

Figure 10 addNonStanPurchaseReq_Click
using System; "Non-Standard Business Purchase Requests");
using System.Collections.Generic;
using System.Linq; ListItem newListItem =
using System.Net; nonStandBusPurList.AddItem(new ListItemCreationInformation());
using System.Windows; newListItem["Title"] = Title.Text;
using System.Windows.Controls; newListItem["RequestDescription"] = Description.Text;
using System.Windows.Documents; newListItem["Price"] = Price.Text;
using System.Windows.Input;
using System.Windows.Media; newListItem.Update();
using System.Windows.Media.Animation;
using System.Windows.Shapes; clientContext.Load(nonStandBusPurList, list => list.Title);

using Microsoft.SharePoint.Client; clientContext.ExecuteQueryAsync(onQuerySucceeded, onQueryFailed);


}
namespace NonStandBusPurchaseReqsSLOM
{ private void onQuerySucceeded(
public partial class MainPage : UserControl object sender, ClientRequestSucceededEventArgs args)
{ {
private string webUrl; Dispatcher.BeginInvoke(() =>
{
public MainPage(string url) MessageBox.Show("New item added.");
{ });
webUrl = url; }

InitializeComponent();
} private void onQueryFailed(object sender,
ClientRequestFailedEventArgs args)
private void addNonStanPurchaseReq_Click(object sender, RoutedEventArgs e) {
{ Dispatcher.BeginInvoke(() =>
ClientContext clientContext = new ClientContext(webUrl); {
MessageBox.Show("Request failed. " + args.Message + "\n" +
Web webSite = clientContext.Web; args.StackTrace);
ListCollection webLists = webSite.Lists; });
}
List nonStandBusPurList = }
clientContext.Web.Lists.GetByTitle( }

38 msdn magazine SharePoint Online


DESIGN
Design Applications That Help Run the Business

Our xamMap™ control in Silverlight and


WPF lets you map out any geospatial
data like this airplane seating app to
manage your business. Come to
infragistics.com to try it today!

Infragistics Sales 800 231 8588


Infragistics Europe Sales +44 (0) 800 298 9055
Infragistics India +91 80 4151 8042
@infragistics

Untitled-6 1 11/10/10 11:41 AM


(we’ll test by hosting the application in SharePoint) and select Note the similarity to the SPWeb, SPListCollection and SPList classes.
Silverlight 4 for the Silverlight Version. Finally, I’ll create a ListItem by calling the List.AddItem method, pop-
To reference the Silverlight client OM API, I’ll add references to ulate it with data from the UI and call the ListItem.Update method.
Microsoft.SharePoint.Client.Silverlight.dll and Microsoft.Share- The ListItem isn’t actually created until the ClientContext.Load
Point.Client.Silverlight.Runtime.dll in C:\Program Files\Common and ClientContext.ExecuteQueryAsync methods are called to
Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\ execute the query. Note that you can save roundtrips to the server
LAYOUTS\ClientBin. Next, I’ll create the Silverlight UI by opening by loading multiple queries via ClientContext.Load, calling the
MainPage.xaml and replacing the XAML with the code in Figure 8. ClientContext.ExecuteQueryAsync method.
The XAML in Figure 8 defines text boxes and a button to collect To deploy the Silverlight 4 application, I’ll add a module to deploy
information to add to my list as seen in Figure 9. the application with my Web Part project. I’ll select Purchasing-
Double-click the button in the designer and replace the class MgrWP in the Solution Explorer, right-click and select Add | New
with the code in Figure 10. Item |Module and name the module ClientBin. I’ll replace the
The code in Figure 10 follows a common pattern in client OM contents of Elements.xml created with this XML:
code. First, I’ll get access to the client context via the ClientContext <?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
class (which is equivalent to the SPContext class). Next, I’ll access the <Module Name="ClientBin">
site and list via the Web, ListCollection and List classes, respectively. <File Path="ClientBin\NonStandBusPurchaseReqsSLOM.xap"
Url="ClientBin/NonStandBusPurchaseReqsSLOM.xap" />
</Module>
Figure 11 Adding the SilverlightObjectTagControl.cs Helper Class </Elements>
This XML deploys the NonStandBusPurchaseReqsSLOM.xap
using System; file to the ClientBin folder in my SharePoint site.
using System.Collections.Generic;
using System.Linq; To deploy the output of the NonStandBusPurchaseReqsSLOM
using System.Text; project with the ClientBin module, I’ll select the ClientBin module in
using System.Web; Solution Explorer and open the Project Output References property
using System.Web.UI; dialog. I’ll click Add and select NonStandBusPurchaseReqsSLOM
using System.Web.UI.WebControls;
as the Project Name and ElementFile as the DeploymentType.
namespace PurchasingMgrWP Next, I’ll add a custom Web Part to my SharePoint solution to
{
class SilverlightObjectTagControl : WebControl host my Silverlight 4 application. I’ll select PurchasingMgrWP in
{ the Solution Explorer, right-click and select Add | New Item, select
public string Source { get; set; }
public string InitParameters { get; set; } Web Part and give the Web Part the name NonStandBusPurchase-
ReqsWP. I’ll use a custom Web Part in order to pass parameters to
protected override void CreateChildControls()
{ my Silverlight 4 application, such as the URL of the site used to
base.CreateChildControls(); create ClientContext. In order to do this, I’ll add a helper class called
if (Source != null && Source != "") SilverlightObjectTagControl.cs and replace the body of that class
{ with the code in Figure 11.
string width = (this.Width == Unit.Empty) ? "400" :
this.Width.ToString();
string height = (this.Height == Unit.Empty) ? "300" : Figure 12 NonStandBusPurchaseReqsWP.cs
this.Height.ToString();
using System;
this.Controls.Add(new LiteralControl( using System.ComponentModel;
" <div>" + using System.Web;
" <object data=\"data:application/x-silverlight-2,\" + using System.Web.UI;
“ type=\"application/x-silverlight-2\" width=\"" + width + using System.Web.UI.WebControls;
“ "\" height=\"" + height + "\">" + using System.Web.UI.WebControls.WebParts;
" <param name=\"source\" value=\"" + Source + "\"/>" + using Microsoft.SharePoint;
" <param name=\"onerror\" value=\"onSilverlightError\" />" + using Microsoft.SharePoint.WebControls;
" <param name=\"background\" value=\"white\" />" +
" <param name=\"minRuntimeVersion\" value=\"4.0.50826.0\" />" + namespace PurchasingMgrWP.NonStandBusPurchaseReqsWP
" <param name=\"autoUpgrade\" value=\"true\" />" + {
" <param name=\"initparams\" value=\"" + InitParameters + "\" />" + [ToolboxItemAttribute(false)]
" <a href=\"http://go.microsoft.com/fwlink/?LinkID=" + public class NonStandBusPurchaseReqsWP : WebPart
" 149156&v=4.0.50826.0\" + {
" style=\"text-decoration: none;\">" + protected override void CreateChildControls()
" <img src=\"http://go.microsoft.com/fwlink/?LinkId=161376\" + {
" alt=\"Get Microsoft Silverlight\" style=\"border-style: none\"/>" + base.CreateChildControls();
" </a>" +
" </object>" + SilverlightObjectTagControl slhc =
" <iframe id=\"_sl_historyFrame\" + new SilverlightObjectTagControl();
" style='visibility:hidden;height:0;width:0;border:0px'></iframe>" + slhc.Source = SPContext.Current.Site.Url +
" </div>" "/ClientBin/NonStandBusPurchaseReqsSLOM.xap";
)); slhc.InitParameters = "url=" + SPContext.Current.Web.Url;

} this.Controls.Add(slhc);
} }
} }
} }

40 msdn magazine SharePoint Online


DEVELOP
Rich Business Intelligence Applications in WPF and Silverlight

Robust Pivot Grids for WPF and


Silverlight let your users analyze data
to make key business decisions.
Visit infragistics.com to try it today!

Infragistics Sales 800 231 8588


Infragistics Europe Sales +44 (0) 800 298 9055
Infragistics India +91 80 4151 8042
@infragistics

Untitled-6 1 11/10/10 10:57 AM


constructor of the MainPage class in NonStandBus-
PurchaseReqsSLOM, open App.xaml.cs and add the
following code to the Application_Startup event:
private void Application_Startup(object sender,
StartupEventArgs e)
{
string url = e.InitParams["url"];

this.RootVisual = new MainPage(url);


}
To test the Web Part, deploy the Purchasing-
Mgr.wsp package to the on-premises Purchasing
site to deploy the Non-Standard Business Purchase
Requests list (the list was removed when the
debug session listed earlier was ended), and then
debug the PurchasingMgrWP solution from
Visual Studio 2010. When added to \Purchasing\
Home.aspx, the Web Part allows me to add items
directly to the list from Silverlight, as seen in
Figure 13 and Figure 14.
Developing and debugging against the on-
Figure 13 The NonStandBusPurchaseReqsWP in Action premises site allows me to use Visual Studio 2010
to debug both the SharePoint and Silverlight 4
code until I have the solution tested completely. At
that point, I’ll upload the PurchasingMgrWP.wsp
to the Solution Gallery in SharePoint Online.
The SharePoint client OM provides a familiar
and consistent object-oriented API for accessing
lists and libraries in SharePoint Online. The API
is a subset of the Microsoft.SharePoint API and is
scoped to the site collection and below, which is per-
fectly aligned to SharePoint Online development.

SharePoint Solutions in the Cloud


Summing up, SharePoint Online provides SharePoint
developers a unique opportunity to build SharePoint
solutions for the cloud using the skills and tools they
already have. By understanding SharePoint Online
customization features (including what’s supported
and what’s not), sandboxed solutions, the SharePoint
client OM and declarative workflows built using
SharePoint Designer 2010, you can build SharePoint
Figure 14 The Updated Non-Standard Business Purchase Requests List solutions that run in the cloud with SharePoint
Online. To keep up on SharePoint Online devel-
The SilverlightObjectTagControl class in Figure 11 has two opment throughout the beta process, check out the SharePoint
properties: Source is used to pass the URL of the Silverlight Online Developer Resource Center (msdn.com/sharepointonline). „
application to load in the Web Part, and InitParameters to pass
initialization parameters to the Silverlight 4 application. These CHRIS MAYO is a technology specialist focusing on Office 365 and SharePoint
properties are used to build the <object /> tag for the Silverlight Online. He has experience as both a writer and a public speaker delivering tech-
application in the CreateChildControls method. To use this class, nical content to audiences ranging in size from small groups to thousands. He
open NonStandBusPurchaseReqsWP.cs and replace the code in coauthored “Programming for Unified Communications with Microsoft Offi ce
Communications Server 2007 R2” (Microsoft Press, 2009). He’s been with
that class with the code in Figure 12. Microsoft for 10 years. Prior to joining Microsoft, he served as a developer and
The code in Figure 12 creates an instance of SilverlightObject- architect in the IT departments of Fortune 500 companies in the retail and finance
TagControl, sets the Source property to the URL of the Silverlight industries. Keep up with Mayo at his blog, blogs.msdn.com/cmayo.
application in ClientBin and sets the InitParameters property to
hold the URL of the current site (where the Non-Standard Busi- THANKS to the following technical experts for reviewing this article:
ness Purchase Requests list can be found). To pass the URL to the George Durzi, Steve Fox, AJ May and Christina Storm

42 msdn magazine SharePoint Online


EXPERIENCE
Beautiful Data Visualizations That Bring Your Data to Life

Use our Motion Framework™ to see your data


over time and give your users new insight
into their data. Visit infragistics.com/motion
to try it today!

Infragistics Sales 800 231 8588


Infragistics Europe Sales +44 (0) 800 298 9055
Infragistics India +91 80 4151 8042
@infragistics

Copyright 1996-2010 Infragistics, Inc. All rights reserved. Infragistics, the Infragistics logo and NetAdvantage are registered trademarks of Infragistics, Inc.
Motion Framework is a trademark of Infragistics, Inc.

Untitled-1 1 1/11/11 1:39 PM


B I Z TA L K E D I S O L U T I O N S

Processing Health
Care Claims with
BizTalk Server 2010
Mark Beckner

BizTalk Server 2010 introduces an entirely re-architected • Configuring ports to enable reception or delivery of docu-
platform for managing the exchange of Electronic Data Interchange ments, whether over FTP, AS2 or another protocol.
(EDI) documents between trading partners. If you’ve worked with For purposes of illustration, we’ll look at the exchange of doc-
earlier editions of the platform, you may have encountered some uments between a hospital and a claims-processing unit. The
headaches with setting up document exchange between various documents exchanged between parties will be Health Insur-
parties. With the latest release, you have complete control over ance Portability and Accountability Act (HIPAA)-compliant 837
the organization and configuration of document settings related Professional and Institutional files.
to any interchange between parties.
BizTalk Server 2010 also provides a much-improved experience Working with Schemas
when developing maps. In most EDI solutions, you’ll be working with two basic types of
To illustrate these new features, I’m going to walk you through schema: the EDI schemas that represent the flat files exchanged with
the steps required for building out an EDI solution. Every EDI trading partners, and the internal schemas that represent document
solution developed in BizTalk Server 2010 follows a basic pattern: types needed for processing the data within the EDI document.
• Adding and modifying base EDI schemas. In my example, the solution exchanges the 837 documents with
• Creating maps between the EDI document and internal/ external parties, but uses a different document format for internal
external messaging. processing. The internal schema represents an ECSIF flat file: a
• Implementing orchestrations to handle workflow associated common format for claims processors. The 837 schemas that ship
with processing the EDI documents. with BizTalk and can be added to a Visual Studio project, but the
• Configuring trading partner settings, including parties, internal schemas (such as an ECSIF) must be built by hand.
business profiles, agreements and acknowledgments. BizTalk Server 2010 ships with thousands of existing schemas
that define a majority of the EDI documents in use today. To access
This article discusses: the schemas, run the MicrosoftEdiXSDTemplates.exe executable
found in the $\Program Files\Microsoft BizTalk Server 2010\XSD_
• EDI schemas
Schema\EDI directory. For the purposes of this example, I’ll use
• Developing EDI maps
the 837 Professional and Institutional HIPAA-compliant schemas
• Trading partner configurations found in the HIPAA\00501 folder. Adding the XSD file to a Visual
• Ports and document delivery Studio project allows it to be referenced and used by other BizTalk
Technologies discussed: components—most importantly, by maps.
BizTalk Server 2010
Figure 1 shows the 837 Professional 5010 schema within Visual
Studio 2010. Notice the number of nodes on this schema: the 837 is
44 msdn magazine
one of the most complex EDI documents, shelling out to an external XSLT stylesheet is
and can be extremely tricky to work with. in order (which bypasses the BizTalk mapper
It contains hundreds of nodes that are vir- altogether). EDI maps can become compli-
tually identical, representing subscriber cated and require ingenuity and planning to
and patient information. end up with the needed solution.
Figure 2 shows the internal schema rep- To illustrate the use of inline C#, let’s
resenting the ECSIF format. This schema look at a common function of mapping
was generated using the Flat File Schema an outbound 837 Professional or Insti-
Wizard. The wizard can be pointed to a tutional file: the mapping of hierarchical
valid flat file instance to create an XSD. (HL) segments. The HL segments require
A number of the fields in the FileHeader incremental values for each record in the
node have been promoted in this schema. file, and denote parent/child relationships.
Promoted fields allow for improved filter- There really is no traditional functoid
ing and mapping options. combination that will allow these values
Once the schemas have been defined to be set properly. There is, however, a
and added to the Visual Studio project, simple inline C# approach that allows for
you can begin building out the maps. In the correct values. This approach requires
this case, I’ll look at several scenarios that two scripting functoids: one that stores a
are useful in mapping an 837 document. Figure 1 The 837 Professional 5010 Schema global variable and maps HL01, while the
second maps HL02 (which is dependent
Developing Maps on the value of HL01).
The mapping interface in BizTalk Server 2010 has The HL01 functoid script looks like this:
been extensively revised and introduces a variety of int intHL01;
public int getHL01() {
new capabilities. These capabilities include zoom- intHL01++;
ing, automatic node matching and search capabili- return intHL01;
}
ties. One of the most noticeable enhancements is
the ability to click on a line or functoid and have all Here’s the code for the HL02 script:
public int getHL02() {
other mappings fade into the background. return intHL01 -1;
As any EDI map developer will know, some maps }
get extremely complex, with multiple tabs and Figure 4 shows the functoids placed in the map.
dozens (or even hundreds) of functoids. Finding Another situation that frequently arises in
out what data in the source schema maps to what mapping EDI documents is the need to use inline
data in the target schema and what functoids are Figure 2 The Target ECSIF XSLT. This is one of the most important skills to
being used to do this mapping can be difficult to Schema Format incorporate into BizTalk mapping, and is some-
visualize. By clicking on any of the lines or func- thing you should acquaint yourself with. It allows
toids used, all related mappings will be highlighted. for many options around looping and node creation that simply
An example of a complex map with a specific set of mapping aren’t available using standard functoid combinations.
logic highlighted is shown in Figure 3 . This brings up a point One illustration of using Inline XSLT in a map is shown in Figure
of BizTalk map development that’s often overlooked: there are 5. This code demonstrates how to use the Inline XSLT Call Template
times to use the map interface, and there are times not to use it. functionality to pass in a parameter from the source document
Not all mapping that’s done in BizTalk is best suited for standard (Name) and create a node in the target 837 document.
mapping—sometimes using alternative approaches is in order. When developing a BizTalk map, always think about the long-
Alternatives include inline scripting and external components, term maintainability of the map. Is this something that you’ll be
including XSLT and Microsoft .NET Framework components. able to easily update? Is this something that someone else can work
BizTalk map development generally consists of a combination of with in the future? Good coding practice should not be forgotten
standard functoids and inline XSLT and C#. There are even times when when working with BizTalk maps, and some amount of architecture

Figure 3 Highlight Capabilities in BizTalk Server 2010 Maps


msdnmagazine.com March 2011 45
and planning should be involved when developing maps that have
a lot of logic or complex functionality within them.

Orchestrations
The use of orchestrations in EDI solutions is not a requirement. Often,
documents simply need to be mapped from one format to another
and delivered, without the need for the inclusion of workflow.
Figure 4 Mapping HL Segment on Outbound 837 In some cases, however, a document may need to have several
steps of processing done to it before it’s ready to be
delivered. To illustrate this, I’ll set up an orchestration to
map and archive messages to a table in SQL Server. The
orchestration will be configured with a filter to ensure
only documents of a certain type are processed by it.

In most EDI solutions,


you’ll be working with two
basic types of schema.
The orchestration can be set up to subscribe to just
about any of the fields within the ISA or ST segments
of an EDI document (among many other properties).
To configure an orchestration to subscribe to a specific
field on a document, a filter can be set on the initial
receive shape of the orchestration, as shown in Figure 6.
With the filter specified, the orchestration can now
Figure 5 Passing Parameters to an Inline XSLT Call Template Script do the necessary processing of the EDI document. In
this case, the orchestration is going to map the data
from the 837 format to the ECSIF format and then
write this information to an archive table in SQL
Server. The mapping of the documents is done through
a transform shape and the inclusion of a map file, but
the writing of the information to SQL Server has a
number of options available for it.
When thinking of SQL Server, many BizTalk
developers assume that they need to use one of the
adapters available for writing to SQL. The truth is
that, in most cases, the SQL adapters are overly com-
plex for basic database calls. Generally, the easiest and
most supportable approach for interacting with SQL
Server is through the use of a custom .NET assembly
class. When developing classes that will be called from
orchestration, always make sure and mark the class
Figure 6 Setting a Filter on an Orchestration as serializable to ensure that BizTalk can call it from
any type of transaction state:
namespace Demo.BizTalk.Helper {
[Serializable]
public class DataAccess
{ }
}
Developing orchestrations for EDI solutions is no
different than for other types of BizTalk implementa-
tions. The main thing to remember when developing
orchestrations is to keep it simple. There’s an art to
Figure 7 Top-Level Party Configuration BizTalk development and to organizing your BizTalk
46 msdn magazine BizTalk EDI Solutions
Untitled-11 1 1/7/11 4:08 PM
projects properly. If you’ve planned and and some of the envelope settings are
developed properly, you’ll end up with a all configured on a business profile. In
solution that’s easy to make updates to many cases, the default information
and deploy to a production environment. can be used at this level, because the
configuration of the specific agree-
Trading Partner ments of a business profile will define
Configurations this information and more.
BizTalk Server 2010 introduces a new A business profile can have one or
interface for managing the exchange of more agreements. An agreement rep-
EDI documents between trading part- resents the way that two parties are
ners. It’s made up of three basic tiers Figure 8 Business Profiles Associated with Parties expecting to exchange one or more
of organization: the party, the business document types with one another. This
profile and the agreement. is where the specifics about the envelope, the acknowledgments and
The party represents the top-level organization of the trading the transaction sets allowed are defined. One agreement could al-
partner. Configurations on this artifact relate to things that are low for certain documents to be exchanged with 997 acknowledg-
common across all documents being exchanged with this trading ments configured, while another agreement could allow for other
partner. For example, certificates that may be required for secure document types to forego the acknowledgments.
communication would be configured at this level. In my example, the claims processor and the hospital are exchang-
ing documents. The hospital will send the claim (X12 837 Institutional

BizTalk Server 2010 introduces a


version 5010) to the claims processor, and the claims processor will
send an acknowledgment (X12 997) back to the hospital. Figure 9

new interface for managing the


shows the envelope identifier configuration and Figure 10 shows
the transaction set configuration on the agreement for documents

exchange of EDI documents.


flowing from the hospital to the claims processor. Note the tabs at
the top of the window indicating the flow of the document.
Figure 11 shows the configuration of the acknowledgments that
In my example, I have two parties: the claims processor and are sent back from the claims processor to the hospital.
the hospital. Each is set up as its own unique BizTalk
party. All that needs to be configured is the name, as
shown in Figure 7.
A party will have one or more business profiles associ-
ated with it. The business profile represents a department
within an organization that has its own unique business
identity when sending or receiving EDI documents. A
business identity is the value that appears in the ISA06
or ISA08 segment of an EDI document (depending on
whether the document is being sent or received) and
uniquely identifies the trading partner from all other
entities. Many organizations will have a single business
profile, but some will require multiple profiles.
In my example, the claims processor has two business Figure 9 Configuring ISA Envelope Settings on an Agreement
profiles: one that represents professional claims pro-
cessing, and another that represents institutional claims
processing. The hospital also has two business profiles:
the national branch and the international branch. Fig-
ure 8 shows the parties with their business profiles.
Because the business profile represents a unique
business identity within a party, configurations at this
level deal with information that’s common across all
documents that will be exchanged with this identity.
All inbound and outbound settings that are common
across all document types being exchanged will be
configured: the protocols used (X12, EDIFACT, AS2),
the validation settings, the transaction sets (EDI doc-
uments are allowed at this level), acknowledgments Figure 10 Configuring Transaction Sets on an Agreement

48 msdn magazine BizTalk EDI Solutions


Untitled-1 1 2/10/11 9:04 AM
If you’re exchanging documents with more than a single trading document within BizTalk into the flat file EDI document expected
partner, you’ll likely find that the majority of your configurations are by trading partners. The pipeline contains the logic to interpret (or
virtually identical—the only thing changing will be the identifiers create) the EDI envelope on a document and to determine which
in the ISA segment of the envelope. party the document resolves to.
To ease development, make sure to use the template functionality

Configuring the send port to


available from the agreement configuration screen. There are two
buttons you’ll be interested in: Save As Template and Load From

deliver documents to a specific


Template. When you have a trading partner fully configured, and the
EDI documents are flowing end-to-end with the correct envelope

location is straightforward.
settings and acknowledgments, simply save the agreement settings as
a template and use them as the starting point for future agreements.

Port Configurations and Document Delivery To understand how ports process EDI documents, let’s look
The actual delivery of documents to or from BizTalk to external at sending an acknowledgment. As you saw earlier, acknowledg-
trading partners is done through the configuration of ports. The ments are configured on the agreements of a BizTalk party. When
port defines the type of delivery mechanism (FTP, file and so a document arrives, BizTalk can automatically generate a 997
on) and contains the BizTalk pipeline that transforms the XML acknowledgment. What happens is that BizTalk creates the 997
XML and drops it on the BizTalk message box, but it
doesn’t actually route the message anywhere. A send
port must be set up and configured to convert the
XML to a flat file, add the envelope and deliver it to the
appropriate destination.
Setting up the send port to deliver an acknowledg-
ment requires three basic steps:
1. The definition of the send port and delivery protocol
2. The inclusion of the EdiSend pipeline (or custom
Figure 11 Configuring Acknowledgements on an Agreement pipeline with EDI pipeline components)
3. The configuration of filters to listen for appro-
priate acknowledgments
Configuring the send port to deliver documents to
a specific location is straightforward. Figure 12 shows
a Send Port configured with the EdiSend pipeline. The
send port will write files out to a file directory with the
full EDI envelope and formatting in place.
Setting the filter on the send port is also easy to do.
It simply requires specifying that it should only pick
up acknowledgments generated by the system for this
trading partner (as opposed to incoming 997s from the
partner). Figure 13 shows a fully configured set of filters.

Figure 12 Setting Pipeline and Delivery Information on a Port Problem Solved


The new EDI functionality in BizTalk Server 2010 is
concentrated primarily in the management of trading
partners. Hierarchical relationships and organizations
that were impossible with previous editions of the
platform can now be modeled with relative ease. In
addition to this, the map interface has been enhanced,
and the developer experience with mapping is much
improved. With the various improvements and increased
functionality, any EDI solution can be modeled and
developed successfully using BizTalk Server 2010. „

M ARK B ECKNER is the founder of Inotek Consulting Group


LLC. He works across the Microsoft stack, including BizTalk,
SharePoint, Dynamics CRM and general .NET development. He
Figure 13 Filtering on a Send Port can be reached at mbeckner@inotekgroup.com.
50 msdn magazine BizTalk EDI Solutions
SNMP
SMTP SFTP
FTP
2IP
POP HTTP

ENTERPRISE
TELNET WEB UDP
UI SSH
SSL EMULATION TCP

Internet Connectivity for the Enterprise


Since 1994, Dart has been a leading provider of high quality, high performance Internet connectivity components supporting a wide
range of protocols and platforms. Dart’s three product lines offer a comprehensive set of tools for the professional software developer.

PowerSNMP for ActiveX and .NET PowerTCP for ActiveX and .NET
Create custom Manager, Agent and Trap applications with a set Add high performance Internet connectivity to your ActiveX, .NET
of native ActiveX, .NET and Compact Framework components. and Compact Framework projects. Reduce integration costs with
SNMPv1, SNMPv2, SNMPv3 (authentication/encryption) and detailed documentation, hundreds of samples and an expert
ASN.1 standards supported. in-house support staff.

PowerWEB for ASP.NET SSH FTP SMTP DNS Telnet


UDP SFTP IMAP Rlogin VT Emulation
AJAX enhanced user interface controls for responsive ASP.NET
TCP HTTP S/MIME Rsh ZIP Compression
applications. Develop unique solutions by including streaming file
upload and interactive image pan/zoom functionality within a page. SSL POP Ping Rexec more...

Ask us about Mono Platform support. Contact sales@dart.com.


Download a fully functional product trial today!

Untitled-1 1 1/11/10 11:10 AM


S I LV E R L I G H T L O C A L I Z AT I O N

Tips and Tricks


for Loading Silverlight
Locale Resources
Matthew Delisle

Silverlight is a great framework for creating rich Internet I’ll present another localization solution that has some benefits over
applications (RIAs), but it doesn’t yet provide the robust support for the standard process. Finally, I’ll round off the solution with a discus-
localization that you enjoy in other components of the Microsoft sion of the back-end components needed to complete the solution.
.NET Framework. Silverlight does have .resx files, a simple Resource-
Manager class and an element in the project file. But after that you’re The Standard Localization Process
on your own. There are no custom markup extensions, and no I’ll start by building a Silverlight application that uses the localiza-
support for the DynamicResource class. tion process outlined by Microsoft. A detailed description of the
In this article I’ll show you how to remedy all of these issues. I’ll process is available at msdn.microsoft.com/library/cc838238(VS.95).
present a solution that will allow a developer to load resource sets The UI contains a TextBlock and an Image, as shown in Figure 1.
at run time, use any format for storing resources, change resources The localization process described by Microsoft uses .resx files
without recompiling and demonstrate lazy loading of resources. to store the resource data. The .resx files are embedded in the main
This article is divided into three parts. First, I’ll develop a simple assembly or a satellite assembly and loaded only once, at application
application using the localization process detailed by Microsoft. Next, startup. You can build applications targeted at certain languages by
modifying the SupportedCultures element in the project file. This
sample application will be localized for two languages, English
This article discusses:
and French. After adding the two resource files and two images
• Locale resource basics representing flags, the project structure looks like Figure 2.
• A smart resource manager I changed the build action for the images to content so I can ref-
• Using a WCF service erence the images using a less verbose syntax. I’ll add two entries to
• Loading the neutral locale each file. The TextBlock is referenced via a property called Welcome,
and the Image control is referenced via a property called FlagImage.
Technologies discussed:
When resource files are created in a Silverlight app, the default
Silverlight modifier for the generated resource class is internal. Unfortunately,
Code download available at: XAML can’t read internal members, even if they’re located in
code.msdn.microsoft.com/mag201103Localization
the same assembly. To remedy this situation, the generated class
modifiers need to be changed to public. This can be accomplished
52 msdn magazine
in the design view of the resource file. The Access The final mandatory step in this process is to edit the
Modifier dropdown menu lets you designate the scope web.config file and add a globalization element inside
of the generated class. of the system.web element, setting the values of its
Once resource files are ready, you need to bind the attributes to auto:
resources in XAML. To do this you create a wrapper <globalization culture="auto" uiCulture="auto"/>

class with a static field referencing an instance of the As mentioned earlier, a Silverlight application has a
resource class. The class is as simple as this: neutral language setting. The setting is reached by going
public class StringResources { to the Silverlight tab of the project properties and clicking
private static readonly strings strings = new strings(); Figure 1 The App
public strings Strings { get { return strings; } }
Assembly Information. The neutral language property is
} located at the bottom of the dialog, as shown in Figure 3.
To make the class accessible from XAML, you need to create an I recommend setting the neutral language to one without a
instance. In this case, I’ll create the instance in the App class so that region. This setting is a fallback, and it’s more useful if it covers a
it’s accessible throughout the project: wide range of potential locales. Setting a neutral language adds an
<Application.Resources> assembly attribute to the assemblyinfo.cs file, like this:
<local:StringResources x:Key="LocalizedStrings"/>
[assembly: NeutralResourcesLanguageAttribute("en")]
</Application.Resources>
Data-binding in XAML is now possible. The XAML for the After all that, what you end up with is a localized application
TextBlock and the Image looks like this: that reads the browser language setting at startup and loads the
<StackPanel Grid.ColumnSpan="2" Orientation="Horizontal" appropriate resources.
HorizontalAlignment="Center">
<TextBlock Text="{Binding Strings.Welcome, Source={StaticResource
LocalizedStrings}}" A Custom Localization Process
FontSize="24"/>
</StackPanel>
The limitations of the standard localization process stem from the
<Image Grid.Row="1" Grid.ColumnSpan="2" use of ResourceManager and .resx files. The ResourceManager class
HorizontalAlignment="Center"
Source="{Binding Strings.FlagImage, Source={StaticResource
doesn’t change resource sets at run time based on culture changes
LocalizedStrings}}"/> within the environment. Using .resx files locks the developer into one
The path is the String property followed by the key of the resource resource set per language and inflexibility in maintaining the resources.
entry. The source is the instance of the StringResources wrapper In response to these limitations, let’s look at an alternative solu-
from the App class. tion that employs dynamic resources.
To make resources dynamic, the resource manager needs to send
Setting the Culture notification when the active resource set changes. To send notifica-
There are three application settings that must be configured for the tions in Silverlight, you implement the INotifyPropertyChanged
application to pick up the browser’s culture setting and display the interface. Internally, each resource set will be a dictionary with a
appropriate culture. key and value type of string.
The first setting is the SupportedCultures element in the .csproj The Prism framework and the Managed Extensibility Framework
file. Currently there’s no dialog box in Visual Studio to edit the (MEF) are popular for Silverlight development, and these frame-
setting, so the project file must be edited works break up the application into mul-
manually. You can edit a project file either tiple .xap files. For localization, each .xap
by opening it outside of Visual Studio, or file needs its own instance of the resource
by unloading the project and selecting edit manager. To send notifications to every
from the context menu within Visual Studio. .xap file (every instance of the resource
To enable English and French for this manager), I need to keep track of each
application, the value of the Supported- instance that gets created and iterate through
Cultures element looks like this: that list when notifications need to be sent.
<SupportedCultures>fr</SupportedCultures> Figure 4 shows the code for this Smart-
The cultures values are separated by com- ResourceManager functionality.
mas. You don’t need to specify the neutral As you can see, a static list is created to
culture; it’s compiled into the main DLL. hold all instances of the resource manager.
These next steps are necessary to pick up The active resource set is stored in the field
the browser language setting. A parameter resourceSet and every resource that has been
must be added to the embedded Silverlight loaded is stored in the ResourceSets list. In
object in the Web page. The parameters the constructor, the current instance is stored
value is the current UI culture, taken from in the Instances list. The class implements
the server side. This requires the Web page INotifyPropertyChanged in the standard
to be an .aspx file. The parameter syntax is: way. When the active resource set is changed,
<param name="uiculture"
value="<%=Thread.CurrentThread.
Figure 2 Project Structure After I iterate through the list of instances and fire
CurrentCulture.Name %>" /> Adding .resx Files each one’s PropertyChanged event.
msdnmagazine.com March 2011 53
the culture has already been loaded, the method simply sets the
corresponding resource set as active. The code to load a resource
is omitted for the moment.
For completeness, I’ll also show you the two methods to load
a resource programmatically (see Figure 5 ). The first method
takes just a resource key and returns the resource from the active
culture. The second method takes a resource and a culture name
and returns the resource for that specific culture.
If you ran the application right now, all localized strings would
be empty, because no resource sets have been downloaded. To
load the initial resource set, I’m going to create a method named
Initialize that takes the neutral language file and culture identifier.
This method should be called only once during the application
lifetime (see Figure 6).

Binding to XAML
A custom markup extension would provide the most fluid binding
syntax for the localized resources. Unfortunately, custom markup
extensions aren’t available in Silverlight. Binding to a dictionary is
available in Silverlight 3 and later, and the syntax looks like this:
Figure 3 Setting the Neutral Language
<StackPanel Grid.ColumnSpan="2" Orientation="Horizontal"
HorizontalAlignment="Center">
The SmartResourceManager class needs a way to change the culture <TextBlock Text="{Binding Path=ResourceSet[Welcome],
at run time, and it’s as simple as a method that takes a CultureInfo object: Source={StaticResource
SmartRM}}" FontSize="24"/>
public void ChangeCulture(CultureInfo culture) {
</StackPanel>
if (!ResourceSets.ContainsKey(culture.Name)) {
<Image Grid.Row="1" Grid.ColumnSpan="2"
// Load the resource set
HorizontalAlignment="Center"
}
Source="{Binding ResourceSet[FlagImage], Source={StaticResource SmartRM}}"/>
else {
ResourceSet = ResourceSets[culture.Name]; The path contains the name of the dictionary property with the
Thread.CurrentThread.CurrentCulture =
Thread.CurrentThread.CurrentUICulture =
key in square brackets. If you’re using Silverlight 2, there are two
culture; options available: creating a ValueConverter class or emitting a
}
}
strongly typed object using reflection. Creating a strongly typed
This method checks to see if the requested culture has been object using reflection is beyond the scope of this article. The code
loaded yet. If not, it loads the culture and then sets it as active. If for a ValueConverter would look like Figure 7.
The LocalizeConverter class takes the dictionary and parameter
Figure 4 SmartResourceManager passed in and returns the value of that key in the dictionary. After
creating an instance of the converter, the binding syntax would
public class SmartResourceManager : INotifyPropertyChanged {
private static readonly List<SmartResourceManager> Instances = look like this:
new List<SmartResourceManager>(); <StackPanel Grid.ColumnSpan="2" Orientation="Horizontal"
private static Dictionary<string, string> resourceSet; HorizontalAlignment="Center">
private static readonly Dictionary<string, <TextBlock Text="{Binding Path=ResourceSet, Source={StaticResource
Dictionary<string, string>> ResourceSets = SmartRM}, Converter={StaticResource LocalizeConverter},
new Dictionary<string, Dictionary<string, string>>(); ConverterParameter=Welcome}" FontSize="24"/>
</StackPanel>
public Dictionary<string, string> ResourceSet { <Image Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Center"
get { return resourceSet; } Source="{Binding ResourceSet, Source={StaticResource SmartRM},
set { resourceSet = value; Converter={StaticResource LocalizeConverter}, ConverterParameter=FlagImage}"/>
// Notify all instances
foreach (var obj in Instances) { The syntax is more verbose using the converter, though you have
obj.NotifyPropertyChanged("ResourceSet"); more flexibility. However, for the rest of the article, I’ll continue
}
} without the converter, instead using the dictionary binding syntax.
}

public SmartResourceManager() { Locale Settings Redux


Instances.Add(this); There are two application settings that must be configured for the
}
culture to be picked up by the Silverlight application. These are the
public event PropertyChangedEventHandler PropertyChanged; same settings discussed with the standard localization process. The
public void NotifyPropertyChanged(string property) {
var evt = PropertyChanged; globalization element in the web.config file needs to have culture
and uiCulture values of auto:
if (evt != null) {
evt(this, new PropertyChangedEventArgs(property)); <globalization culture="auto" uiCulture="auto"></globalization>
} Also, the Silverlight object in the .aspx file needs to be passed in
}
the thread current UI culture value as a parameter:
54 msdn magazine SilverLight Localization
Just released. . .
Essential Studio Enterprise 2011 Vol. 1:
s0OWERFUL2ICH4EXT%DITORCONTROLFOR3ILVERLIGHT
s3UPPORTFOR!30.%4-6#ANDTHENEW2AZORVIEWENGINE
s3UPPORTFOR%XCELFORMULACOMPUTATIONWITHOUTUSING%XCEL

1.888.9DOTNET | www.syncfusion.com | @syncfusion

Untitled-10 1 2/2/11 3:27 PM


Figure 5 Loading Resources The reason I chose a WCF service is for the ease of creation and
public string GetString(string key) {
robustness offered by WCF. The reason I chose to store the resources
if (string.IsNullOrEmpty(key)) return string.Empty; in a relational database is for ease of maintenance and administration.
if (resourceSet.ContainsKey(key)) {
An administration application could be created that would allow
return resourceSet[key]; translators to easily modify the resources.
}
else {
I’m using SQL Server 2008 Express for this application. The data
return string.Empty; schema is shown in Figure 9.
}
}
A Tag is a named group of resources. A StringResource is the entity
representing a resource. The LocaleId column represents the name
public string GetString(string key, string culture) {
if (ResourceSets.ContainsKey(culture)) {
of the culture that the resource is under. The Comment column is
if (ResourceSets[culture].ContainsKey(key)) { added for compatibility with the .resx format. The CreatedDate and
return ResourceSets[culture][key];
}
ModifiedDate columns are added for auditing purposes.
else { A StringResource can be associated with multiple Tags. The
return string.Empty;
}
advantage of this is that you can create specific groups (for
} example, the resources for a single screen) and download only
else {
return string.Empty;
those resources. The disadvantage is that you can assign multiple
} resources with the same LocaleId, Key and Tag. In that case, you
}
may want to write a trigger to manage creating or updating resources
or use the ModifiedDate column when retrieving resource sets to
<param name="uiculture"
determine which is the latest resource.
value="<%=Thread.CurrentThread.CurrentCulture.Name %>" /> I’m going to retrieve the data using LINQ to SQL. The first ser-
To showcase the dynamic localization of the application, I’m going vice operation will take in a culture name and return all resources
to add a couple buttons that facilitate changing culture, as shown in associated with that culture. Here’s the interface:
Figure 8. The click event for the English button looks like this: [ServiceContract]
public interface ILocaleService {
(App.Current.Resources["SmartRM"] as SmartResourceManager).ChangeCulture(
[OperationContract]
new CultureInfo("en"));
Dictionary<string, string> GetResources(string cultureName);
With some mocked-up data, the application would run and }
display the appropriate language. The solution here allows for Here’s the implementation:
dynamic localization at run time and is extensible enough to load public class LocaleService : ILocaleService {
private acmeDataContext dataContext = new acmeDataContext();
the resources using custom logic.
The next section focuses on filling in the remaining gap: where public Dictionary<string, string> GetResources(string cultureName) {
return (from r in dataContext.StringResources
the resources are stored and how they’re retrieved. where r.LocaleId == cultureName
select r).ToDictionary(x => x.Key, x => x.Value);

Server-Side Components }
}

Now let’s create a database to store resources and a Windows The operation simply finds all resources whose LocaleId is equal
Communication Foundation (WCF) service to retrieve those to the cultureName parameter. The dataContext field is an instance
resources. In larger applications, you’d want to create data and busi- of the LINQ to SQL class hooked up to the SQL Server database.
ness layers, but for this example, I won’t be using any abstractions. That’s it! LINQ and WCF make things so simple.
Now, it’s time to link the WCF service to the SmartResource-
Figure 6 Initializing the Neutral Language Manager class. After adding a service reference to the Silverlight
public SmartResourceManager() { application, I register to receive the completed event for the
if (Instances.Count == 0) { GetResources operation in the constructor:
ChangeCulture(Thread.CurrentThread.CurrentUICulture);
}
Instances.Add(this); Figure 7 Custom ValueConverter
}
public class LocalizeConverter : IValueConverter {
public void Initialize(string neutralLanguageFile, public object Convert(object value,
string neutralLanguage) { Type targetType, object parameter,
lock (lockObject) { System.Globalization.CultureInfo culture) {
if (isInitialized) return; if (value == null) return string.Empty;
isInitialized = true;
} Dictionary<string, string> resources =
value as Dictionary<string, string>;
if (string.IsNullOrWhiteSpace(neutralLanguageFile)) { if (resources == null) return string.Empty;
// No neutral resources string param = parameter.ToString();
ChangeCulture(Thread.CurrentThread.CurrentUICulture);
} if (!resources.ContainsKey(param)) return string.Empty;
else {
LoadNeutralResources(neutralLanguageFile, neutralLanguage); return resources[param];
} }
} }

56 msdn magazine SilverLight Localization


Untitled-1 1 1/13/11 9:48 AM
public SmartResourceManager() { WebClient to retrieve the resource file from the server.
Instances.Add(this);
localeClient.GetResourcesCompleted +=
It then parses the file and converts the XML string into
localeClient_GetResourcesCompleted; a Dictionary object. I won’t show the code here as it’s
if (Instances.Count == 0) {
ChangeCulture(Thread.CurrentThread.CurrentUICulture);
a bit long and somewhat trivial, but you can find it in
} the code download for this article if you’re interested.
}
To call the parameterized SmartResourceManager
The callback method should add the resource set to constructor, I need to move the instantiation of the
the list of resource sets and make the resource set the SmartResourceManager into the code-behind of the App
active set. The code is shown in Figure 10. Figure 8 Culture- class (because Silverlight doesn’t support XAML 2009).
The ChangeCulture method needs to be modified to Change Buttons I don’t want to hardcode the resource file or the culture
make a call to the WCF operation: code, though, so I’ll have to create a custom Configuration-
public void ChangeCulture(CultureInfo culture) {
if (!ResourceSets.ContainsKey(culture.Name)) { Manager class, which you can check out in the code download.
localeClient.GetResourceSetsAsync(culture.Name, culture); After integrating the ConfigurationManager into the App class,
}
else { the Startup event callback method looks like this:
ResourceSet = ResourceSets[culture.Name]; private void Application_Startup(object sender, StartupEventArgs e) {
Thread.CurrentThread.CurrentCulture = ConfigurationManager.Error += ConfigurationManager_Error;
Thread.CurrentThread.CurrentUICulture = culture; ConfigurationManager.Loaded += ConfigurationManager_Loaded;
} ConfigurationManager.LoadSettings();
} }
The startup callback method now serves to load the application
Loading the Neutral Locale settings and register for callbacks. If you do choose to make the
This application needs a way to recover if the Web services can’t be loading of the configuration settings a background call, be careful
contacted or are timing out. A resource file containing the neutral of the race conditions that you can run into. Here are the callback
language resources should be stored outside of the Web services and methods for the ConfigurationManager events:
loaded at startup. This will serve as a fallback and a performance private void ConfigurationManager_Error(object sender, EventArgs e) {
Resources.Add("SmartRM", new SmartResourceManager());
enhancement over the service call. this.RootVisual = new MainPage();
I’m going to create another SmartResourceManager constructor }
with two parameters: a URL pointing to the neutral language private void ConfigurationManager_Loaded(object sender, EventArgs e) {
resources file and a culture code identifying the culture of the Resources.Add("SmartRM", new SmartResourceManager(
ConfigurationManager.GetSetting("neutralLanguageFile"),
resource file (see Figure 11). ConfigurationManager.GetSetting("neutralLanguage")));
If there’s no neutral resource file, the normal process of calling the this.RootVisual = new MainPage();
}
WCF call is performed. The LoadNeutralResources method uses a
The Error event callback method loads SmartResourceManager
without a neutral language, and the Loaded event callback method
loads with a neutral language.
I need to put the resource file in a location where I don’t have
to recompile anything if I change it. I’m going to put it in the
ClientBin directory of the Web project, and after creating the
resource file, I’m going to change its extension to .xml so that it’s
publicly accessible and the WebClient class can access it from the
Figure 10 Adding Resources
private void localeClient_GetResourcesCompleted(object sender,
LocaleService.GetResourcesCompletedEventArgs e) {
if (e.Error != null) {
var evt = CultureChangeError;

if (evt != null)
evt(this, new CultureChangeErrorEventArgs(
e.UserState as CultureInfo, e.Error));
}
else {
if (e.Result == null) return;

CultureInfo culture = e.UserState as CultureInfo;

if (culture == null) return;

ResourceSets.Add(culture.Name, e.Result);
ResourceSet = e.Result;
Thread.CurrentThread.CurrentCulture =
Thread.CurrentThread.CurrentUICulture = culture;
}
}
Figure 9 Schema for Localization Tables in SQL Server 2008 Express
58 msdn magazine SilverLight Localization
Figure 11 Loading the Neutral Locale
public SmartResourceManager(string neutralLanguageFile, string neutralLanguage) {
Instances.Add(this);
localeClient.GetResourcesCompleted +=
localeClient_GetResourcesCompleted;

if (Instances.Count == 1) {
if (string.IsNullOrWhiteSpace(neutralLanguageFile)) {
// No neutral resources
ChangeCulture(Thread.CurrentThread.CurrentUICulture);
}
else {
LoadNeutralResources(neutralLanguageFile, neutralLanguage);
}
}
}

Silverlight application. Because it’s publicly accessible, don’t put


any sensitive data in the file.
ConfigurationManager also reads from the ClientBin directory.
It looks for a file called appSettings.xml, and the file looks like this:
<AppSettings>
<Add Key="neutralLanguageFile" Value="strings.xml"/>
<Add Key="neutralLanguage" Value="en-US"/>
</AppSettings>
Once appSettings.xml and strings.xml are in place, Configuration-
Manager and SmartResourceManager can work together to load
the neutral language. There’s room for improvement in this process,
because if the thread’s active culture is different than the neutral
language and the Web service is down, the thread’s active culture
will be different than the active resource set. I’ll leave that as an
exercise for you.

Wrapping Up
What I didn’t go over in this article was normalizing the resources on
the server side. Let’s say that the fr-FR resource is missing two keys that
the fr resource has. When requesting the fr-FR resources, the Web ser-
vice should insert the missing keys from the more general fr resource.
Another aspect that’s built into this solution that I didn’t cover is
loading resources by culture and resource set instead of just culture.
This is useful for loading resources per screen or per .xap file.
The solution I walked you through here does allow you to do a
few useful things, however, including loading resource sets at run
time, using any format for storing resources, changing resources
without recompiling and lazy loading resources.
The solution presented here is general-purpose, and you can
hook into it in multiple points and drastically change the imple-
mentation. I hope this helps reduce your daily programming load.
For further in-depth reading about internationalization, check
out the book “.NET Internationalization: The Developer’s Guide
to Building Global Windows and Web Applications” (Addison-
Wesley, 2006), by Guy Smith-Ferrier. Smith-Ferrier also has a great
video on internationalization on his Web site; the video is called
“Internationalizing Silverlight at SLUGUK” (bit.ly/gJGptU). „

MATTHEW DELISLE enjoys studying both the software and hardware aspects of
computers. His first daughter was born in 2010 and he thinks she’s almost ready to
begin her programming career. Keep up with Delisle via his blog at mattdelisle.net.

THANKS to the following technical expert for reviewing this article:


John Brodeur
msdnmagazine.com
DEBUGGER APIs

Writing a Debugging
Tools for Windows
Extension
Andrew Richards

Troubleshooting production issues can be one of the contain a snapshot of a process memory at the time of capture.
most frustrating jobs that any engineer can do. It can also be one Depending on the dumping tool, this could be the entire address
of the most rewarding jobs. Working in Microsoft Support, I’m space or just a subset.
faced with this every day. Why did the application crash? Why did Windows automatically creates a minidump through Windows
it hang? Why is it having a performance issue? Error Reporting (WER) when any application throws an unhan-
Learning how to debug can be a daunting task, and is one of dled exception. In addition, you can manually create a dump
those skills that requires many hours of regular practice to stay file via the Userdump.exe tool. The Sysinternals tool ProcDump
proficient. But it’s a crucial skill for being an all-around developer. (technet.microsoft.com/sysinternals/dd996900) is becoming the preferred
Still, by bottling the skills of a few debugging experts, debugger process-dumping tool of Microsoft Support because it can capture
engineers of all skill levels can execute extremely complex debug- a dump based upon a large variety of triggers and can generate
ging logic as easy-to-run commands. dumps of various sizes. But once you have the dump data, what
Myriad troubleshooting techniques can be used to get to the can you do with it to aid debugging?
root cause of a crash, but the most valuable—and most fruitful to Various versions of Visual Studio support opening dump files
engineers with debugging skills—is a process dump. Process dumps (.dmp), but the best tool to use is a debugger from Debugging Tools
for Windows. These tools are all based on a single debugging engine
that supports two debugger extension APIs. In this article, I’m going
This article discusses:
to cover the basics of building a custom debugger extension so
• Debugging tools basics you can analyze these dump files (and also live systems) with ease.
• Using the debugger APIs
• Symbol resolution Setting up the Tools
• Dealing with processor types Debugging Tools for Windows (microsoft.com/whdc/devtools/debugging)
is an installable and redistributable component of the Windows
Technologies discussed:
SDK and Windows Driver Kit (WDK). As I write this, the current
Debugging Tools for Windows, C++ version is 6.12, and it’s available in version 7.1 of the Windows
Code download available at: SDK or the WDK. I recommend using the most-recent version,
code.msdn.microsoft.com/mag201103Debugger
as the debugging engine has many valuable additions, including
better stack walking.
60 msdn magazine
Figure 1 Sources Similarly, they use the wrong hardcoded register (esp in place of
TARGETNAME=MyExt
rsp, for example) instead of a pseudo-register such as $csp.
TARGETTYPE=DYNLINK If you’re having an issue with a debugger extension, you should
_NT_TARGET_VERSION=$(_NT_TARGET_VERSION_WINXP)
try running the debugger designed for the same architecture as the
target environment. This might overcome the assumptions of the
DLLENTRY=_DllMainCRTStartup
poorly written extension.
!if "$(DBGSDK_INC_PATH)" != "" Each application build type and associated processor architec-
INCLUDES = $(DBGSDK_INC_PATH);$(INCLUDES)
!endif
ture comes with its own set of debugging headaches. The assembler
!if "$(DBGSDK_LIB_PATH)" == "" generated for a debug build is relatively linear, but the assembler
DBGSDK_LIB_PATH = $(SDK_LIB_PATH)
!else
generated for a release build is optimized and can resemble a bowl
DBGSDK_LIB_PATH = $(DBGSDK_LIB_PATH)\$(TARGET_DIRECTORY) of spaghetti. On x86 architectures, Frame Pointer Omission (FPO)
!endif
plays havoc with call stack reconstruction (the latest debugger
TARGETLIBS=$(SDK_LIB_PATH)\kernel32.lib \ handles this well). On x64 architectures, function parameters and
$(DBGSDK_LIB_PATH)\dbgeng.lib
local variables are stored in registers. At the time of dump capture,
USE_MSVCRT=1 they may have been pushed onto the stack, or may no longer exist
UMTYPE=windows
due to register reuse.
Experience is the key here. To be accurate, one person’s experi-
MSC_WARNING_LEVEL = /W4 /WX
ence is the key here. You just need to bottle their know-how in a
SOURCES= dbgexts.rc \ debugger extension for the rest of us. It only takes a few repetitions
dbgexts.cpp \
myext.cpp
of a similar debugging sequence before I automate it as a debugger
extension. I’ve used some of my extensions so much that I forget how
I did the same thing using the underlying debugging commands.
The Debugging Tools for Windows guidelines say that you
should compile debugger extensions using the WDK build Using the Debugger APIs
environment. I use the latest release of the WDK (version 7.1.0 There are two debugger extension APIs: the deprecated WdbgExts
build 7600.16385.1), but any version of the WDK or its previous API (wdbgexts.h) and the current DbgEng API (dbgeng.h).
incarnation as the Driver Development Kit (DDK) will suffice. WdbgExts extensions are based on a global call that’s configured
When building an extension using the WDK, you use x64 and x86 at initialization (WinDbgExtensionDllInit):
Free Build environments. WINDBG_EXTENSION_APIS ExtensionApis;
With a little bit of effort you can also adapt my projects to build
Figure 2 dbgexts.cpp
in the Windows SDK build environment or Visual Studio.
One note of warning: The WDK doesn’t like spaces in path names. // dbgexts.cpp

Make sure you compile from an unbroken path. For example, use #include "dbgexts.h"
something like C:\Projects instead of C:\Users\Andrew Richards\
extern "C" HRESULT CALLBACK
Documents\Projects. DebugExtensionInitialize(PULONG Version, PULONG Flags) {
Regardless of how you build the extension, you’ll need the header *Version = DEBUG_EXTENSION_VERSION(EXT_MAJOR_VER, EXT_MINOR_VER);
*Flags = 0; // Reserved for future use.
and library files of the Debugging Tools SDK, which is a component return S_OK;
of Debugging Tools for Windows. The examples in this article use }

my x86 path (C:\debuggers_x86\sdk) when referencing the header extern "C" void CALLBACK
and library files. If you choose to install the debugger elsewhere, DebugExtensionNotify(ULONG Notify, ULONG64 Argument) {
UNREFERENCED_PARAMETER(Argument);
remember to update the path and add quotes when necessary to switch (Notify) {
accommodate spaces in the path names. // A debugging session is active. The session may not necessarily be suspended.
case DEBUG_NOTIFY_SESSION_ACTIVE:
break;
Using the Debugging Tools // No debugging session is active.
case DEBUG_NOTIFY_SESSION_INACTIVE:
The Debugging Tools for Windows debuggers are architecture- break;
agnostic. Any edition of the debugger can debug any target // The debugging session has suspended and is now accessible.
case DEBUG_NOTIFY_SESSION_ACCESSIBLE:
architecture. A common example is using the x64 debugger to break;
debug an x86 application. The debugger is released for x86, x64 // The debugging session has started running and is now inaccessible.
case DEBUG_NOTIFY_SESSION_INACCESSIBLE:
(amd64) and IA64, but it can debug x86, x64, IA64, ARM, EBC and break;
PowerPC (Xbox) applications. You can install all of the debugger }
return;
editions side-by-side. }
This agility isn’t universally understood, though. Not all debugger
extern "C" void CALLBACK
extensions adapt to the target architecture as well as the debugger DebugExtensionUninitialize(void) {
engine does. Some debugger extensions assume that the pointer return;
}
size of the target will be the same as the pointer size of the debugger.
msdnmagazine.com March 2011 61
The global provides the functionality required to run functions I’ve added the switch statement for the Notify parameter for
such as dprintf(“\n”) and GetExpression(“@$csp”) without any completeness, but I haven’t added any functional code in this area.
namespace. This type of extension resembles the code you’d write The switch statement processes four session state changes:
when doing Win32 programming. • DEBUG_NOTIFY_SESSION_ACTIVE occurs when you
DbgEng extensions are based on debugger interfaces. The attach to a target.
IDebugClient interface is passed to you by the debug engine as • DEBUG_NOTIFY_SESSION_INACTIVE occurs when
a parameter of each call. The interfaces support QueryInterface the target becomes detached (via .detach or qd).
for access to the ever-increasing range of debugger interfaces. • If the target suspends (hits a breakpoint, for example), the
This type of extension resembles the code you’d write when doing function will be passed DEBUG_NOTIFY_SESSION_
COM programming. ACCESSIBLE.
It’s possible to make a hybrid of the two extension types. You expose • If the target resumes running, the function will be passed
the extension as DbgEng, but add the functionality of the WdbgExts DEBUG_NOTIFY_SESSION_INACCESSIBLE.
API at run time via a call to IDebugControl::GetWindbgExtension- The DebugExtensionUninitialize function is called when the
Apis64. As an example, I’ve written the classic “Hello World” as a extension is unloaded.
DbgEng extension in C. If you prefer C++, refer to the ExtException Each extension command to be exposed is declared as a function
class in the Debugging Tools SDK (.\inc\engextcpp.cpp). of type PDEBUG_EXTENSION_CALL. The name of the function
Compile the extension as MyExt.dll (TARGETNAME in the is the name of the extension command. Because I’m writing “Hello
sources file shown in Figure 1 ). It exposes a command called World,” I’ve named the function helloworld (see Figure 3).
!helloworld. The extension dynamically links to the Microsoft Note that the convention is to use lower-case function names.
Visual C runtime (MSVCRT). If you want to use static, change the Because I’m using the WDK build environment, the myext.def file
USE_MSVCRT=1 statement to USE_LIBCMT=1 in the sources file. also needs to be changed. The name of the extension command
The DebugExtensionInitialize function (see Figure 2) is called needs to be added so that it’s exported:
when the extension is loaded. Setting the Version parameter is a ;-------------
; MyExt.def
simple matter of using the DEBUG_EXTENSION_VERSION ;-------------
macro with the EXT_MAJOR_VER and EXT_MINOR_VER EXPORTS
helloworld
#defines I’ve added to the header file: DebugExtensionNotify
// dbgexts.h DebugExtensionInitialize
DebugExtensionUninitialize
#include <windows.h>
#include <dbgeng.h>
The args parameter contains a string of the arguments for the
command. The parameter is passed as a null-terminated ANSI
#define EXT_MAJOR_VER 1
#define EXT_MINOR_VER 0
string (CP_ACP).
The Version value is reported as the API version in the debugger The pDebugClient parameter is the IDebugClient interface
.chain command. To change the File Version, File Description, pointer that allows the extension to interact with the debugging
Copyright and other values you need to edit the dbgexts.rc file: engine. Even though the interface pointer looks like it’s a COM
myext.dll: image 6.1.7600.16385, API 1.0.0, built Wed Oct 13 20:25:10 2010 Interface pointer, it can’t be marshaled, nor accessed at a later
[path: C:\Debuggers_x86\myext.dll] time. It also can’t be used from any other thread. To do work
The Flags parameter is reserved and should be set to zero. The on an alternate thread, a new debugger client (a new interface
function needs to return S_OK. pointer to IDebugClient) must be created on that thread using
The DebugExtensionNotify function is called when the session IDebugClient::CreateClient. This is the only function that can be
changes its active or accessible status. The Argument parameter is run on an alternate thread.
wrapped with the UNREFERENCED_PARAMETER macro to The IDebugClient interface (like all interfaces) is derived from
eliminate the unused parameter compiler warning. IUnknown. You use QueryInterface to access the other DbgEng
interfaces, be they later versions of the IDebugClient interface
Figure 3 MyExt.cpp (IDebugClient4) or different interfaces (IDebugControl, IDebug-
// MyExt.cpp Registers, IDebugSymbols, IDebugSystemObjects and so on). To
output text to the debugger, you need the IDebugControl interface.
#include "dbgexts.h"
I have two non-SDK files in the folder to help with develop-
HRESULT CALLBACK ment. The make.cmd script adds the Debugger SDK inc and lib
helloworld(PDEBUG_CLIENT pDebugClient, PCSTR args) {
UNREFERENCED_PARAMETER(args); paths to the WDK build environment, then runs the appropriate
build command:
IDebugControl* pDebugControl;
if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), @echo off
(void **)&pDebugControl))) { set DBGSDK_INC_PATH=C:\Debuggers_x86\sdk\inc
pDebugControl->Output(DEBUG_OUTPUT_NORMAL, "Hello World!\n"); set DBGSDK_LIB_PATH=C:\Debuggers_x86\sdk\lib
pDebugControl->Release(); set DBGLIB_LIB_PATH=C:\Debuggers_x86\sdk\lib
} build -cZMg %1 %2
return S_OK; Note that the WDK build environment itself determines whether
}
an x86 or x64 binary will be built. If you want to build for multiple
62 msdn magazine Debugger APIs
/update/2011/03
www.componentsource.com

BEST
BEST SELLER
SELLER Spread for Windows Forms from $959.04
A comprehensive Excel compatible spreadsheet component for Windows Forms applications.
t4QFFEEFWFMPQNFOUXJUI4QSFBETIFFU%FTJHOFST 2VJDLTUBSU8J[BSEBOE$IBSU%FTJHOFST
t"VUPNBUJDDPNQMFUJPOQSPWJEFiUZQFBIFBEwGSPNVTFSJOQVUUPDFMM
t'FBUVSFTCVJMUJOUPPMUPEFTJHODIBSUTXJUIEJòFSFOUTUZMFT
t1SFTFSWFT&YDFMöMFTBOESFTUPSFTVOTVQQPSUFEGVODUJPOBMJUZ
t*ODMVEFTTMFFLOFXQSFEFöOFETLJOTBOEUIFBCJMJUZUPDSFBUFDVTUPNTLJOT

BEST
BEST SELLER
SELLER ActiveReports 6 from $685.02
Latest release of the best selling royalty free .NET report writer.
t/PXTVQQPSUT8JOEPXT4FSWFS #JU*&
t'JSTU'MBTICBTFE3FQPSU7JFXFSGPSFOEVTFST
t4VQQPSUT1%'%JHJUBM4JHOBUVSFT 344#BS$PEFTBOEFYUFSOBMTUZMFTIFFUT
t'FBUVSFTBOFBTZUPVTF7JTVBM4UVEJPSFQPSUEFTJHOFSBOEBQPXFSGVM"1*
t0òFSTTFBNMFTTSVOUJNFEFQMPZNFOU SPZBMUZGSFF

BESTSELLER
BEST SELLER TX Text Control .NET for Windows Forms/WPF from $499.59
Word processing components for Visual Studio .NET.
t"EEQSPGFTTJPOBMXPSEQSPDFTTJOHUPZPVSBQQMJDBUJPOT
t3PZBMUZGSFF8JOEPXT'PSNTBOE81'SJDIUFYUCPY
t5SVF8:4*8:( OFTUFEUBCMFT UFYUGSBNFT IFBEFSTBOEGPPUFST JNBHFT CVMMFUT 
TUSVDUVSFEOVNCFSFEMJTUT [PPN EJBMPHCPYFT TFDUJPOCSFBLT QBHFDPMVNOT
t-PBE TBWFBOEFEJU%0$9 %0$ 1%' 1%'" 35' )5.- 595BOE9.-

BEST SELLER FusionCharts from $195.02


Interactive Flash & JavaScript (HTML5) charts for web apps.
t-JWFOVQZPVSXFCBQQMJDBUJPOTVTJOHBOJNBUFEEBUBESJWFODIBSUT
t$SFBUF"+"9FOBCMFEDIBSUTXJUIESJMMEPXODBQBCJMJUJFTJONJOVUFT
t&YQPSUDIBSUTBTJNBHFT1%'BOEEBUBBT$47GSPNDIBSUTJUTFMG
t$SFBUFHBVHFT EBTICPBSET öOBODJBMDIBSUTBOEPWFSNBQT
t5SVTUFECZPWFS DVTUPNFSTBOE VTFSTJODPVOUSJFT

We accept purchase orders.


© 1996-2011 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-8 1 1/27/11 4:31 PM


architectures, you’ll need to open multiple prompts and run make. I’ve modified the test script to load a dump file of the x86
cmd in each. The building can be done concurrently. version of test01 instead of launching Notepad:
Once built, I use the (x86) test.cmd script to copy the compiled @echo off
copy objfre_win7_x86\i386\myext.dll c:\Debuggers_x86
i386 binaries into the x86 debugger folder (c:\Debuggers_x86), copy objfre_win7_x86\i386\myext.pdb c:\Debuggers_x86
then launch an instance of Notepad with the debugger attached \Debuggers_x86\windbg.exe -a myext.dll -y "..\Test01\x86;SRV*c:\symbols*http://
msdl.microsoft.com/download/symbols" -z ..\Test01\x86\Test01.dmp
and the extension loaded:
@echo off
I’ve also set the symbol path to the test01 x86 folder and the
copy objfre_win7_x86\i386\myext.dll c:\Debuggers_x86 Microsoft Public Symbol Server so that everything can be resolved.
copy objfre_win7_x86\i386\myext.pdb c:\Debuggers_x86
\Debuggers_x86\windbg.exe -a myext.dll -x notepad
Additionally, I’ve made an x64 test script that does the same as
If everything has gone as planned, I can type “!helloworld” in the the x86 test script, but with a dump file of the x64 version of the
debugger command prompt and see a “Hello World!” response: test application:
@echo off
0:000> !helloworld
copy objfre_win7_x86\i386\myext.dll c:\Debuggers_x86
Hello World!
copy objfre_win7_x86\i386\myext.pdb c:\Debuggers_x86
\Debuggers_x64\windbg.exe -a myext.dll -y "..\Test01\x64;SRV*c:\symbols*http://
Symbol Resolution and Reading msdl.microsoft.com/download/symbols" -z ..\Test01\x64\Test01.dmp

The “Hello World” application may be amazing, but you can do When I run the scripts, the x86 debugger is launched, the
better. I’m now going to use this infrastructure to add a command appropriate dump file is opened, the x86 version of the extension
that actually interacts with the target and would help you do some is loaded and symbols are resolvable.
analysis. The simple test01 application has a global pointer that’s Once again, if everything has gone to plan, I can type “x test01!g_ptr”
assigned a value: and !gptr in the debugger command prompt and see similar responses:
// test01.cpp // x86 Target
0:000> x test01!g_ptr
#include <windows.h> 012f3370 Test01!g_ptr = 0x012f20e4

void* g_ptr; 0:000> !gptr


int main(int argc, char* argv[]) { 012f3370 test01!g_ptr = 0x012f20e4
g_ptr = "This is a global string"; This is a global string
Sleep(10000);
return 0; // x64 Target
} 0:000> x test01!g_ptr
00000001`3fda35d0 Test01!g_ptr = 0x00000001`3fda21a0
The new !gptr command in MyExt.cpp (see Figure 4 ) will
resolve the test01!g_ptr global, read the pointer and then output 0:000> !gptr
000000013fda35d0 test01!g_ptr = 0x000000013fda21a0
the values found in the same format as “x test01!g_ptr”: This is a global string
0:000> x test01!g_ptr
012f3370 Test01!g_ptr = 0x012f20e4
Figure 4 Revised MyExt.cpp
0:000> !gptr
012f3370 test01!g_ptr = 0x012f20e4 HRESULT CALLBACK
<string> gptr(PDEBUG_CLIENT pDebugClient, PCSTR args) {
The first step is to determine the location of the test01!g_ptr UNREFERENCED_PARAMETER(args);

pointer. The pointer will be in a different location each time the IDebugSymbols* pDebugSymbols;
application runs because Address Space Layout Randomization if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugSymbols),
(void **)&pDebugSymbols))) {
(ASLR) will change the module load address. To get the location, // Resolve the symbol.
I use QueryInterface to get the IDebugSymbols interface, and then ULONG64 ulAddress = 0;
if (SUCCEEDED(pDebugSymbols->GetOffsetByName("test01!g_ptr", &ulAddress))) {
use GetOffsetByName. The GetOffsetByName function takes a IDebugDataSpaces* pDebugDataSpaces;
symbol name and returns the address as a 64-bit pointer. The if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugDataSpaces),
(void **)&pDebugDataSpaces))) {
debugger functions always return 64-bit pointers (ULONG64) so // Read the value of the pointer from the target address space.
that 64-bit targets can be debugged with a 32-bit debugger. ULONG64 ulPtr = 0;
if (SUCCEEDED(pDebugDataSpaces->ReadPointersVirtual(1, ulAddress,
Remember, this is the address of the pointer in the target address &ulPtr))) {
space, not your own. You can’t just read from it to determine its value. PDEBUG_CONTROL pDebugControl;
if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl),
To get the value of the pointer, I use QueryInterface again to get the (void **)&pDebugControl))) {
IDebugDataSpaces interface, and then use ReadPointersVirtual. This // Output the values.
pDebugControl->Output(DEBUG_OUTPUT_NORMAL,
reads the pointer from the target address space. ReadPointersVirtual "%p test01!g_ptr = 0x%p\n", ulAddress, ulPtr);
automatically adjusts for pointer size and endian differences. You don’t pDebugControl->Output(DEBUG_OUTPUT_NORMAL, "%ma\n", ulPtr);
pDebugControl->Release();
need to manipulate the pointer returned. }
IDebugControl::Output takes the same format string as printf, }
pDebugDataSpaces->Release();
but also has formatters that allow you to reference the target address }
space. I use the %ma format to print out the ANSI string that the pDebugSymbols->Release();
}
global pointer points to in the target address space. The %p format }
is pointer-size-aware and should be used for pointer output (you return S_OK;
}
must pass a ULONG64).
64 msdn magazine Debugger APIs
WINDOWS FORMS / WPF / ASP.NET / ACTIVEX

WORD PROCESSING
COMPONENTS
MILES BEYOND RICH TEXT

G
TRUE WYSIWY
F U L M A IL M ERGE
POWER
F IC E N O T R E QUIRED MEET US AT
MS OF HTML
, D O C , R T F &
PDF, DOCX
C O N N E C T I O N S
MARCH 27-30, 2011, ORLANDO

TX Text Control Sales:


Word Processing Components US +1 877 - 462 - 4772 (toll-free)
for Windows Forms & ASP.NET WWW.TEXTCONTROL.COM EU +49 421 - 4270671 - 0

Untitled-5 1 2/2/11 1:56 PM


Figure 5 Sleepy
HRESULT CALLBACK pDebugStackFrame[n].StackOffset, &dwMilliseconds,
sleepy(PDEBUG_CLIENT4 Client, PCSTR args) { sizeof(dwMilliseconds), NULL))) {
UNREFERENCED_PARAMETER(args); pDebugControl->Output(DEBUG_OUTPUT_NORMAL,
BOOL bFound = FALSE; "Sleeping for %ld msec\n", dwMilliseconds);
bFound = TRUE;
IDebugControl* pDebugControl; }
pDebugDataSpaces->Release();
if (SUCCEEDED(Client->QueryInterface(__uuidof(IDebugControl), }
(void **)&pDebugControl))) { if (bFound) break;
IDebugSymbols* pDebugSymbols; }

if (SUCCEEDED(Client->QueryInterface(__uuidof(IDebugSymbols), else if ((ProcessorType == IMAGE_FILE_MACHINE_AMD64) &&


(void **)&pDebugSymbols))) { (_stricmp(SymName, "KERNELBASE!SleepEx") == 0) &&
DEBUG_STACK_FRAME* pDebugStackFrame = (Displacement == 0xAB)) {
(DEBUG_STACK_FRAME*)malloc( // Win7 x64; KERNELBASE!SleepEx+0xAB is usually in frame 1.
sizeof(DEBUG_STACK_FRAME) * MAX_STACK_FRAMES); IDebugRegisters* pDebugRegisters;

if (pDebugStackFrame != NULL) { if (SUCCEEDED(Client->QueryInterface(


// Get the Stack Frames. __uuidof(IDebugRegisters),
memset(pDebugStackFrame, 0, (sizeof(DEBUG_STACK_FRAME) * (void **)&pDebugRegisters))) {
MAX_STACK_FRAMES)); // The value is in the 'rsi' register.
ULONG Frames = 0; ULONG rsiIndex = 0;
if (SUCCEEDED(pDebugRegisters->GetIndexByName(
if (SUCCEEDED(pDebugControl->GetStackTrace(0, 0, 0, "rsi", &rsiIndex)))
pDebugStackFrame, MAX_STACK_FRAMES, &Frames)) && {
(Frames > 0)) { DEBUG_VALUE debugValue;
ULONG ProcessorType = 0; if (SUCCEEDED(pDebugRegisters->GetValue(
ULONG SymSize = 0; rsiIndex, &debugValue)) &&
char SymName[4096]; (debugValue.Type == DEBUG_VALUE_INT64)) {
memset(SymName, 0, 4096); // Truncate to 32bits for display.
ULONG64 Displacement = 0; pDebugControl->Output(DEBUG_OUTPUT_NORMAL,
"Sleeping for %ld msec\n", debugValue.I32);
if (SUCCEEDED(pDebugControl->GetEffectiveProcessorType( bFound = TRUE;
&ProcessorType))) { }
for (ULONG n=0; n<Frames; n++) { }
pDebugRegisters->Release();
// Use the Effective Processor Type and the contents }
// of the frame to determine existence
if (SUCCEEDED(pDebugSymbols->GetNameByOffset( if (bFound) break;
pDebugStackFrame[n].InstructionOffset, SymName, 4096, }
&SymSize, &Displacement)) && (SymSize > 0)) { }
}
if ((ProcessorType == IMAGE_FILE_MACHINE_I386) && }
(_stricmp(SymName, "KERNELBASE!Sleep") == 0) && }
(Displacement == 0xF)) { free(pDebugStackFrame);
// Win7 x86; KERNELBASE!Sleep+0xF is usually in frame 3. }
IDebugDataSpaces* pDebugDataSpaces; pDebugSymbols->Release();
}
if (SUCCEEDED(Client->QueryInterface( if (!bFound)
__uuidof(IDebugDataSpaces), pDebugControl->Output(DEBUG_OUTPUT_NORMAL,
(void **)&pDebugDataSpaces))) { "Unable to determine if Sleep is present\n");
// The value is pushed immediately prior to pDebugControl->Release();
// KERNELBASE!Sleep+0xF }
DWORD dwMilliseconds = 0; return S_OK;
}
if (SUCCEEDED(pDebugDataSpaces->ReadVirtual(

If you repeat the test using the x64 debugger, the amd64-compiled The first step is to get the stack frames. To get the frames, I use
version of the debugger extension and the x86 or x64 dump files, you’ll QueryInterface to get the IDebugControl interface, and then use
get the same results. That is, the extension is architecture-agnostic. GetStackTrace to retrieve information about each stack frame.
GetStackTrace takes an array of DEBUG_STACK_FRAME struc-
Processor Types and Stacks tures. I always allocate the array of DEBUG_STACK_FRAME
I’m now going to extend this infrastructure once again. Let’s add structures on the heap so that I don’t cause a stack overflow. If you’re
a command that finds the duration of a Sleep call on the current retrieving a stack overflow thread of a target, you’ll probably hit
thread stack. The !sleepy command (see Figure 5 ) will resolve your own stack limit if the array is allocated on your stack.
the call stack symbols, look for the Sleep function and read the If GetStackTrace succeeds, the array will be populated with
DWORD that represents the milliseconds to delay, and then will information for each frame that was walked. Success here doesn’t
output the delay value (if found). necessarily mean that the frame information is correct. The
To add some complexity to the command, the command will sup- debugger does its best to walk the stack frames, but mistakes can
port the x86 and x64 versions of the test01 application. Because the be made when the symbols aren’t correct (when they’re missing or
calling convention is different for x86 and x64 applications, the com- have been forced). If you’ve used “.reload /f /i” to force the symbol
mand will have to be aware of the target’s architecture as it progresses. load, poor symbol alignment will probably occur.
66 msdn magazine Debugger APIs
Project3 12/16/09 11:55 AM Page 1
To effectively use the contents of each of the DEBUG_STACK_ There are two parts to the symbol: the symbol name
FRAME structures, I need to know the target’s effective proces- (KERNELBASE!Sleep) and the displacement (0xf). The symbol
sor type. As mentioned previously, the target architecture can be name is an amalgamation of the module name and the function
completely different from the debugger extension architecture. The name (<module>!<function>). The displacement is the byte offset
effective processor type (.effmach) is the architecture that the from the start of the function to which program flow will return to
target is currently using. after the call has returned.
The processor type can also be a different processor type than that If there are no symbols, the function will be reported as just the
used by the target’s host. The most common example of this is when module name with a large displacement (Test01+0x1015).
the target is an x86 application that’s running via Windows 32-bit Once I’ve found the frame, the next step is to extract the delay.
on Windows 64-bit (WOW64) on an x64 edition of Windows. The When the target is x86 based, the delay will be in a DWORD that
effective processor type is IMAGE_FILE_MACHINE_I386. The has been pushed onto the stack immediately prior to the function
actual type is IMAGE_FILE_MACHINE_AMD64. call (note that this is fragile logic):
This means you should consider an x86 application to be an x86 // @$csp is the pseudo-register of @esp
0:000> dps @$csp
application regardless of whether it’s running on an x86 edition <snip>
of Windows or an x64 edition of Windows. (The only exception 001bfa4c 752c1818 KERNELBASE!Sleep+0xf
001bfa50 00002710
to this rule is when you’re debugging the WOW64 calls that <snip>
surround the x86 process.) The StackOffset member of the DEBUG_STACK_FRAME
To get the effective processor type, I use the IDebugControl in- structure actually points to this address already, so no pointer
terface that I already have, and then use GetEffectiveProcessorType. arithmetic is necessary. To get the value, I use QueryInterface to
If the effective processor type is i386, I need to look for the get the IDebugDataSpaces interface, and then use ReadVirtual to
KERNELBASE!Sleep+0xf function. If all the symbols are resolved read the DWORD from the target address space.
correctly, the function should be in frame 3: If the target is x64 based, the delay isn’t in the stack—it’s in
0:000> knL4
# ChildEBP RetAddr
the rsi register (this is also fragile logic due to its frame-context
00 001bf9dc 76fd48b4 ntdll!KiFastSystemCallRet dependency):
01 001bf9e0 752c1876 ntdll!NtDelayExecution+0xc 0:000> r @rsi
02 001bfa48 752c1818 KERNELBASE!SleepEx+0x65 rsi=0000000000002710
03 001bfa58 012f1015 KERNELBASE!Sleep+0xf
To get the value, I use QueryInterface to get the IDebugRegisters
If the effective processor type is AMD64, I look for the
interface. I first need to use GetIndexByName to get the index of
KERNELBASE!SleepEx+0xab function. If all the symbols are
the rsi register. I then use GetValue to read the register value from
resolved correctly, the function should be in frame 1:
0:000> knL2
the target registers. Because rsi is a 64-bit register, the value is
# Child-SP RetAddr Call Site returned as an INT64. Because the DEBUG_VALUE structure is a
00 00000000'001cfc08 000007fe'fd9b1203 ntdll!NtDelayExecution+0xa
01 00000000'001cfc10 00000001'3fda101d KERNELBASE!SleepEx+0xab
union, you can simply reference the I32 member instead of the I64
However, based on the level of symbol resolution available, the member to get the truncated version that represents the DWORD
function symbol I’m looking for may or may not be in the expected passed to Sleep.
frame. If you open the test01 x86 dump file and don’t specify a sym- Once again, in both cases, I use the IDebugControl::Output
bol path, you can see an example of this. The KERNELBASE!Sleep function to output the result.
call will be in frame 1 instead of frame 3:
0:000> knL4 Break!
# ChildEBP RetAddr In this article, I barely scratched the surface of what can be achieved.
WARNING: Stack unwind information not available. Following frames may be wrong.
00 001bfa48 752c1818 ntdll!KiFastSystemCallRet Stacks, symbols, registers, memory I/O and environmental infor-
01 001bfa58 012f1015 KERNELBASE!Sleep+0xf mation are but a few of the many things that you can interrogate
02 001bfaa4 75baf4e8 Test01+0x1015
03 001bfab0 76feaf77 kernel32!BaseThreadInitThunk+0x12 and change from within an extension.
The debugger warns you of this possible mistake. If you want to In a future article I’ll delve deeper into the relationship a debug-
have your extension adapt to these types of issues, you should iterate ger extension can have with the debugger. I’ll cover debugger
over the frames as I have, instead of just looking at the expected frame. clients and debugger callbacks, and I’ll use these to encapsulate
To determine the existence of the Sleep function, I need to look the SOS debugger extension so that you can write an extension
up the symbol for each frame. If the effective processor type and that can debug managed applications without having to have any
symbol make a valid pair, the function has been found. Note that knowledge of the underlying .NET structures. „
this logic is fragile and is being used to simplify the example. The
symbol may change between builds and platforms. For example, ANDREW RICHARDS is a Microsoft senior escalation engineer for Exchange Server.
Windows Server 2008 is kernel32!Sleep+0xf, but Windows 7 is He has a passion for support tools, and is continually creating debugger extensions
and applications that simplify the job of support engineers.
KERNELBASE!Sleep+0xf.
To get the symbol, I use QueryInterface to get the IDebugSymbol THANKS to the following technical experts for reviewing this article:
interface. I then use GetNameByOffset to get the symbol of the Drew Bliss, Jen-Lung Chiu, Mike Hendrickson, Ken Johnson, Brunda
instruction offset address. Nagalingaiah and Matt Weber

68 msdn magazine Debugger APIs


20 Minutes to 4 Seconds...
SpreadsheetGear for .NET reduced the time to generate a
critical Excel Report “from 20 minutes to 4 seconds” making
his team “look like miracle workers.”
Luke Melia, Software Development Manager at Oxygen Media in New York

ASP.NET Excel Reporting


Easily create richly formatted Excel reports without Excel using the
new generation of spreadsheet technology built from the ground up
for scalability and reliability.

Excel Compatible Windows Forms Control


Add powerful Excel compatible viewing, editing, formatting, calculating,
charting and printing to your Windows Forms applications with the
easy to use WorkbookView control.

Create Dashboards from Excel Charts and Ranges


You and your users can design dashboards, reports, charts, and
models in Excel rather than hard to learn developer tools and you can
easily deploy them with one line of code.

Download the FREE fully functional 30-Day


evaluation of SpreadsheetGear 2010 today at

www.SpreadsheetGear.com.
Toll Free USA (888) 774-3273 | Phone (913) 390-4797 | sales@spreadsheetgear.com

Untitled-9 1 11/2/10 12:01 PM


RIA FRAMEWORKS

Building Data-Centric
Web Apps with ASP.NET
MVC and Ext JS
Juan Carlos Olamendy

A rich Internet application (RIA) combines the usability with Selenium for testing. Ext JS also provides pre-defined controls
of a desktop app with the flexibility of Web-based deployment that simplify creating the UI of your Web app.
and revision. There are two key approaches to building RIAs. Unfortunately, most examples of Ext JS are illustrated with
First, there are browser plug-ins that host execution environ- PHP, Python and Ruby on Rails code on the server side. But that
ments such as Flash, Java and Silverlight. Second, there are doesn’t mean developers using Microsoft technologies can’t take
JavaScript-based extension libraries such as Dojo, Ext JS, jQuery, advantage of Ext JS. While it’s difficult to integrate Ext JS with Web
MooTools, Prototype and YUI. Each approach has its advantages Forms development (due to the abstraction layer that encapsulates
and disadvantages. the request-response nature of the Web in order to provide a
JavaScript libraries are a popular choice for building RIAs stateful control-based model), you could use the ASP.NET MVC
because JavaScript is supported by all major browsers and there’s no framework, enabling you to leverage both the Microsoft .NET
need to install a plug-in or runtime environment. I’ve been experi- Framework and Ext JS in the same app.
menting with another of the libraries mentioned—Ext JS—and I’ve In this article, I’ll provide the tutorial I couldn’t find, walking
found that it makes an interesting choice for implementing Web through the development of a real-world Web solution using ASP.NET
apps. It’s easy to implement, well-documented and is compatible MVC and Ext JS that reads from and writes to a back-end database.

Ext JS Form Basics


This article discusses:
To use Ext JS, you first need to download it from sencha.com. (I used
• Ext JS form basics version 3.2.1, but you should grab the most recent version.) Note
• Creating the data store that a free, open source version of Ext JS is available for open source
• Using ASP.NET MVC projects, non-profit organizations and educational use. For other
• Tying the layers together uses you may need to purchase a license. See sencha.com/products/
license.php for more information.
Technologies discussed:
Uncompress the download into a directory in your file system. It
ASP.NET MVC, Ext JS, SQL Server 2008 contains everything you need to develop a Web solution using Ext
Code download available at: JS, in particular the main file ext-all.js. (There’s also a debug version
code.msdn.microsoft.com/mag201103ASPNET
to help find errors more easily.) Dependencies, documentation and
example code are all included in the download.
70 msdn magazine
The Ext.BLANK_IMAGE_URL statement is important for
rendering the widgets. It’s called the spacer image (a transparent
1x1 pixel image) and mainly used to generate the blank space as
well as to place icons and separators.
The Ext.onReady statement is the first method to define with
Ext JS code. This method is automatically called once the DOM
is fully loaded, guaranteeing that every HTML element that you
may reference is available when the script runs. In the case of
extjs-example.js, here’s the script itself:
formextjs.tutorial.FormTutorial = {
init: function () {
this.form = new Ext.FormPanel({
title: 'Getting started form',
renderTo: 'frame',
width: 400,
url: 'remoteurl',
defaults: { xtype: 'textfield' },
bodyStyle: 'padding: 10px',
html: 'This form is empty!'
});
}
}
An instance of the class Ext.FormPanel is created as a container
Figure 1 The Completed Form for the fields. The renderTo property points to the div element
where the form will be rendered. The defaults property specifies
The required folders for a project are \adapters and \resources. the default component type on the form. The url property specifies
The adapters folder enables use of other libraries alongside Ext JS. the URI to send the request of the form. Finally, the html property
The resources folder contains dependencies such as CSS and images. specifies the text (with any HTML formatting) as the default output.
To use Ext JS correctly, you’ll also need to include the three key To add fields, you need to replace the html property with the
file references in your pages: items property:
ext-3.2.1/adapter/ext/ext-base.js items: [ nameTextField, ageNumberField ]
ext-3.2.1/ext-all.js
ext-3.2.1/resources/css/ext-all.css
The first two items to add are a text field and a number field:
var nameTextField = new Ext.form.TextField({
The ext-base.js file contains the core functionality of Ext JS. The fieldLabel: 'Name',
widget definitions are contained in ext-all.js, and ext-all.css includes emptyText: 'Please, enter a name',
name: 'name'
stylesheets for the widgets. });
Let’s start using Ext JS in a static HTML page to illustrate the var ageNumberField = new Ext.form.NumberField({
fieldLabel: 'Age',
basics. The following lines are contained within the head section value: '25',
of the page and link the required files to successfully develop an name: 'age'
});
Ext JS solution (I’ve also included the JavaScript module with some
The required properties are: fieldLabel property (to set a descrip-
example widgets from the Ext JS download):
<link rel="stylesheet" type="text/css"
tive message accompanying the component of the form) and name
href="ext-3.2.1/resources/css/ext-all.css" /> property (to set name of the request parameter). The emptyText
<script type="text/javascript" language="javascript"
src="ext-3.2.1/adapter/ext/ext-base.js"></script>
property defines the watermark text that the field will contain when
<script type="text/javascript" language="javascript" it’s empty. The value property is the default value for the control.
src="ext-3.2.1/ext-all.js"></script>
<script type="text/javascript" language="javascript"
Figure 2 Form Buttons
src="extjs-example.js"></script>
Within the body of the file, I insert a div element for rendering buttons: [{
text: 'Save',
the main Ext JS form: handler: function () {
<div id="frame"> form.getForm().submit({
</div> success: function (form, action) {
The extjs-example.js file provides some insight into how Ext JS Ext.Msg.alert('Success', 'ok');
},
applications are constructed. The template for any Ext JS application uses failure: function (form, action) {
the Ext.ns, Ext.BLANK_IMAGE_URL and Ext.onReady statements: Ext.Msg.alert('Failure', action.result.error);
}
Ext.ns('formextjs.tutorial'); });
Ext.BLANK_IMAGE_URL = 'ext-3.2.1/resources/images/default/s.gif'; }
formextjs.tutorial.FormTutorial = { },
... {
} text: 'Reset',
Ext.onReady(formextjs.tutorial.FormTutorial.init, handler: function () {
formextjs.tutorial.FormTutorial); form.getForm().reset();
The Ext.ns statement enables you to logically organize your code in a }
}]
namespace, in order to avoid naming conflicts and scoping problems.
msdnmagazine.com March 2011 71
Figure 3 Creating the Human Resources Database property contains the logic associated with the action executed on
create table department(
the button. In this case, there are two buttons whose names are Save
deptno varchar(20) primary key, and Reset. The Save button handler executes a submit action on the
deptname varchar(50) not null,
location varchar(50)
form and shows a message indicating success or failure. The Reset
); button handler resets the field values on the form.
create unique index undx_department_deptname on department(deptname);
The last—but important—step in form creation is validation.
In order to specify required fields, we need to set the allowBlank
insert into department
values('HQ-200','Headquarter-NY','New York');
property to false and the blankText property to an error message
insert into department that's displayed when the required validation fails. For example,
values('HR-200','Human Resources-NY','New York');
insert into department
here’s the name field of the form:
values('OP-200','Operations-NY','New York'); { fieldLabel: 'Name', emptyText: 'Please, enter a name', name: 'name',
insert into department allowBlank: false }
values('SL-200','Sales-NY','New York'); When you run the application and click the Save button without
insert into department
values('HR-300','Human Resources-MD','Maryland'); entering any data in the Name and Age fields, then you receive an
insert into department error message and the required fields are underlined in red.
values('OP-300','Operations-MD','Maryland');
insert into department

The view will present the


values('SL-300','Sales-MD','Maryland');

create table employee(

form to get the data related


empno varchar(20) primary key,
fullname varchar(50) not null,
address varchar(120),

to one employee.
age int,
salary numeric(8,2) not null,
deptno varchar(20) not null,
constraint fk_employee_department_belong_rltn foreign key(deptno)
references department(deptno)
); To customize the error messages on the fields, add the following
create unique index undx_employee_fullname on employee(fullname); line of code just under the Ext.onReady function:
Ext.QuickTips.init();

Another way to declare controls is on the fly: Now, when the user moves the mouse pointer over the field, a
items: [ balloon with a message displaying the error is displayed.
{ fieldLabel: 'Name', emptyText: 'Please, enter a name', name: 'name' },
{ xtype: 'numberfield', fieldLabel: 'Age', value: '25', name: 'age' }
I set several validation rules for the fields such as specifying the
] minimum and maximum length allowed, deferring the field vali-
As you can see, for the name field you don’t have to specify the dation until form submission, and creating validation functions
type because it’s taken from the default properties of the form. for URLs, e-mail addresses, and other types of data. You can see
I’ll add some additional elements to the form, which ends up the details of this validation in the code download.
looking like Figure 1.
So far, you’ve built a form using Ext JS to take data from the user. Building the Web App
Now, let’s send the data to the server. You’ll need to add a button Now, let’s develop a Web solution using Ext JS and ASP.NET MVC.
to handle the submit process and show the result to the user, as I used ASP.NET MVC 2, but this solution should be applicable
shown in Figure 2. to ASP.NET MVC 3 as well. The scenario I’m going to address is
The buttons property enables the form to manage all the possible adding an employee in a human resources management system.
actions. Each button has name and handler properties. The handler
Figure 5 Adding the Employee Form
Figure 4 Site.Master <%@ Page Title="" Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
<head runat="server"> Inherits="System.Web.Mvc.ViewPage" %>
<title><asp:ContentPlaceHolder ID="TitleContent"
runat="server" /></title> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent"
<link href="../../Content/Site.css" rel="stylesheet" runat="server">
type="text/css" /> Index
</asp:Content>
<!-- Include the Ext JS framework -->
<link href="<%= Url.Content("~/Scripts/ext-3.2.1/resources/css/ext-all.css") %>" <asp:Content ID="Content2" ContentPlaceHolderID="MainContent"
rel="stylesheet" type="text/css" /> runat="server">
<script type="text/javascript" <h2>Add a New Employee</h2>
src="<%= Url.Content("~/Scripts/ext-3.2.1/adapter/ext/ext-base.js") %>"> <div id="employeeform"></div>
</script> </asp:Content>
<script type="text/javascript"
src="<%= Url.Content("~/Scripts/ext-3.2.1/ext-all.js") %>"> <asp:Content ID="Content3" ContentPlaceHolderID="Scripts"
</script> runat="server">
<!-- Placeholder for custom JS and CSS and JS files <script type="text/javascript"
for each page --> src="<%= Url.Content("~/Scripts/employee_form.js") %>">
<asp:ContentPlaceHolder ID="Scripts" runat="server" /> </script>
</head> </asp:Content>

72 msdn magazine RIA Frameworks


Untitled-1 1 1/11/10 10:55 AM
Figure 6 Form Field Widgets Next, create the database schema. For this example, the schema
items: [
will contain two entities: employee and department. Figure 3 shows
{ fieldLabel: 'Employee ID', name: 'empno', allowBlank: false }, how I created the Human Resources database and the underlying
{ fieldLabel: 'Fullname', name: 'fullname', allowBlank: false },
{ xtype: 'textarea', fieldLabel: 'Address', name: 'address',
tables and constraints.
multiline: true }, Now let’s use LINQ to SQL to define the structure of the entities
{ xtype: 'numberfield', fieldLabel: 'Age', name: 'age' },
{ xtype: 'numberfield', fieldLabel: 'Salary', name: 'salary',
and the persistence mechanism. Start by creating an Employee-
allowBlank: false }, Repository class to manage the data-access logic to the employee
{ xtype: 'combo', fieldLabel: 'Department', name: 'deptno',
store: departmentStore, hiddenName: 'deptno',
table. In this case, you only need to implement the create operation:
displayField: 'deptname', valueField: 'deptno', typeAhead: true, public class EmployeeRepository {
mode: 'remote', forceSelection: true, triggerAction: 'all', private HumanResourcesDataContext _ctxHumanResources =
emptyText: 'Please, select a department...', editable: false } new HumanResourcesDataContext();
],
public void Create(employee employee) {
this._ctxHumanResources.employees.InsertOnSubmit(employee);
this._ctxHumanResources.SubmitChanges();
The Add Employee use case description is as follows: A screen }
prompts the user to enter valid information for a new employee }
such as employee identifier, full name, address, age, salary and You also need a DepartmentRepository class to manage the
department. The department field is a list of departments from data-access logic to the department table. Again, in this simple case
which to choose. you only need to implement the read operation in order to find a
The main implementation strategy is to create an Ext JS form on list of departments:
the client side—as you’ve already seen—and then process the data public class DepartmentRepository {
private HumanResourcesDataContext _ctxHumanResources =
using ASP.NET MVC. The persistence layer will use LINQ to rep- new HumanResourcesDataContext();
resent business entities and to persist data to the database system.
public IQueryable<department> FindAll() {
The back-end database is Microsoft SQL Server 2008. return from dept in this._ctxHumanResources.departments
Start by opening Visual Studio 2010 and creating a new project orderby dept.deptname
select dept;
with the ASP.NET MVC 2 Web Application template. }
}
Figure 7 HumanResourceController Now let’s define another important piece of the architecture: the
controller. To define a controller, right-click on the Controllers folder
using System;
using System.Collections.Generic; in the Solution Explorer window and select Add | Controller. I used
using System.Linq; HumanResourcesController as the controller name.
using System.Web;
using System.Web.Mvc;

Ext JS Presentation Layer


using HumanResources_ExtJS_ASPNETMVC.Models;

namespace HumanResources_ExtJSASPNETMVC.Models.BusinessObjects { Now let’s go back to Ext JS and use the framework to build the
public class HumanResourcesController : Controller {
DepartmentRepository _repoDepartment = new DepartmentRepository(); presentation layer of the application. For this solution, you only
EmployeeRepository _repoEmployee = new EmployeeRepository(); need to import ext-all.js and the \adapter and \resources folders.
// GET: /HumanResources/ Go to the Site.Master page and add references to the Ext JS files
public ActionResult Index() {
return View();
inside the head element, as well as an <asp:ContentPlaceHolder>
} tag element as a container of the customized JavaScript and CSS
// POST: /HumanResource/Departments code for each page, as shown in Figure 4.
[HttpPost] Now let’s add the other important pieces of the MVC architec-
public ActionResult Departments() {
var arrDepartment = this._repoDepartment.FindAll(); ture: the view. The view will present the form to get the data related
var results = (new { to one employee. Go to HumanResourcesController, right-click
departments = arrDepartment
}); on the Index action method and select Add View. Click the Add
return Json(results); button in the Add View dialog box.
}
To implement the Ext JS form created earlier in the article,
// POST: /HumanResource/AddEmployee you need to add a JavaScript file to the Scripts directory and a
[HttpPost]
public ActionResult AddEmployee(employee employee) { reference to this JavaScript file in the view. Then include the
string strResponse = String.Empty; reference to the employee_form.js file and add a div element into
try {
this._repoEmployee.Create(employee); the Index.aspx view (see Figure 5).
strResponse = "{success: true}"; Go to the employee_form.js file and add some code to configure
}
catch { the ExtJS form and its underlying widgets. The first step is to define
strResponse = "{success: false, error: \"An error occurred\"}"; an instance of Ext.data.JsonStore class to get a list of departments:
}
return Content(strResponse); var departmentStore = new Ext.data.JsonStore({
} url: 'humanresources/departments',
} root: 'departments',
} fields: ['deptno', 'deptname']
});

74 msdn magazine RIA Frameworks


the file here. See the code download for the complete source code
listing for this file.)
Now let’s go to HumanResourceController and implement the
corresponding action methods, as shown in Figure 7.

That’s It!
Now run the solution. You’ll see the Web page shown in Figure
8. Enter some data in the form and then click Add. You’ll see a
confirmation message box. You’ll also see the row inserted in the
dbo.employee table on the database.
That’s really all there is to creating a simple RIA. Depending on
the features you want to leverage, a similar application could be
built with any of the other popular JavaScript frameworks while
still employing ASP.NET MVC. You could easily substitute Entity
Framework for the data layer, and use Windows Azure storage or
SQL Azure as the back-end data store. These simple building blocks
make building a basic data-centric RIA quick and easy. „

JUAN CARLOS OLAMENDY is a senior architect, developer and consultant. He has


been recognized as a Microsoft Most Valuable Professional (MVP) and Oracle
Figure 8 Running the Application ACE several times. He is Microsoft Certified Technology Specialist in Windows
Communication Foundation. You can contact Olamendy at johnx_olam@fastmail.
The url property points to the departments action method on
the HumanResourceController controller. This method is accessed THANKS to the following technical experts for reviewing this article:
by the HTTP POST verb. The root property is the root element of Scott Hanselman and Eilon Lipton
the list of departments. The fields property
specifies the data fields. Now define the form.
The properties are self-descriptive:
var form = new Ext.FormPanel({
title: 'Add Employee Form',
renderTo: 'employeeform',
width: 400,
url: 'humanresources/addemployee',
defaults: { xtype: 'textfield' },
bodyStyle: 'padding: 10px',
In this case, the url property points to
the AddEmployee action method on the
HumanResourceController controller. This
method is also accessed using HTTP POST.
The items property provides the list of
widgets representing the fields of the form
(Figure 6). Here the default widget is a text
field (this is specified in the defaults property).
The first field is employee number, which is
required (specified by the allowBlank prop-
erty). The second field is the full name, which
is also a required text field. The address field
is an optional text area. The age field is an
optional number field. The salary field is
a required number field. And finally, the
department number field is an identifier string,
which is selected from a list of departments.
Finally, the buttons property is defined
to handle the actions over the form. This is
configured just like Figure 2, but the text
property has the value “Add.”
Now the employee_form.js file is complete.
(I’ve gone through most of the elements of
msdnmagazine.com March 2011 75
C A C H E I N T E G R AT I O N

Building and Using


Custom OutputCache
Providers in ASP.NET
Brandon Satrom

If you’re a Web developer, in the past you may have utilized The ASP.NET output cache uses an in-memory storage mecha-
the output-caching facility provided by ASP.NET. Introduced nism and, until the .NET Framework 4, it wasn’t possible to override
with the first version of the Microsoft .NET Framework, ASP.NET or replace the default cache with your own implementation. With
output caching can improve the performance of serving content the new OutputCacheProvider type, it’s now possible to implement
to site visitors by retrieving that content from a cache, bypassing your own mechanism for caching page output in ASP.NET.
re-execution of pages or controllers. This saves your application In this article, I’ll discuss two such custom mechanisms. First,
expensive database calls when returning data that doesn’t update using MongoDB—a popular document-oriented database—I’ll
frequently, or that can be stale for periods of time. create my own provider to facilitate output caching in a simple
ASP.NET MVC application. Then, using the same application,
I’ll quickly swap out my custom provider to leverage features of
This article discusses a prerelease version of Windows Azure Windows Azure AppFabric—specifically, the new DistributedCache
AppFabric SDK 2.0. All information is subject to change.
provider that leverages Windows Azure infrastructure to provide
This article discusses: a distributed, in-memory cache in the cloud.
• Output caching in ASP.NET
• Extensible output caching in ASP.NET
Output Caching in ASP.NET
In ASP.NET Web Forms applications, output caching can be con-
• NoSQL, document databases and MongoDB
figured by adding an OutputCache Page directive to any ASP.NET
• Building a custom OutputCacheProvider using MongoDB page or user control:
• Using the MongoDB OutputCacheProvider in ASP.NET MVC <%@ OutputCache Duration="60" Location="Any" VaryByParam="name" %>

• Using the Windows Azure AppFabric DistrubutedCache provider For ASP.NET MVC applications, output caching is available
using an action filter that ships with ASP.NET MVC, and which
Technologies discussed:
can be leveraged as an attribute on any controller action:
ASP.NET 4, Windows Azure, MongoDB [OutputCache(Duration=60, VaryByParam="none")]

Code download available at: “Duration” and “VaryByParam” are required in ASP.NET MVC 1
code.msdn.microsoft.com/mag201103OutputCache
and 2 applications (VaryByParam is optional in ASP.NET MVC 3),
and both mechanisms provide several other attributes and parameters
76 msdn magazine
Browser requests page Yes Retrieve page from Has item Yes Retrieve page from
(with OutputCache Does page exist cache by key cache by key
in cache? expired?
directive or attribute) (path + parameters) (path + parameters)

No No

Execute page/action Return cached content Store content in cache

Return content Store content in cache Return content

Figure 1 The ASP.NET Output Caching Process

that enable developers to control how content is cached (several Although the .NET Framework 2.0 and 3.0 introduced several
VaryByX parameters), where it’s cached (Location) and capabilities enhancements to the default cache provider, the provider itself
for setting cache invalidation dependencies (SqlDependency). remained the same: an in-memory store, with no extension points
For traditional output caching, nothing else is needed to imple- or way to provide your own implementation. The in-memory cache
ment this functionality in your applications. The OutputCache type is a perfectly acceptable option in most cases, but can, at times,
is an HttpModule that runs when your application starts and goes to contribute to diminished site performance as server resources
work when a page directive or action filter is encountered. Upon the are maxed out and memory becomes scarce. What’s more, the
first request of the page or controller in question, ASP.NET will take default caching provider mechanism automatically evicts cached
the resulting content (HTML, CSS, JavaScript files and so on) and resources—regardless of specified duration—when memory does
place each item in an in-memory cache, along with an expiration become scarce, which leaves the developer with little control over
and a key to identify that item. The expiration is determined by the how cached resources are managed.
Duration property, and the key is determined by a combination of
the path to the page and any necessary VaryBy values—for example, Extensible Output Caching in ASP.NET
query string or parameter values if the VaryByParam property is The release of the .NET Framework 4 introduced a new facility that
provided. So, consider a controller action defined in this manner: enables developers to create their own output cache providers and
[OutputCache(Duration=20, VaryByParam="vendorState")]
easily plug those providers into new or existing applications with
Public ActionResult GetVendorList(string vendorState) only minor changes to the application and its configuration. These
{
// Action logic here.
providers are free to use whatever storage mechanism for cached
} information that they choose, such as local disks, relational and non-
In this case, ASP.NET will cache an instance of the resulting relational databases, the cloud or even distributed caching engines
HTML view for each occurrence of vendorState (for example, one such as that provided in Windows Server AppFabric. It’s even possible
for Texas, one for Washington and so on) as that state is requested. to use multiple providers for different pages in the same application.
The key by which each instance is stored, in this case, will be a

It’s now possible to implement


combination of the path and the vendorState in question.
If, on the other hand, the VaryByParam property is set to “none,”

your own mechanism for


ASP.NET will cache the result of the first execution of GetVendor-
List and will deliver the same cached version to all subsequent

caching page output in ASP.NET.


requests, regardless of the value of the vendorState parameter passed
into that action. The key—by which this instance is stored when
no VaryByParam value is provided—would just be the path. A
simplified view of this process is depicted in Figure 1. Creating your own output cache provider is as simple as creating
Beyond the Duration parameter—used to control the life a new class that derives from the new System.Web.Caching.Out-
of the item in the cache—and a handful of VaryBy parameters putCacheProvider abstract class and overriding the four methods
(VaryByParam, VaryByHeader, VaryByCustom, VaryByControl and that ASP.NET requires to work with cached items. The framework
VaryByContentEncoding) used to control the granularity of cached definition for the OutputCacheProvider class is listed here (see bit.ly/
items, the output cache can be configured to control the location fozTLc for more information):
of cached content (client, server or downstream proxy). In addi- public abstract class OutputCacheProvider : ProviderBase
{
tion, ASP.NET 2.0 introduced a SqlDependency attribute, which public abstract object Get(string key);
allows developers to specify database tables that a page or control public abstract object Add(string key, object entry, DateTime utcExpiry);
public abstract void Set(string key, object entry, DateTime utcExpiry);
depends upon so that, in addition to time expiration, updates to public abstract void Remove(string key);
your underlying source data can also cause cached items to expire. }

msdnmagazine.com March 2011 77


Figure 2 A Starter OutputCacheProvider Class One situation well-suited for a NoSQL database is output caching.
public class DocumentDatabaseOutputCacheProvider : OutputCacheProvider
NoSQL databases are ideal for working with transient or temporary
{ data, and cached pages from an ASP.NET application certainly fit
readonly Mongo _mongo;
readonly IMongoCollection<CacheItem> _cacheItems;
that bill. One popular NoSQL option is MongoDB (mongodb.org), a
document-oriented NoSQL database used by Shutterfly, Foursquare,
public override object Get(string key)
{
The New York Times and many others. MongoDB is a fully open
return null; source database written in C++ , with drivers for nearly every major
}
programming language, C# included. We’ll use MongoDB as the
public override object Add(string key, object entry, DateTime utcExpiry) storage mechanism for our custom output cache provider.
{

}
return null;
Building a Custom OutputCacheProvider
public override void Set(string key, object entry, DateTime utcExpiry)
Using MongoDB
{ To get started, you’ll want to go to mongodb.org to download and
return; install the tool. The documents at mongodb.org/display/DOCS/Quickstart
}
should tell you everything you need to know to install MongoDB on
public override void Remove(string key) Windows, Mac OS X and Unix. Once you’ve downloaded MongoDB
{
return; and tested things out with the shell, I recommend installing the
} database as a service using the following command from the instal-
}
lation directory (be sure to run cmd.exe as an administrator):
C:\Tools\MongoDB\bin>mongod.exe --logpath C:\Tools\MongoDB\Logs
Once you’ve implemented these four methods, all that’s left is to --directoryperdb --install
add the new provider to your web.config, specify it as the default MongoDB will install itself as a service on your computer and
and add an OutputCache directive or attribute to your application. will use C:\Data\db as the default directory for all its databases. The
I’ll cover these steps in detail as I walk through the creation of our option --diretoryperdb tells MongoDB to create a root directory
own output cache provider that uses a document database called for every database you create.
MongoDB. But first, it may be helpful to introduce a little context After running the previous command, type the following to
around the tool we’ll be using to build our custom provider. start the service:
net start MongoDB

NoSQL, Document Databases and MongoDB Once you have things up and running, you’ll need to install a
For much of the past few decades, the preferred application storage driver library to work with MongoDB in .NET. There are several
mechanism has been the relational database management system options available; I’ll be using the mongodb-csharp driver created
(RDBMS), which stores data and relationships in tables. SQL Server by Sam Corder (github.com/samus/mongodb-csharp).
and Oracle are examples of RDBMSes, as are most of the popular We have MongoDB installed, and we have a driver that we
commercial and open source databases currently in use. can use within a .NET application, so now it’s time to create our
However, not all problems requiring storage fit into the same custom output cache provider. To do this, I created a new class
transactional mold. In the late ’90s, as the Internet expanded and library called DocumentCache and added two classes: Document-
many sites grew to manage large volumes of data, it became obvi- DatabaseOutputCacheProvider and CacheItem.
ous that the relational model provided less-than-ideal performance The first is my provider, a public class that subclasses the
on certain types of data-intensive applications. Examples include abstract OutputCacheProvider. The beginning implementation is
indexing large volumes of documents, delivering Web pages on depicted in Figure 2.
high-traffic sites or streaming media to consumers. Figure 3 Implementing the Add Method
Many companies addressed their growing storage needs by
public override object Add(string key, object entry, DateTime utcExpiry)
turning to NoSQL databases, a class of lightweight database that {
doesn’t expose a SQL interface, fixed schemas or pre-defined key = MD5(key);
var item = _cacheItems.FindOne(new { _id = key });
relationships. NoSQL databases are used heavily by companies if (item != null) {
such as Google Inc. (BigTable), Amazon.com Inc. (Dynamo) and if (item.Expiration.ToUniversalTime() <= DateTime.UtcNow) {
_cacheItems.Remove(item);
Facebook (which has a store of more than 50TB for inbox searches) } else {
and are experiencing steady growth in popularity and use. return Deserialize(item.Item);
}
It’s important to note that, while some have used the term NoSQL }
as a rallying cry to call for the abandonment of all RDBMSes,
_cacheItems.Insert(new CacheItem
others emphasize the value of utilizing both types of storage. {
NoSQL databases were conceived to solve a class of problem Id = key,
Item = Serialize(entry),
that RDBMSes couldn’t—not to replace these systems outright. Expiration = utcExpiry
The discriminating developer would be wise to understand both });

systems and utilize each where appropriate, even at times mixing return entry
}
both types of storage in a single application.
78 msdn magazine Cache Integration
Notice that the second private variable in Figure 2 references Figure 4 Implementing the Get Method
CacheItem, the other class I need to create in my project. CacheItem public override object Get(string key)
exists to contain the relevant details that my output cache provider {
key = MD5(key);
needs to work with both ASP.NET and my database, but it isn’t an var cacheItem = _cacheItems.FindOne(new { _id = key });
object needed external to my provider. As such, I define CacheItem
if (cacheItem != null) {
as an internal class, as shown here: if (cacheItem.Expiration.ToUniversalTime() <= DateTime.UtcNow) {
[Serializable] _cacheItems.Remove(cacheItem);
internal class CacheItem } else {
{ return Deserialize(cacheItem.Item);
public string Id { get; set; } }
public byte[] Item { get; set; } }
public DateTime Expiration { get; set; }
} return null;
Id maps to the key provided to me by ASP.NET. You’ll recall that }

the key is a combination of the path and any VaryBy conditions


defined in your page directive or action attribute. The Expiration { "_id" : ObjectId(Id), "CacheItem": new CacheItem { Id = key, Item =
entry, Expiration = utcExpiry } }
field corresponds to the Duration parameter, and the Item prop-
If I find a CacheItem with the same key as the one passed in, I
erty is the item to be cached.
check the expiration of that item against the current UTC time. If
We’ll start implementing our provider by setting things up in the
the item hasn’t expired, I binary deserialize it using a private method
constructor of our DocumentDatabaseOutputCacheProvider class.
(available in the online source code) and return the existing item.
Because we know that ASP.NET maintains a single instance of our
Otherwise, I insert a new item into my store, binary serialize it and
provider for the entire life of the application, we can perform some
return the passed-in entry.
setup work in our constructor, like this:
Once I’ve implemented adding items to the cache, I can add the
readonly Mongo _mongo;
readonly IMongoCollection<CacheItem> _cacheItems; Get method, which will find and return a cached item by key (or
null if a result isn’t found) as shown in Figure 4.
public DocumentDatabaseOutputCacheProvider()
{ As with the Add method, the Get method also checks the expiration
_mongo = new Mongo(); of the item if it exists in the database and, if it has expired, removes
_mongo.Connect();
it and returns null. If the item exists and hasn’t expired, it’s returned.
var store = _mongo.GetDatabase("OutputCacheDB"); Now, let’s implement the Remove method, which accepts a key and
_cacheItems = store.GetCollection<CacheItem>();
} removes the item matching that key from the database, as shown here:
The constructor creates a new instance of the Mongo type and public override void Remove(string key)
{
connects to the server using the default location (localhost). It key = MD5(key);
then asks MongoDB for the OutputCacheDB database and for an _cacheItems.Remove(new { _id = key });
}
IMongoCollection of our CacheItem type. Because MongoDB is
Just as with the code our driver uses to get a database that doesn’t
a schema-less database, creating databases on the fly is supported.
yet exist, MongoDB doesn’t complain if we attempt to remove an
Your first call to _mongo.GetDatabase(“OutputCacheDB”) will
item that isn’t found in our database. It simply does nothing.
return an instance of a new database, and that database will be
According to our abstract base class, there’s still one final method
created on disk when the first insert occurs.
we need to implement to have a functional custom output cache
Now let’s implement the Add method, as shown in Figure 3.
provider, the Set method. I’ve included it in Figure 5.
The first thing I do in each method is call the MD5 method on
the passed-in key. This method—omitted for brevity, but which
Figure 5 Implementing the Set Method Public Override Void
is available in the online source code download—generates a
Set(string key, object entry, DateTime utcExpiry)
database-friendly MD5 hash based on the key that ASP.NET
provides to me. Then, I call my IMongoCollection<CacheItem> {
key = MD5(key);
type, _cacheItems, to query the underlying database for the key in var item = _cacheItems.FindOne(new { _id = key });
question. Notice the anonymous type (new { _id = key}) passed into
if (item != null)
the FindOne method. Querying MongoDB is primarily done via {
selector objects or template documents that specify one or more item.Item = Serialize(entry);
item.Expiration = utcExpiry;
fields in a document to match in the database. _id is the key that _cacheItems.Save(item);
MongoDB uses to store documents, and—by convention of the }
else
driver I’m using—that property is automatically mapped to the Id {
property of my CacheItem class. So when I save a new cache item, _cacheItems.Insert(new CacheItem
{
as you see in the _cacheItems.Insert method shown in Figure 3, Id = key,
the key is assigned using the Id property, which MongoDB uses Item = Serialize(entry),
Expiration = utcExpiry
to populate the internal _id field of the record. MongoDB is a });
key-value store, so each CacheItem object is stored using binary- }
}
serialized JSON that looks like the following:
msdnmagazine.com March 2011 79
<caching>
<outputCache defaultProvider="DocumentDBCache">
<providers>
<add name="DocumentDBCache"
type="DocumentCache.DocumentDatabaseOutputCacheProvider, DocumentCache" />
</providers>
</outputCache>
</caching>
The <providers> element defines all of the custom providers
you want to add to your application and defines a name and type
for each. Because you can have multiple custom providers in a
single application, you’ll also want to specify the defaultProvider
attribute, as I do in the preceding code snippet.
My sample application is a simple ASP.NET MVC site with a
CustomersController. In that controller is an action called Top-
Figure 6 The Cached TopCustomers View Customers, which returns a list of the top customers for my busi-
ness. This information is the result of complex calculations and
At a glance, it may seem that the Add and Set methods are several database queries in my SQL Server database and is only
identical, but there’s a key difference between their intended updated once an hour. For these reasons, it’s an ideal candidate for
implementation. According to the MSDN Library documents caching. So I add an OutputCache attribute to my action, like so:
on the OutputCacheProvider class (bit.ly/fozTLc), the Add method [OutputCache(Duration = 3600, VaryByParam = "none")]
public ActionResult TopCustomers()
of a custom provider should look for a value in the cache that {
var topCustomers = _repository.GetTopCustomers();
matches the specified key and, if it exists, do nothing to the return View(topCustomers);
cache and return the saved item. If that item doesn’t exist, Add }
should insert it. Now, if I run the site and navigate to my TopCustomers page,
The Set method, on the other hand, should always put its value my custom provider will roll into action. First, my Get method
into the cache, inserting the item if it doesn’t exist and overwriting will be called, but because this page isn’t yet cached, nothing will
it if it does. You’ll notice, in Figure 3 for Add and Figure 5 for Set, be returned. The controller action will then execute and return the
that these methods behave as specified. TopCustomers view, as depicted in Figure 6.
With those four methods implemented, we’re now ready to put ASP.NET will then call my custom cache provider, executing the
our provider to work. Set method, and the item will be cached. I’ve set the duration to 3,600
seconds—or 60 minutes—and every subsequent request for that
Using the MongoDB OutputCacheProvider time period will use the cached item returned by my Get method,
in ASP.NET MVC bypassing re-execution of my Controller Action. If any underlying
Once we’ve compiled our custom provider, we can add that data is changed, updates will be reflected on the first execution after
provider to any ASP.NET application with a few lines of config- expiration, and that new information will then be cached for an hour.
uration. After adding a reference to the assembly that contains If you want to see MongoDB in action, you have a couple of options.
the provider, add the following text to your web.config file in the You can open your browser and navigate to http://localhost:28107/,
<system.web> section: which displays the log, as well as recent queries and statistics about your
database. Or, you can run mongo.
exe from the bin directory of your
MongoDB installation and query
your database via the Mongo Shell.
For more information about using
these tools, see mongodb.org.

Using the
DistributedCache
Provider
So what if everything I’ve discussed
so far is more than you’d care to dive
into? Perhaps you want to leverage an
alternative caching mechanism, but
you have neither the time nor the de-
sire to roll your own? You’ll be happy
to know that, since the introduction
Figure 7 Windows Azure AppFabric Labs Summary Page of extensible output caching, many
80 msdn magazine Cache Integration
alternatives—commercial, open source and provided by Microsoft— Next, create the <dataCacheClient> section, replacing the
are either available or in development. One such example is a <host> name, cachePort and <messageSecurity> authorizationInfo
cloud-based, distributed in-memory cache: the DistributedCache properties with details from your portal account, like so:
provider currently available as a part of Windows Azure AppFabric. <dataCacheClient deployment="Simple">
<hosts>
If you’re already building cloud-based applications, Windows <host name="yournamespace.cache.appfabriclabs.com" cachePort="your port" />
Azure AppFabric Caching can speed up access to data for those </hosts>
<securityProperties mode="Message">
applications and, because caching is delivered as a cloud service, <messageSecurity authorizationInfo="your authentication token">
the setup is simple and requires no overhead to maintain. </messageSecurity>
</securityProperties>
At the time of this writing, AppFabric Caching is part of the </dataCacheClient>
Windows Azure AppFabric Community Technology Preview Then, find the <caching> section under <system.web> and add
October Release, so you can access caching features without an the following provider entry after the entry you created for your
active Windows Azure account. However, if you’re an MSDN custom provider:
subscriber, I highly recommend activating your Windows Azure <add name="DistributedCache"
benefits at windows.azure.com. Go to portal.appfabriclabs.com and create type="Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider,
Microsoft.Web.DistributedCache"
an account to use the developer preview features. cacheName="default" />
Once you’ve created a Labs account, click on the Add Service Finally, change the defaultProvider attribute on the <outputCache>
Namespace link to enable AppFabric services (see Figure 7). element to “DistributedCache.” The DistributedCacheOutput-
After you’ve set up your service namespace, click on the Cache CacheProvider is a subclass type of the abstract OutputCache-
link, and take note of the service URL and authentication token listed Provider, just like our MongoDB implementation. Now, build and
in the cache section (see Figure 8). You’ll need this information to run your application and navigate to the Top Customers page. Try
configure your application to use the DistributedCache provider. adding a customer while the list is still cached and notice that, as
Next, you’ll need to download and install the Windows Azure with our MongoDB implementation, the list will remain cached
AppFabric SDK (click the download link on the Cache page in the as long as you specify.
portal). After the installation is complete, you’re ready to configure
Windows Azure AppFabric Caching for your application. Wrapping Up
You’ll need to add references to several assemblies that the SDK In this article, I discussed ASP.NET output caching, classical
installation placed on your machine. Using the same ASP.NET uses of the default in-memory cache, and new extensible caching
MVC application you used for your custom Document database facilities provided using the OutputCacheProvider abstract class
cache, navigate to the SDK install location (default is C:\Program in the .NET Framework 4. I talked about NoSQL and document
Files*\Windows Azure AppFabric SDK\V2.0\Assemblies\Cache) databases and how these types of systems are ideal for working
and add references to each assembly contained within. with transient data, such as cached output. We used MongoDB
Once you’ve done that, open your web.config and add the following to build a sample output cache and used that within an ASP.NET
<configSections> entry, keeping any existing configuration sections: MVC application. Finally, we moved our output cache to the
<configSections> cloud, and with minor setup and configuration and no code
<section name="dataCacheClient"
type="Microsoft.ApplicationServer.Caching.DataCacheClientSection, changes whatsoever, we were able to swap out caching mecha-
Microsoft.ApplicationServer.Caching.Core" nisms in our application.
allowLocation="true" allowDefinition="Everywhere"/>
</configSections> Extensible output caching is just one of the many great new
features in ASP.NET 4, and I hope
this exploration of the feature—
and of the technologies that can
be leveraged along with it—has
been useful. To learn more about
MongoDB, go to mongodb.org. To learn
more about Windows Azure App-
Fabric, go to portal.appfabriclabs.com/
helpandresources.aspx. „

BRANDON SATROM works as a developer


evangelist for Microsoft in Austin, Texas.
He blogs at userinexperience.com and can
be found on Twitter: @BrandonSatrom.

THANKS to the following technical


experts for reviewing this article:
Figure 8 AppFabric Labs Cache Settings Page Brian H. Prince and Clark Sell
msdnmagazine.com March 2011 81
MOBILE MATTERS YOCHAY KIRIATY AND JAIME RODRIGUEZ

Windows Phone Navigation: The Basics


Silverlight Windows Phone applications have a Web-like page model Figure 1 Instantiation of RootFrame in App.xaml.cs
where the end users navigate from one page to another. There’s a
private void InitializePhoneApplication()
dedicated hardware Back button to easily navigate back to previous {
pages (without consuming screen real estate), and the journaling (or if (phoneApplicationInitialized)
return;
history) of your navigation is integrated with the platform to ease
navigating or transitioning across different applications. This two- // Create the frame but don't set it as RootVisual yet; this allows the splash
// screen to remain active until the application is ready to render.
part article will: RootFrame = new PhoneApplicationFrame();
• Introduce you to the page navigation model on Windows Phone. RootFrame.Navigated += CompleteInitializePhoneApplication;
• Provide the best practices you’ll need to get the most out
of the current APIs–such as integration with the hardware // Handle navigation failures
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
Back button, optimized loading and unloading of pages,
and ensuring your navigation model meets Windows Phone // Ensure we don't initialize again
phoneApplicationInitialized = true;
certification guidelines. }
• Introduce actionable, easy-to-follow recipes to create the
most complex navigations not implemented with the cur-
rent APIs, including transient content and page transitions. • Pressing the Back button while in the first screen of an
application must exit the application. This functionality comes
Windows Phone Nav Model for free. If you don’t prevent the navigation, the framework
The Windows Phone navigation model consists of a frame (Phone- exits for you—in fact, this is the only way to exit a Silverlight
ApplicationFrame) and one or more pages (PhoneApplicationPage) application. There’s no Exit method in the exposed APIs.
that hold the content loaded into the frame. • To maintain a consistent user experience (UX) across apps,
The PhoneApplicationFrame exposes most of the navigation the Back button should only be used for backward navigation.
events and exposes the Navigate method you’ll use to go across In addition to the Back button’s critical role in navigation, the
pages. It also determines the client area for the application and Start button also participates in navigation. When the user presses
reserves the space for the application bar and the system tray. the Start button, the running application is deactivated and a context
PhoneApplicationPage has page-specific notifications for when switch is performed as you navigate forward to the Start menu. From
a page is navigated to and when a user navigates away from a page. here a user can launch another application and navigate within the new
It also handles the events related to the hardware Back button. application, or he can choose to navigate back (using the hardware
Both PhoneApplicationFrame and PhoneApplicationPage share Back button) to the previously running application. This effectively
a NavigationService; this service is actually doing the navigation. creates a navigation model where the Back button navigates
Windows Phone supports journaling (tracking the history of the pages through the pages of a running application or through the stack of
you’ve loaded so you can go back to a previous page) and exposes APIs previously running application.
so you can go back. Forward navigation isn’t supported on the phone.
Windows Phone has three dedicated hardware buttons: Back, Windows Phone APIs
Start and Search. There are specific application-certification require- As noted earlier, the core players for navigation are Phone-
ments around handling of the hardware Back button: ApplicationFrame and PhoneApplicationPage.
• An application shouldn’t prevent the user from going back PhoneApplicationFrame acts as the RootVisual for the applica-
to a previous page. The only possible exception is a prompt tion. At startup, a PhoneApplicationFrame is instantiated in the
when data loss is involved–you can then prompt to confirm App class in App.xaml.cs (see Figure 1).
and let the user through if he chooses to navigate back. The runtime automatically navigates to the instance of Phone-
• If a popup, the Software Input Panel (SIP) or other transient ApplicationPage, which is specified by the NavigationPage
dialog is open, pressing the hardware Back button should
dismiss this dialog but not leave the current page (effec- Code download available at code.msdn.microsoft.com/mag201103Mobile.
tively canceling the Back button navigation).
82 msdn magazine
Figure 2 PhoneApplicationFrame Methods, Properties and Events
Name Type Description
Navigate Method Navigates to a new PhoneApplicationPage specified by the URI parameter. The parameter is a Uri, so a Navigate call
effectively instantiates the new page and navigates to it (you don’t pass it an already instantiated page).
CanGoBack Read-Only Returns true if the application’s back stack (the journaling history) isn’t empty. This means users have navigated forward
Property at least once within the app. If the application is in the first page loaded in the app, CanGoBack will return false and
you won’t be able to programmatically call GoBack, but the end user can still press the hardware Back button and the
application will exit because it’s going back to the previously running application.
CanGoForward Read-Only Not applicable to Windows Phone. It’s always false because forward navigation isn’t supported.
Property
UriMapper Property Gets/Sets a UriMapper. Beyond our scope for this article, but worth mentioning that Uri mapping is supported.
GoBack Method Navigates to the most recent entry in the back stack. This method will throw an exception if there’s no entry in the back
stack; always check CanGoForward before calling this method.
GoForward Method Not supported on Windows Phone; will throw InvalidOperationException
Navigating Event Occurs when a new navigation is requested. At this point, it can still be canceled by setting the Cancel property in
the NavigatingCancelEventArgs parameter to true. Please see notes later on about why you shouldn’t cancel back
navigations in this event.
Navigated Event Occurs when a navigation has been executed. This doesn’t mean that the page content that was navigated to has been
loaded. It simply occurs when the content has been found and navigated to.
NavigationFailed Event Occurs when an error has been encountered.
NavigationStopped Event Occurs when navigation is stopped by either calling the StopLoading method or more commonly when a new
navigation is requested and a navigation was in progress.

attribute in the DefaultTask on the WMAppManifest.xml application • Handling of the hardware Back button presses.
manifest, as shown here: • Providing events for page lifecycle to know when a page is
<Tasks>
<DefaultTask Name ="_default" NavigationPage="MainPage.xaml"/>
activated/deactivated.
</Tasks> For integration with the hardware Back button, PhoneApplication-
Getting a bit closer to responsibilities and APIs, PhoneApplication- Page exposes a BackKeyPress event. The page also has a virtual
Frame exposes most of the navigation methods and events we’ll OnBackKeyPress method you can override in your instance of a
need for this article. Figure 2 lists the most relevant methods, page to handle and even cancel a Back button press event.
properties and events in PhoneApplicationFrame. PhoneApplicationFrame has a Navigating event and an OnNavi-
Most are inherited from Frame, so for those familiar with the gatingFrom notification/callback. Within both of these, you can
Silverlight Frame class, these methods should look familiar. The cancel navigations to other pages within the app by setting e.Cancel
list in Figure 2 isn’t inclusive of all PhoneApplicationFrame = true in the NavigationCancelEventArgs parameter passed to these
features, just the relevant ones for navigation. methods; due to a known bug on the platform, you shouldn’t can-
Code Walk-Through: To see all these events and properties cel Back button navigations from these events/methods. If you do
in action, explore AllNavigations-
Events.xaml.cs in the sample code
accompanying this article. You can Page1 Page2 Page3
also see the order in which events
fire in Figure 3.
Navigate
PhoneApplicationFrame also (Page1.xaml) OnNavigationTo
determines the client area that the Loaded Navigate
application will get and reserves (Page2.xaml) OnNavigationTo
the space for the application bar OnNavigationFrom Loaded Navigate
and the system tray. This detail (Page3.xaml)
will be relevant as we navigate OnNavigationFrom OnNavigationTo
Loaded
across pages that have an appli-
GoBack
cation bar because it’s specified OnNavigationTo OnNavigationFrom
at the page level, and there’s a Loaded
system-wide animation to show OnNavigationTo
GoBack
OnNavigationFrom
and hide the application bar as a GoBack Loaded
page gets loaded. (Exits app)
The second participant in the nav- OnNavigationFrom
igation is PhoneApplicationPage. It
plays two critical roles in navigation: Figure 3 The Sequence of Events as You Navigate Across Pages
msdnmagazine.com March 2011 83
Figure 4 Events and Methods Where Navigations Can Be Canceled in the back stack. Also notice
Can Cancel a Can Cancel Back that NavigatedTo always fires
Owner Event/Notification New Navigation Navigations Check for Back Navigations before a page is loaded, so don’t
PhoneApplicationFrame Navigating Yes No Yes; check e.NavigationMode assume the contents of the
!= NavigationMode.Back page are loaded at this time.
PhoneApplicationPage OnNavigatingFrom Yes No Yes; check e.NavigationMode The reason OnNavigatedTo
!= NavigationMode.Back and OnNavigatedFrom are
PhoneApplicationPage OnBackKeyPress No (called only Yes Not needed; only called on critical to Windows Phone is
when Back key hardware Back key press because of the back stack. The
press is called) OS maintains the back stack
PhoneApplicationPage BackKeyPress No (called only Yes Not needed; only called on for pages you can go back to, so
(event) when Back key hardware Back key press
pages aren’t immediately un-
press is called)
loaded, destroyed or garbage
collected when a navigation
cancel a hardware Back button press in this event, your navigation happens from one page to another. Instead, the pages are moved to
will break and the application will need to be restarted. The only the back stack and kept alive (in memory), and when the user clicks
two recommended methods for canceling a hardware Back but- back to get to that page, the page is simply added back into the visual
ton press are the PhoneApplicationPage BackKeyPress event and tree. The page isn’t recreated (unless the application has been deacti-
the OnBackKeyPress callback. vated and tombstoned between when the user left the page and clicked
See Figure 4 for a list of events and methods where navigations back). Because forward journaling isn’t supported, pages are eligible
can be canceled, with recommendations on whether a Back press for garbage collection when you navigate from a page back to the
can be canceled in that method, and advice on how to check if the previous page—assuming there are no other references to this page.
event was a back navigation. Figure 5 shows a diagram that illustrates a PhoneApplication-
PhoneApplicationPage complements these events to complete Page lifecycle.
the navigation lifecycle with the more useful OnNavigatedTo and As you navigate from Page1 to Page2 and then Page3, pages aren’t
OnNavigatedFrom method callbacks for the page. To better under- garbage collected until you call the GoBack method from the page.
stand and easily remember when these callbacks are called, it’s best Inactive pages are in the back stack, but still in memory. If these
to complete their method names with a “this page.” One method pages are listening to global events, the event listeners are still active.
is called when the user has “navigated to this page,” and later the Despite a page not getting garbage collected when you navigate
other method gets called when the user is “navigated from this from it, the page is no longer visible or active until you navigate
page” onto another page. back to it, so you should make sure you do any cleanup and release
Figure 3 shows the sequence of events as you navigate across any expensive resources when the user has navigated away from
pages. The symmetry between NavigatedTo/NavigatedFrom makes a page. For example, if you’re listening to location changes using
these two methods ideal for starting and stopping work that’s GeoCoordinateWatcher, you should stop the listener on the
required when the page is visible, but not required when the page is OnNavigatedFrom and restart it when the user navigates back to
your page–and your page OnNavigatedTo is called.
Code Walk-Through: To see how pages are retained in
Page1 Page2 Page3 memory while they’re in the back stack, explore the Garbage-
CollectedSample page included in the accompanying code
Navigate download. It keeps a running tally of pages in memory, and you
(Page1.xaml)
can see it increase as you navigate forward and decrease as you
Navigate navigate back from a page.
(Page2.xaml) That wraps up the first part of our series. Next month, we’ll
focus on advanced navigation topics. „
Navigate
(Page3.xaml)
YOCHAY KIRIATY is a senior technical evangelist at Microsoft, focusing on client
technologies such as Windows and Windows Phone. He coauthored the books
GoBack “Introducing Windows 7 for Developers” (Microsoft Press, 2009) and “Learning
Windows Phone Programming” (O’Reilly Media, 2011).

GoBack
J AIME R ODRIGUEZ is a principal evangelist at Microsoft driving adoption
of emerging client technologies such as Silverlight and Windows Phone. You
GoBack can reach him on Twitter: @jaimerodriguez or on blogs.msdn.com/jaimer.
(Exits app)

THANKS to the following technical expert for reviewing this article:


Figure 5 The PhoneApplicationPage Lifecycle Peter Torr

84 msdn magazine Mobile Matters


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
nnn%;peXd`ZG;=%Zfd&\mXcfiZXcc/''%-/(%,''/s"(+('%..)%/-)'

Untitled-3 1 12/7/10 3:43 PM


TEST RUN JAMES MCCAFFREY

Diffusion Testing

In this month’s column, I introduce you to a software-testing tech-


nique I call diffusion testing. The key idea of diffusion testing is that
it’s sometimes possible to automatically generate new test case data
from existing test cases that yield a pass result.
Although diffusion testing is a technique that isn’t applicable in
most software-testing scenarios, when diffusion testing is applicable,
it can greatly improve the efficiency of your test effort. Perhaps in
part because it’s a niche technique, diffusion testing is one of the
least known of all major testing techniques, based on my experience.
Before I present examples of diffusion testing, let me explain the
motivation behind the method. A test case typically consists of a
test case ID, a set of one or more inputs and an expected result.
For example, the vector {001, 2, 3, 5} could represent a test case for
a Sum function, with ID = 001, inputs = 2 and 3 and an expected
result = 5. The test case inputs are sent to the system under test, an
actual result is produced and the actual result is compared to the
expected result to determine a test case pass/fail result.
In many software-testing situations, it’s difficult and time-
consuming to determine the expected result part of a test case.
For example, suppose you’re testing a basic math function that
computes the harmonic mean of two inputs that are rates. The
average of 30.0 kilometers per hour (kph) and 60.0 kph is not (30.0
Figure 1 Diffusion Testing Demo
+ 60.0) / 2 = 45.0 kph, but rather the harmonic mean of 30.0 and
60.0, which is 1 / ((1/30.0 + 1/60.0) / 2) = 40.0 kph. Generating Automatically generating test case data with diffusion testing is
hundreds of expected results for this function would be tedious, great in principle, but how does it work? The best way to explain
take a lot of time and be prone to error. diffusion testing is by way of an example. Take a look at Figure 1.
Here, I’m testing a function, Choose(n,k), which returns the
number of ways to select k items from n items where order doesn’t
When diffusion testing is matter. In my simplified example, I have three existing test cases.
The first test case has inputs n = 8 and k = 3 and an expected
applicable, it can greatly improve result of 56. After my test harness executed the first test case, which
yielded a pass result, I used diffusion testing to automatically gen-
the efficiency of your test effort. erate a new test case with inputs n = 9, k = 3 and an expected result
of 84. Neat! Notice that because test case 002 yielded a fail result,
I didn’t generate a new diffused test case.
The difficulty of determining test case expected results is a But how are new test cases generated from an existing test case?
fundamental concept in software testing and is sometimes referred For the Choose(n,k) function, it turns out that, mathematically,
to as the test oracle problem. In fact, one of the holy grails of soft- Choose(n+1,k) = Choose(n,k) * (n+1) / (n-k+1). In other words, there’s
ware testing is the search for techniques that can automatically a known relationship between new inputs and old return values. The
determine test cases. So the motivation behind diffusion testing function I used to generate a diffused test case from an existing test case
is that, if you can somehow automatically generate new test case
data, you’ll sidestep a time-consuming part of the testing process Code download available at code.msdn.microsoft.com/mag201103TestRun.
and be able to test your system more thoroughly.

86 msdn magazine
Figure 2 Generating a New Test Case one that isn’t diffusion testing, is when you’re testing a function
static string CreateDiffusedTestCase(string existingTestCase)
where switching the order of input values doesn’t change the return
{ value, such as with Sum(x,y).
// Assumes input format is CaseID:N:K:Expected
string[] tokens = existingTestCase.Split(':');
Mathematical functions are the most common type of component
under test that can benefit from diffusion testing, because such func-
string oldTestCase = tokens[0];
int oldN = int.Parse(tokens[1]); tions most often are recurrent, monotonic or symmetric—but you
int oldK = int.Parse(tokens[2]); should be alert to other situations, too. Mathematical functions that
long oldExpected = long.Parse(tokens[3]);
involve recurrence relations are especially well-suited for diffusion
string newTestCase = oldTestCase + "-diffused"; testing because you can often generate multiple new test cases from
int newN = oldN + 1;
int newK = oldK; an existing test case. In the demo in Figure 1, test case 001 with n =
long newExpected = (oldExpected * (oldN + 1)) / (oldN - oldK + 1); 8, k = 3 and expected = 56 generated a new diffused test case with
return newTestCase + ":" + newN + ":" + newK + ":" + newExpected; n = 9, k = 3 and expected = 84. This new test case could be used to
} generate another test case with n = 10, k = 3 and expected = 120, and
if that test case passed, it could be used to generate yet another new
is shown in Figure 2. The entire program that generated the output test case, and so on.
shown in Figure 1 is available at code.msdn.microsoft.com/mag201103TestRun.

If you can somehow


A couple of additional examples may help to make this idea clearer.
Suppose you’re testing functions that compute the trigonometric

automatically generate new test


sine and cosine. You may recall that sin 2t = 2 * sin t * cos t. So if
you have test cases that yield pass results for the sine and cosine

case data, you’ll sidestep a time-


of some input, you could use diffusion testing to derive a new test
case for the sine function.

consuming part of the testing


Diffusion testing isn’t magic. Suppose you’re testing a function
that accepts a product ID of some sort, searches a SQL database and

process and be able to test your


returns true if the product is in stock and false if the product isn’t in
stock. Because there’s no relationship between different inputs and

system more thoroughly.


results, you couldn’t use diffusion testing in this scenario. In this
respect, diffusion testing is similar to other forms of testing such as
boundary condition testing and pairwise testing: It’s a technique that’s
applicable only in certain situations. Before I wrap up, let me jump on my soapbox and address a
Let me present another example of diffusion testing. Suppose pet peeve of mine related to naming different software-testing
you’ve written a function, Gauss(z), which accepts a standard normal techniques and principles. I’ve labeled the technique described in
z value and which returns the area under the standard normal (bell- this column as diffusion testing because existing test cases diffuse,
shaped curve) distribution from negative infinity to z. For example, or scatter, to create new cases. I could just as well have called the
Gauss(-1.645) = 0.0500, Gauss(1.645) = 0.9500 and Gauss(0) = 0.5000. technique adaptive testing or auto-generation testing or any of a
One way to use diffusion testing is to note the monotonic property of number of other things. It’s not the label that’s important, but rather
Gauss and that for any z value in the range negative infinity to 2.5, the the technique represented by the label that counts.
result of Gauss(z + 0.1) must be greater than Gauss(z). Another way In many fields of study, including software testing, self-proclaimed
to use diffusion testing is to note the symmetric property of Gauss experts apply some label to a common-sense technique and implic-
and that for any z value that’s less than 0.0, Gauss(-z) = 1.0 - Gauss(z). itly attempt to convince people new to the field that the label itself
somehow carries some importance. This is typically motivated by the

Diffusion testing is one


desire to directly sell training or indirectly sell consulting services by
delivering a talk at a conference on the marvelous new label. Notable

of the least known of all major


offenders are the terms “exploratory testing” and “context school of
testing,” but there are many others. So take the term “diffusion testing”

testing techniques.
for what it is—simply a label to describe a software-testing technique,
but one that can be a useful addition to your technical toolkit. „

The examples I’ve presented illustrate the three most common— DR. JAMES MCCAFFREY works for Volt Information Sciences Inc., where he man-
but by no means the only—scenarios where diffusion testing is ages technical training for software engineers working at the Microsoft Redmond,
applicable. The first scenario is where you’re testing a mathematical Wash., campus. He’s worked on several Microsoft products, including Internet
Explorer and MSN Search. Dr. McCaffrey is the author of “.NET Test Automation
function that can be defined as a recurrence relationship. The second Recipes” (Apress, 2006), and can be reached at jammc@microsoft.com.
scenario is where you’re testing a function that has some monotonic
relationship. And the third scenario is where you’re testing a function THANKS to the following technical experts for reviewing this article:
that has some symmetric relationship. A related form of testing, but Bj Rollison and Alan Page

msdnmagazine.com March 2011 87


THE WORKING PROGRAMMER TED NEWARD

Multiparadigmatic .NET, Part 6:


Reflective Metaprogramming
The marathon continues. In my previous installment we discussed spond on a one-to-one basis with the columns of a relational table
automatic metaprogramming, and by this point the ideas of and the properties are publicly accessible. You could write a pro-
commonality and variability should be taking on a familiar feel. cedure to take instances of Person, extract the bits of data, inject
I’m now full-on talking about metaprogramming—the idea of those bits into SQL statements, and send the resulting statement
programs writing programs. to the database:
Last month I examined one of the more familiar approaches to class DB {
public static bool Insert(Person p) {
metaprogramming: automatic metaprogramming, more commonly // Obtain connection (not shown)
known as code generation. In an automatic metaprogramming // Construct SQL
string SQL = "INSERT INTO person VALUES (" +
scenario, developers write programs that describe the “things” to be "'" + p.FirstName + "', " +
generated. Usually this is done with the aid of command-line param- "'" + p.LastName + "', " +
p.Age + ")";
eters or other inputs such as relational database schemas, XSD files // Send resulting SQL to database (not shown)
or even Web Services Description Language (WSDL) documents. // Return success or fail
return true;
Because code generation is essentially “just as if ” the code were }
written by human hands, variability can come at any point inside }
the code. Data types, methods, inheritance ... all of it can be varied The drawback to a procedural approach rears its ugly head fairly
according to need. The drawback, of course, is twofold. First, too quickly: new types (Pet, Instructor, Student and so on) that also
much variability can render the generated code (and, more often want to be inserted will require new methods of mostly similar
than not, the templates by which the code is generated) difficult to code. Worse, if properties exposed to the public API don’t corre-
understand. Second, the generated artifacts are essentially uneditable spond one-to-one with columns or internal fields, things can get
unless the code generation is somehow partitioned away through complicated quickly—developers writing the SQL routines will
the use of partial classes or code generation is no longer necessary. need to know which fields need persisting and which don’t, a pretty
Fortunately, C# and Visual Basic offer more metaprogrammatic clear violation of encapsulation.
tactics than just automatic metaprogramming, and have done so From a design perspective, the object-relational problem wants
since the earliest days of the Microsoft .NET Framework. to capture the SQL-esque parts of persisting the data into common-
ality, so that managing database connections and transactions is
Persistent Problems handled in one place, but still allows for variability in the actual
A recurring problem in the object-oriented environment is that of structure of the things being persisted (or retrieved).
object-to-relational persistence—also known as object-to-XML Recall, from our earlier investigations, that procedural approaches
conversion or, in the more modern Web 2.0 world, object-to-JSON capture algorithmic commonality, and that inheritance captures
transformation. Despite developers’ best efforts, it seems inevitable structural commonality while allowing for (positive) variability—
that object models need to escape the CLR somehow and either but neither of these exactly do what’s needed. The inheritance
move across the network or move onto disk and back again. And approach—putting the commonality into a base class—will require
herein lies the problem: the previous modes of design—procedural that developers working in derived classes specify the SQL string
and object-oriented—don’t offer good solutions for this dilemma. and handle much of the in/out bookkeeping (essentially pushing
Consider a canonical, simplified representation of a human being commonality back down into derived classes). The procedural
modeled in code: approach will need some kind of variability inside the procedure
class Person { (extracting and building the SQL to execute) specified from outside
public string FirstName { get; set; }
public string LastName { get; set; }
the procedure, which turns out to be relatively difficult to achieve.
public int Age { get; set; }

public Person(string fn, string ln, int a) { Enter Metaprogramming


FirstName = fn; LastName = ln; Age = a;
}
One solution to the object-relational persistence problem frequently
} cited is that of automatic metaprogramming: using the database
Persisting instances of this object, as it turns out, is not difficult, schema, create classes that know how to persist themselves to and
particularly because the properties (in their simplest form) corre- from the database.
88 msdn magazine
Unfortunately, this has all the traditional problems of code genera- as @NotSerialized in the case of object serialization. It’s important
tion, particularly when classes want to change the object representation to note, however, that the attribute itself does nothing—it’s merely
to be something easier to work with than what the physical database a flag to the code looking for that attribute. Of itself, then, the
schema implies. For example, a VARCHAR(2000) column would be attribute provides no negative variability, but merely makes it easier
much easier to work with if it were a .NET Framework System.String to denote when that negative variability should kick in.
and not a char[2000]. Attributes can also be used to convey positive variability. One
Other code-generation techniques started from the class defini- example is how the .NET Framework uses attributes to convey
tions and created a database schema alongside the persistent class transactional handling, assuming that the lack of an attribute on a
definitions … but that meant that somehow now the object hierarchy method indicates no transactional affinity whatsoever.
was duplicated into two different models, one solely for persistence
and one for everything else. (Note that as soon as it becomes neces-
sary to transform the object into XML, another hierarchy springs Now names can be used to refer
into being that you have to handle, and yet another one to handle
JSON. Quickly this approach grows intractable.) to elements within the program.
Fortunately, reflective metaprogramming offers potential relief.
A part of the .NET Framework since 1.0, System.Reflection allows Mirror, Mirror, on the Wall
developers to examine the structure of objects at run time, which Without attributes, reflective metaprogramming establishes an entirely
in this case permits the persistence-minded infrastructure the new kind of variability. Now names can be used to refer to elements
opportunity to examine the structure of the object being persisted within the program (rather than through compiler symbols)—and at
and generate the SQL required from there. A basic introduction to a much later time (runtime) than the compiler traditionally permits.
System.Reflection is well-documented both within the MSDN For example, early drops of the NUnit unit-testing framework, like
documentation at msdn.microsoft.com/library/f7ykdhsy(v=VS.400) and in the its cousin JUnit in the Java space, used reflection to discover methods
MSDN Magazine articles “Use Reflection to Discover and Assess the that began with “test” as part of the name, and assumed that they were
Most Common Types in the .NET Framework” (msdn.microsoft.com/ test methods to execute as part of a test suite.
magazine/cc188926) and “CLR Inside Out: Reflections on Reflection” The name-based approach requires developers to take elements
(msdn.microsoft.com/magazine/cc163408). I won’t discuss it any further here. traditionally reserved for human eyes—the names of things—and
Reflection permits commonality of algorithm, while allowing require them to follow strict conventions, such as the “test” prefix for
for variability of structure manipulated by that algorithm, all while NUnit methods. The use of custom attributes relaxes that naming-
continuing to preserve the appearance of encapsulation. Because based convention (at the expense of now requiring additional code
reflection (in an appropriately configured security context) has constructs in the classes in question), essentially creating an
access to private members of objects, internal data can still be opt-in mechanism that developers must accept in order to receive
manipulated without forcing those data members to be made public. the benefits of the metaprogram.
Positive variability—the ability to vary by adding things—is, as Attributes also provide the ability to tag arbitrary data along with
always, easy to work with, as the number of fields is largely irrelevant the attribute, providing a much more fine-grained parameterization
to most reflection-based code. Negative variability—the ability to vary to the metaprogrammatic behavior. This is something not typically
by removing things—doesn’t seem to fit at all, however. After all, a class possible with automatic metaprogramming, particularly not when
without fields doesn’t really need to be persisted, does it? And a reflection- the client wants different behavior for structurally similar constructs
based infrastructure looping through private fields won’t have much of (such as the strings-to-BLOBs-instead-of-VARCHAR-columns
a problem not looping at all, as nonsensical as that may seem. example from earlier).
However, negative variability here is slightly different than just Owing to its runtime-bound nature, however, reflection fre-
not having fields. In certain scenarios, the Person class will have quently enforces a performance hit on code that uses it extensively.
internal fields that don’t want to be persisted at all. Or, more strik- In addition, reflection doesn’t offer solutions to the problems cited
ingly, the Person class will have fields it wants persisted in a different in the automatic metaprogramming scenario from last month—the
data format than its CLR-hosted representation. Person.Birthdate proliferation of classes, for example. Another metaprogrammatic
wants to be stored as a String, perhaps, or even across three columns solution is available, but that will have to wait for next month.
(day, month, year) rather than in a single column. In other words, Happy coding! „
negative variability in a reflective metaprogrammatic sense is not
about the lack of fields, but about doing something different to TED NEWARD is a Principal with Neward & Associates, an independent firm
certain instances of types that would otherwise be handled in a specializing in enterprise Microsoft .NET Framework and Java platform systems.
standard way (persisting a string to a VARCHAR column being He’s written more than 100 articles, is a C# MVP and INETA speaker and has
authored and coauthored a dozen books, including “Professional F# 2.0” (Wrox,
the standard, for example, but for one or more particular fields, 2010). He also consults and mentors regularly. Reach him at ted@tedneward.com
persisting a string to a BLOB column). with questions or consulting requests, and read his blog at blogs.tedneward.com.
The .NET Framework makes use of custom attributes to convey
this negative variability. Developers use attributes to tag elements THANKS to the following technical expert for reviewing this article:
within the class to convey the desire for that custom handling, such Anthony Green
msdnmagazine.com March 2011 89
AGENDA
VISUAL STUDIO LIVE! LAS VEGAS TRACKS
Visual Studio
Programming Data Web/ "Simplification" Mobile
Silverlight/WPF 2010/ WCF Cloud Computing
Practices Management HTML 5 Tools Development
.NET 4

Visual Studio Live! Pre-Conference Workshops: Monday, April 18, 2011


(Separate entry fee required)
MWK1 An Introduction to Multi-Platform Mobile MWK2 Workshop: Making Effective Use of MWK3 Workshop: Programming with WCF in
Development Using C#: iPhone, Android, and Silverlight and WPF Billy Hollis & Rockford Lhotka One Day Miguel Castro
Windows Phone 7 Ken Getz & Brian Randell
Visual Studio Live! Day 1: Tuesday, April 19, 2011
T1 Easing in to Windows Phone 7 T2 Getting Started with ASP.NET MVC T3 Azure Platform Overview T4 Best Kept Secrets in Visual Studio
Development Walt Ritscher Philip Japikse Vishwas Lele 2010 and .NET 4.0 Deborah Kurata
T5 Silverlight in 75 Minutes T6 Test Driving ASP.NET MVC2 T7 Building Azure Applications T8 How We Do Language Design at
Ken Getz Philip Japikse Vishwas Lele Microsoft Lucian Wischik
TCT1 Chalk Talk: Silverlight, WCF RIA Services, and TCT2 Chalk Talk: Building N-Tier Applications With TCT3 Chalk Talk: Join the XAML Revolution
Your Business Objects Deborah Kurata Entity Framework 4 Leonard Lobel Billy Hollis
T9 Transitioning from Windows Forms T10 HTML5/IE9 inspire T11 Building Compute-Intensive Apps T12 Turn Your Development Up to 11:
to WPF Miguel Castro in Azure Vishwas Lele Debugging to Win with Visual Studio
2010 Brian Randell
T13 Programming for Windows 7 with T14 Improving Your ASP.NET T15 Using C# and Visual Basic to T16 Designing and Developing for the
WPF Miguel Castro Application Performance with Build a Cloud Application for Windows Rich Mobile Web Joe Marini
Asynchronous Pages and Actions Phone 7 Lucian Wischik &
Tiberiu Covaci Srivatsn Narayanan
Visual Studio Live! Day 2: Wednesday, April 20, 2011
W1 Bind Anything to Anything in W2 HTML 5 and Your Web Sites W3 How to Make Your Application W4 Visual Studio LightSwitch—
Silverlight and WPF Rockford Lhotka Robert Boedigheimer Awesome with JSON, REST, WCF and Beyond the Basics Robert Green
MVC James Bender
W5 Design, Don't Decorate W6 Styling Web Pages with CSS 3 W7 RESTBuilding RESTful Services in W8 Advanced LightSwich
Billy Hollis Robert Boedigheimer the Microsoft Platform: When to Use Development Michael Washington
What? Jesus Rodriguez
WCT1 Chalk Talk: CSLA .NET Rockford Lhotka WCT2 Chalk Talk: Busy Developer’s Guide to (ECMA/Java)Script Ted Neward
W9 Leveraging the MVVM Pattern in W10 HTML5 Messaging, Web W11 WCF Workflow Services W12 The Almighty @— A Razor
Silverlight, WPF and Windows Phone Workers and Web Sockets with Rob Daigneau Primer Charles Nurse
Rockford Lhotka JavaScript Jeffrey McManus
W13 Top 7 Lessons Learned On My W14 jQuery Application Development W15 WCF Tips & Tricks – From the W16 WebMatrix Real World Data-
First Big Silverlight Project Jeffrey McManus Field Christian Weyer Centric Applications Charles Nurse
Benjamin Day
Visual Studio Live! Day 3: Thursday, April 21, 2011
TH1 Multi-touch Madness! Brian Peek TH2 The Best of jQuery TH3 Making WCF Simple: Best TH4 Digging Deeper in Windows
Robert Boedigheimer Practices for Testing, Deploying and Phone 7 Walt Ritscher
Managing WCF Solutions in the Big
Enterprise Jesus Rodriguez
TH5 XAML Primer Clarifying the UI TH6 Single Sign-On for ASP.NET TH7 How to Take WCF Data Services TH8 C# on Android: Building Android
Markup Language Walt Ritscher Applications Dominick Baier to the Next Level Rob Daigneau Apps with .NET Christian Weyer
TH9 Silverlight Security TH10 The Scrum vs. Kanban Cage TH11 Busy .NET Developer's Guide TH12 C# on Android: Building Android
Dominick Baier Match Benjamin Day & David Starr to Parallel Extensions for .NET 4 Apps with .NET Christian Weyer
Ted Neward
TH13 LINQ Programming Model TH14 Patterns of Healthy Teams using TH15 Designing Applications in the TH16 XNA Games for Windows
Marcel de Vries Visual Studio and TFS David Starr Era of Many-Core Computing Tiberiu Phone 7 Brian Peek
Covaci
TH17 So Many Choices, So Little TH18 Produce Better Quality Code TH19 Building Event-Driven TH20 Windows Azure and PHP
Time: Understanding Your .NET 4.0 by Leveraging the Visual Test Tools Applications with Microsoft Jeffrey McManus
Data Access Options Leonard Lobel You Never Discovered Before StreamInsight Torsten Grabs
Marcel de Vries
Visual Studio Live! Post-Conference Workshops: Friday, April 22, 2011
(Separate entry fee required)
FWK1 Architectural Katas Workshop Ted Neward FWK2 SQL Server 2011 Andrew Brust & Leonard Lobel

VISIT US ONLINE AT VSLIVE.COM/LV FOR DETAILS ON SESSIONS, SPEAKERS, AND MORE!

Untitled-6 2 2/2/11 2:01 PM


APRIL 18–22, 2011
LAS VEGAS, NEVADA
RIO ALL-SUITE HOTEL & CASINO

THE REAL-WORLD
TRAINING YOU NEED.
If you’re looking for hard-hitting .NET
development training, look no further
than Visual Studio Live! Las Vegas.
Our goal? To arm you with the knowledge
to build better applications.

CHECK OUT THE FULL 50+ SESSION SCHEDULE NOW!

VISUAL STUDIO LIVE! LAS VEGAS is 5 days packed with full


day workshops, keynotes from industry heavy weights and your choice
of 50 hard-hitting sessions.

You'll learn tips, tricks and fixes from .NET pros like Billy Hollis,
Rockford Lhotka, Andrew Brust, Deborah Kurata and Dave Mendlen,
Senior Director, Developer Platform and Tools at Microsoft.

DOWNLOAD
THE AGENDA
REGISTER BY MARCH 23
NOW! SAVE $200!
USE CODE MARAD

SUPPORTED BY:

PRODUCED BY:

WWW.VSLIVE.COM/LV
Untitled-6 3 2/2/11 2:01 PM
UI FRONTIERS CHARLES PETZOLD

Touch Gestures on Windows Phone 7

As someone who spends much of his professional life observing the us a “sneak peek” at enhancements to the frameworks that might be
evolution of APIs, I’ve been quite entertained by that little corner incorporated in future releases. Full source code is an extra bonus.
of the API universe occupied by multi-touch. I’m not sure I’d even Windows Phone 7 now also benefits from this custom. The Silverlight
want to count the number of different multi-touch APIs spread out for Windows Phone Toolkit (available at silverlight.codeplex.com) contains
over Windows Presentation Foundation (WPF), Microsoft Surface, DatePicker, TimePicker and ToggleSwitch controls already familiar
Silverlight, XNA and Windows Phone, but what’s most evident is to users of Windows Phone 7; a WrapPanel (handy for dealing with
that a “unified theory” of multi-touch is still elusive. phone orientation changes); and multi-touch gesture support.
Of course, this plethora of touch APIs shouldn’t be surprising for

I’ve been quite


a technology that’s still comparatively young. Moreover, multi-touch
is more complex than the mouse. That’s partially due to the potential

entertained by that little corner


interaction of multiple fingers, but it also reflects the difference
between a purely artificial device such as the mouse and all-natural

of the API universe occupied


fingers. We humans have a lifetime of experience using our fingers,
and we expect them to interact with the world in well-known ways,

by multi-touch.
even if we’re touching the glossy surface of a video display.
For the application programmer, Windows Phone 7 defines
four—yes, four—different touch interfaces.
Silverlight applications written for Windows Phone 7 have This new Silverlight gesture support in the toolkit is intended to
the option of obtaining low-level touch input through the static be similar to the XNA TouchPanel.ReadGesture method, except
Touch.FrameReported event, or higher-level input through the it’s delivered through routed events rather than polling.
various Manipulation routed events. These Manipulation events How similar is it? Much more so than I expected! Looking at
are mostly a subset of similar events in WPF, but they’re different the source code, I was quite surprised to discover that these new
enough to cause major headaches. Silverlight gesture events were entirely derived from a call to the
XNA applications for Windows Phone 7 use the static TouchPanel XNA TouchPanel.ReadGesture method. I wouldn’t have thought
class to obtain touch input, but that single class actually incorpo- that a Silverlight application on Windows Phone was allowed to
rates two touch interfaces: The GetState method obtains low-level call this XNA method, but there it is.
finger activity, and the ReadGesture method obtains higher-level Although the Silverlight and XNA gestures are fairly similar, the
gestures. The gestures supported by the ReadGesture method are properties associated with the gestures are not. The XNA properties
not stylus-like gestures such as checkmarks and circles. They’re use vectors, for example, and because Silverlight doesn’t include a
much simpler gestures described by names such as Tap, Drag and Vector structure (an omission I feel is ridiculous), the properties
Pinch. In keeping with XNA architecture, touch input is polled by had to be redefined for Silverlight in certain simple ways.
the application rather than being delivered through events. As I’ve been working with these gesture events, they’ve come to
be my favorite multi-touch API for Silverlight for Windows Phone.
Gestures Come to Silverlight I’ve found them to be comprehensive for much of what I need to
I naturally assumed that Silverlight for Windows Phone 7 already do and also fairly easy to use.
had a sufficient number of multi-touch APIs, so I was quite surprised Let me demonstrate by giving these gestures actual work to do.
to see a third one added to the mix—albeit in a toolkit that came
out a little too late for me to describe in my book, “Programming Gesture Service and Listener
Windows Phone 7” (Microsoft Press, 2010). All the source code for this column is in a downloadable Visual Studio
As you probably know, various releases of WPF and Silverlight solution named GestureDemos that contains three projects. You’ll
over the past several years have been supplemented by toolkits
released through CodePlex. These toolkits allow Microsoft to get new Code download available at code.msdn.microsoft.com/mag201103UIFrontiers.
classes to developers outside of the usual ship cycle and often give

92 msdn magazine
need to have the Windows Phone 7 development tools installed, of Figure 1 The Image Element in ScaleAndRotate
course, and also the Silverlight for Windows Phone Toolkit. <Image Name="image"
After installing the toolkit, you can use it in your own Windows Source="PetzoldTattoo.jpg"
Stretch="None"
Phone projects by adding a reference to the Microsoft.Phone.Con- HorizontalAlignment="Left"
trols.Toolkit assembly. In the Add Reference dialog box, it should VerticalAlignment="Top">
<Image.RenderTransform>
be listed under the .NET tab. <TransformGroup>
In a XAML file, you’ll then need an XML namespace declaration <MatrixTransform x:Name="previousTransform" />
like this one (but all on one line): <TransformGroup x:Name="currentTransform">
xmlns:toolkit=
<ScaleTransform x:Name="scaleTransform" />
"clr-namespace:Microsoft.Phone.Controls;
<RotateTransform x:Name="rotateTransform" />
assembly=Microsoft.Phone.Controls.Toolkit"
<TranslateTransform x:Name="translateTransform" />
Here are the 12 available gesture events, roughly in the order that </TransformGroup>
</TransformGroup>
I’ll discuss them (the events that I’ve grouped on a single line are </Image.RenderTransform>
related and occur in a sequence): </Image>
GestureBegin, GestureCompleted
Tap
DoubleTap fingers have left the screen. These events may be handy for initial-
Hold ization or cleanup, but you’ll generally be more focused on gesture
DragStarted, DragDelta, DragCompleted
Flick events that occur between these two events.
PinchStarted, PinchDelta, PinchCompleted I’m not going to spend much time on the simpler gestures. A
Suppose you want to handle Tap and Hold events that occur on Tap occurs when a finger touches the screen and then lifts up
a Grid or any child of the Grid. You can specify that in the XAML within about 1.1 seconds, without moving too far from the original
file like so: position. If two taps are close in succession, the second one comes
<Grid ... > through as a DoubleTap. A Hold occurs when a finger is pressed
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener on the screen and remains in roughly the same spot for about
Tap="OnGestureListenerTap" 1.1 seconds. The Hold event is generated at the end of this time
Hold="OnGestureListenerHold" />
</toolkit:GestureService.GestureListener> without waiting for the finger to lift.
...

Drag and Flick


</Grid>
You indicate the events and handlers in a GestureListener
A Drag sequence—consisting of a DragStarted event, zero or more
tag that’s a child of the GestureListener attached property of the
DragDelta events and a DragCompleted event—occurs when a
GestureService class.
finger touches the screen, moves and lifts. Because it isn’t known
Alternatively in code, you’ll need a namespace directive for the
that dragging will occur when a finger first touches the screen,
Microsoft.Phone.Controls namespace and the following code:
GestureListener gestureListener =
the DragStarted event is delayed until the finger actually starts
GestureService.GetGestureListener(element); moving beyond the Tap threshold. The DragStarted event might
gestureListener.Tap += OnGestureListenerTap;
be preceded by a Hold event if the finger has been on the screen
gestureListener.Hold += OnGestureListenerHold; without moving for about a second.
In either case, if you’re setting this gesture listener on a panel, make

We humans have a lifetime


sure that the Background property is at least set to Transparent! Events
will simply fall through a panel with a default background of null.

Tap and Hold of experience using our


fingers, and we expect them
All gesture events are accompanied by event arguments of type
GestureEventArgs or a type that derives from GestureEventArgs. The

to interact with the world in


OriginalSource property indicates the top-most element touched by
the first finger that meets the screen; the GetPosition method pro-

well-known ways.
vides the current coordinates of that finger relative to any element.
The gesture events are routed, which means that they can travel
up the visual tree and be handled for any element that has a
GestureListener installed. As usual, an event handler can set the Because the finger has already begun moving when the Drag-
Handled property of GestureEventArgs to true to prevent an event Started event is fired, the DragStartedEventArgs object can include
from travelling further up the visual tree. However, this only affects a Direction property of type Orientation (Horizontal or Vertical).
other elements using these gesture events. Setting Handled to true The DragDeltaEventArgs object accompanying the DragDelta
does not prevent elements higher in the visual tree from obtaining event includes more information: HorizontalChange and Vertical-
touch input through other interfaces. Change properties that are convenient for adding to the X and
The GestureBegin event indicates that a finger has touched a Y properties of a TranslateTransform, or the Canvas.Left and
previously fingerless screen; GestureCompleted signals when all Canvas.Top attached properties.
msdnmagazine.com March 2011 93
The Flick event occurs when a finger leaves the screen as it’s to entirely reconstruct the positions of the two fingers, so you can
still moving, suggesting that the user wants inertia to occur. The always go back to first principles if you need to.
event arguments include an Angle (measured clockwise from During a Pinch sequence, the current location of one finger—let’s
the positive X axis) and HorizontalVelocity and VerticalVelocity call it the primary finger—is always available with the GetPosition
values, both in pixels per second. method. For this discussion, call that return value pt1. For the
The Flick event can occur in isolation; or it can occur between PinchStarted event, the PinchStartedGestureEventArgs class has
DragStarted and DragCompleted events without any DragDelta two additional properties named Distance and Angle indicating
events; or it might follow a series of DragDelta events before the location of the second finger relative to the first. You can easily
DragCompleted. Generally you’ll want to handle Drag events and calculate that actual location using the following statement:
Flick events in conjunction, almost as if the Flick is a continuation Point pt2 = new Point(pt1.X + args.Distance * Cos(args.Angle),
pt1.Y + args.Distance * Sin(args.Angle));
of the Drag. However, you’ll need to add your own inertia logic.
The Angle property is in degrees, so you’ll need Cos and Sin
methods to convert to radians before calling Math.Cos and
Although the Silverlight and XNA Math.Sin. Before the PinchStarted handler has completed, you’ll
also want to save the Distance and Angle properties in fields,
gestures are fairly similar, the perhaps named pinchStartDistance and pinchStartAngle.
The PinchDelta event is accompanied by a PinchGestureEvent-
properties associated with the Args object. Once again, the GetPosition method gives you the
location of the primary finger, which has perhaps moved from
gestures are not. its original location. For the second finger, the event arguments
provide DistanceRatio and TotalAngleDelta properties.
This is demonstrated in the DragAndFlick project. The display The DistanceRatio is the ratio of the current distance between
contains an ellipse that the user simply drags around with a finger. If the fingers to the original distance, which means you can calculate
the finger leaves the screen with a flicking motion, then a Flick event the current distance like so:
double distance = args.DistanceRatio * pinchStartDistance;
occurs and the Flick handler saves some information and installs a
The TotalAngleDelta is a difference between the current angle
handler for the CompositionTarget.Rendering event. This event—
between the fingers and the original angle. You can calculate the
which occurs in synchronization with the video display refresh—keeps
current angle like this:
the ellipse moving while applying a deceleration to the velocity. double angle = args.TotalAngleDelta + pinchStartAngle;
Bouncing off the sides is handled a bit unusually: The program Now you can calculate the location of the second finger as before:
maintains a position as if the ellipse simply keeps moving in the Point pt2 = new Point(pt1.X + distance * Cos(angle),
same direction until it stops; that position is folded into the area pt1.Y + distance * Sin(angle));
in which it can bounce. You don’t need to save any additional information to fields during
PinchDelta handling to process further PinchDelta events.
Pinch Me, I Must Be Dreaming
For the application programmer,
The Pinch sequence occurs when two fingers are touching the
screen; it’s generally interpreted to expand or contract an on-screen

Windows Phone 7 defines


object, possibly rotating it as well.
There’s no question that the pinching operation constitutes one

four—yes, four—different
of the most treacherous areas of multi-touch processing, and it’s
not unusual to see higher-level interfaces fail at providing adequate

touch interfaces.
information. Most notoriously, the Windows Phone 7 Manipulation-
Delta event is particularly tricky to use.
When handling gestures, Drag sequences and Pinch sequences
are mutually exclusive. They don’t overlap but they can occur back The TwoFingerTracking project demonstrates this logic by displaying
to back. For example, press a finger to the screen and drag it. That blue and green ellipses that track one or two fingers around the screen.
generates a DragStarted and multiple DragDelta events. Now press
a second finger to the screen. You’ll get a DragCompleted to com- Scale and Rotate
plete the Drag sequence followed by a PinchStarted and multiple The PinchDelta event also provides sufficient information to per-
PinchDelta events. Now lift the second finger while the first finger form scaling and rotation on objects. I had to supply my own matrix
keeps moving. That’s a PinchCompleted to complete the Pinch multiplication method, but that was about the extent of the hassles.
sequence, followed by DragStarted and DragDelta. Depending To demonstrate, the ScaleAndRotate project implements what is
on the number of fingers touching the screen, you’re basically now a “traditional” type of demonstration that lets you drag, scale
alternating between Drag sequences and Pinch sequences. and optionally rotate a photograph. To perform these transforms, I
One helpful characteristic of this Pinch gesture is that it doesn’t defined the Image element with a double-barreled RenderTransform
discard information. You can use properties of the event arguments as shown in Figure 1.
94 msdn magazine UI Frontiers
Figure 2 The ScaleAndRotate Code
public partial class MainPage : PhoneApplicationPage void OnGestureListenerPinchDelta(object sender, PinchGestureEventArgs args)
{ {
bool isDragging; if (isPinching)
bool isPinching; {
Point ptPinchPositionStart; // Set scaling
scaleTransform.ScaleX = args.DistanceRatio;
public MainPage() scaleTransform.ScaleY = args.DistanceRatio;
{
InitializeComponent(); // Optionally set rotation
} if (allowRotateCheckBox.IsChecked.Value)
rotateTransform.Angle = args.TotalAngleDelta;
void OnGestureListenerDragStarted(object sender, DragStartedGestureEventArgs args)
{ // Set translation
isDragging = args.OriginalSource == image; Point ptPinchPosition = args.GetPosition(this);
} translateTransform.X = ptPinchPosition.X - ptPinchPositionStart.X;
translateTransform.Y = ptPinchPosition.Y - ptPinchPositionStart.Y;
void OnGestureListenerDragDelta(object sender, DragDeltaGestureEventArgs args) }
{ }
if (isDragging)
{ void OnGestureListenerPinchCompleted(object sender, PinchGestureEventArgs args)
translateTransform.X += args.HorizontalChange; {
translateTransform.Y += args.VerticalChange; if (isPinching)
} {
} TransferTransforms();
isPinching = false;
void OnGestureListenerDragCompleted(object sender, }
DragCompletedGestureEventArgs args) }
{
if (isDragging) void TransferTransforms()
{ {
TransferTransforms(); previousTransform.Matrix = Multiply(previousTransform.Matrix,
isDragging = false; currentTransform.Value);
}
} // Set current transforms to default values
scaleTransform.ScaleX = scaleTransform.ScaleY = 1;
void OnGestureListenerPinchStarted(object sender, scaleTransform.CenterX = scaleTransform.CenterY = 0;
PinchStartedGestureEventArgs args)
{ rotateTransform.Angle = 0;
isPinching = args.OriginalSource == image; rotateTransform.CenterX = rotateTransform.CenterY = 0;

if (isPinching) translateTransform.X = translateTransform.Y = 0;


{ }
// Set transform centers
Point ptPinchCenter = args.GetPosition(image); Matrix Multiply(Matrix A, Matrix B)
ptPinchCenter = previousTransform.Transform(ptPinchCenter); {
return new Matrix(A.M11 * B.M11 + A.M12 * B.M21,
scaleTransform.CenterX = ptPinchCenter.X; A.M11 * B.M12 + A.M12 * B.M22,
scaleTransform.CenterY = ptPinchCenter.Y; A.M21 * B.M11 + A.M22 * B.M21,
A.M21 * B.M12 + A.M22 * B.M22,
rotateTransform.CenterX = ptPinchCenter.X; A.OffsetX * B.M11 + A.OffsetY * B.M21 + B.OffsetX,
rotateTransform.CenterY = ptPinchCenter.Y; A.OffsetX * B.M12 + A.OffsetY * B.M22 + B.OffsetY);
}
ptPinchPositionStart = args.GetPosition(this); }
}
}

When a Drag or Pinch operation is in progress, the three trans- change for the duration of the Pinch sequence. During PinchDelta
forms in the nested TransformGroup are manipulated to move events, the DistanceRatio and TotalAngleDelta properties provide
the picture around the screen, scale it and rotate it. When a Drag- scaling and rotation information relative to that center. Any change
Completed or PinchCompleted event occurs, the Matrix in the in movement of the primary finger (which must be detected with a
MatrixTransform named previousTransform is multiplied by saved field) then becomes an overall translation factor.
the composite transform available as the Value property of the That’s certainly the simplest pinch code I’ve ever written, and
TransformGroup. The three transforms in this TransformGroup that fact is perhaps the best endorsement I can provide for this
are then set back to their default values. new gesture interface.
Scaling and rotation are always relative to a center point, which Perhaps a unified theory of multi-touch isn’t far off after all. „
is the point that remains in the same location when the transform
occurs. A photograph scaled or rotated relative to its upper-left CHARLES PETZOLD is a longtime contributing editor to MSDN Magazine. His
corner ends up in a different location than a photograph scaled or new book, “Programming Windows Phone 7” (Microsoft Press, 2010), is avail-
rotated relative to its lower-right corner. able as a free download at bit.ly/cpebookpdf.
The ScaleAndRotate code is shown in Figure 2. I use the primary
finger as the scaling and rotation center; these center points are set THANKS to the following technical expert for reviewing this article:
on the transforms during PinchStarted handling and they don’t Richard Bailey
msdnmagazine.com March 2011 95
DON’T GET ME STARTED DAVID PLATT

Missing the (Power) Point

I just sat through the gazzilionth bad PowerPoint presentation of


my working life. Has anyone ever seen a good PPT presentation?
I can’t remember one. They’re rarer than Siberian tigers.
This speaker had apparently just discovered slide transition
effects, and used a different one for every slide. Bounces. Dissolves.
Wipe downs. A clockwise wheel with eight spokes. It reminded
of the late ’80s when we first got laser printers. We switched fonts
every few words just because we could, so our documents resem-
bled kidnap ransom notes.
Worse, this presenter did nothing but read out his PPT bullets
one by one. I pay $2,000 to get groped by the TSA, crammed into
a metal tube with crying infants and flung across many time zones,
eat bad hotel food and sleep on lumpy mattresses to hear some
art major read a list of bullet points? I could’ve stayed home, slept Figure 1 The Background Isn’t Easy on the Eyes
in my own Tempur-Pedic bed, played with my kids, and read the
darn bullet points myself. degrade it. The slides in the Microsoft November 2010 Windows
A speaker should have to earn a license before presenting a PPT Azure training kit all contain a horizontal color gradient, dark blue
talk. Until you enter the authorization key from a certified instructor, on the left fading to light blue on the right (see Figure 1). “That’s
PPT will refuse to run on any computer connected to a projector. our branding,” said one Microsoft employee. “It looks nice. What’s
You’d learn to stroll through the audience as they gather for your wrong with it?”
talk, greeting as many as you could—addressing them by the names Here’s what’s wrong: The constantly changing contrast between the
on their badges, shaking hands if you could. You’d ask where they’re white text and the background color requires constant fine muscle
from, what they’re doing in their jobs, what they came here to learn adjustment as your eye scans the line. That quickly causes pain in
from you. You’d start getting onto their wavelength, and getting your eye muscles, your body saying, “Hey, knock it off.” Try it now:
them onto yours. Concentrate on the text in the figure. You’ll feel the first twinges of
And during the talk, you’d get out from behind the podium genuine pain in under a minute. And class attendees look at these
and use a remote clicker to switch slides. You’d make eye contact slides for three days. Ouch.
with individuals, placing a monitor facing you at the front of the This branding says, “We’re Microsoft. We know we make your
audience so you wouldn’t have to turn your head away from them head hurt, and we do it anyway. Remember us.” I doubt that was
to see the screen that’s being projected. If the venue wouldn’t give the intention, but it is the result.
you one, you could have a colleague hold up a laptop with its screen Both the bad presenter and the bad slide builder had been care-
facing you. If a colleague wouldn’t do it, you could bribe an attendee. fully trained in the use of a software package. They had not been
If none of those work, then you’d be alone in the world, my friend, trained in communication with human beings, the ultimate goal of
and have problems way bigger than PPT. But you shouldn’t inflict their efforts, which the software package was supposed to enhance.
your problems on your audience. We need to start teaching them what they’re really doing. „
Likewise, anyone who composed a PPT deck would have to earn
a new, different certification before PPT would project it. The cur- DAVID S. PLATT teaches Programming .NET at Harvard University Extension
rent PPT certification teaches you how to do things, but it doesn’t School and at companies all over the world. He’s the author of 11 programming
teach you what to do and what not to, or where or when or why. It books, including “Why Software Sucks” (Addison-Wesley Professional, 2006)
and “Introducing Microsoft .NET” (Microsoft Press, 2002). Microsoft named
teaches you how to start the chain saw, but not which end to hold. him a Software Legend in 2002. He wonders whether he should tape down two
For example, it teaches you how to do color gradients, but doesn’t of his daughter’s fingers so she learns how to count in octal. You can contact him
tell you when they improve the audience’s experience and when they at rollthunder.com.
96 msdn magazine
Untitled-1 1 1/12/11 10:03 AM
Untitled-1 1 11/4/10 4:37 PM

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy