PUBLISHED BY Microsoft Press A Division of Microsoft Corporation One Microsoft Way Redmond, Washington 98052-6399 Copyright © 2008 by Matthew Stoecker All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form or by any means without the written permission of the publisher. Library of Congress Control Number: 2008929780 Printed and bound in the United States of America. 1 2 3 4 5 6 7 8 9 QWE 3 2 1 0 9 8 Distributed in Canada by H.B. Fenn and Company Ltd. A CIP catalogue record for this book is available from the British Library. Microsoft Press books are available through booksellers and distributors worldwide. For further information about international editions, contact your local Microsoft Corporation office or contact Microsoft Press International directly at fax (425) 936-7329. Visit our Web site at www.microsoft.com/mspress. Send comments to
[email protected]. Microsoft, Microsoft Press, Internet Explorer, Visual Basic, Visual Studio, Windows, Windows Server, and Windows Vista are either registered trademarks or trademarks of the Microsoft group of companies. Other product and company names mentioned herein may be the trademarks of their respective owners. The example companies, organizations, products, domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious. No association with any real company, organization, product, domain name, e-mail address, logo, person, place, or event is intended or should be inferred. This book expresses the author’s views and opinions. The information contained in this book is provided without any express, statutory, or implied warranties. Neither the authors, Microsoft Corporation, nor its resellers, or distributors will be held liable for any damages caused or alleged to be caused either directly or indirectly by this book. Acquisitions Editor: Ken Jones Developmental Editor: Laura Sackerman Project Editor: Kathleen Atkins Editorial Production: S4Carlisle Publishing Services Technical Reviewer: Kurt Meyer; Technical Review services provided by Content Master, a member of CM Group, Ltd. Cover: Tom Draper Design
Body Part No. X14-15151
About the Author Matthew A. Stoecker Matthew Stoecker started programming in BASIC on a TRS-80 at the age of nine. In 2001, he joined Microsoft Corporation as a programming writer authoring documentation for Microsoft Visual Basic .NET. He has written numerous technical articles about Visual Basic .NET and Visual C#, and he has written or contributed to multiple books about these languages, Windows Forms, and now Windows Presentation Foundation (WPF). He holds a Bachelor of Music degree in trombone performance from the Oberlin Conservatory and a Ph.D in microbiology from the University of Washington that he hopes he will never have to use again. He spends his spare time biking, playing the trombone, and playing with his cats. He lives in Bellevue, Washington.
Contents at a Glance 1 2 3 4 5 6 7 8 9 10
WPF Application Fundamentals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Events, Commands, and Settings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Building the User Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 Adding and Managing Content. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Configuring Databinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Converting and Validating Data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 Styles and Animation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303 Customizing the User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 Resources, Documents, and Localization . . . . . . . . . . . . . . . . . . . . . . . . . 389 Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441 Answers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473 Glossary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
v
Table of Contents Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xxi 1
WPF Application Fundamentals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Lesson 1: Selecting an Application Type. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Application Type Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Windows Applications. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4 Navigation Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9 XBAPs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .11 Security and WPF Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13 Choosing an Application Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .14 Lab: Creating WPF Applications. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19 Lesson 2: Configuring Page-Based Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21 Using Pages. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21 Hosting Pages in Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21 Using Hyperlinks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .22 Using NavigationService . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .23 Using the Journal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25 Handling Navigation Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .27 Using PageFunction Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .30 Simple Navigation and Structured Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . .32 Lab: The Pizza Kitchen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .32 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .38 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .39
What do you think of this book? We want to hear from you! Microsoft is interested in hearing your feedback so we can continually improve our books and learning resources for you. To participate in a brief online survey, please visit:
www.microsoft.com/learning/booksurvey/
vii
viii
Table of Contents
Lesson 3: Managing Application Responsiveness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Running a Background Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Providing Parameters to the Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Returning a Value from a Background Process . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Cancelling a Background Process. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Reporting the Progress of a Background Process with BackgroundWorker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 Using Dispatcher to Access Controls Safely on Another Thread . . . . . . . . . . . . 47 Freezable Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Lab: Practicing with BackgroundWorker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Chapter Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Key Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Case Scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Case Scenario: Designing a Demonstration Program . . . . . . . . . . . . . . . . . . . . . 54 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Take a Practice Test. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2
Events, Commands, and Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Lesson 1: Configuring Events and Event Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 RoutedEventArgs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Attaching an Event Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 The EventManager Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Defining a New Routed Event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Creating a Class-Level Event Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Application-Level Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Lab: Practice with Routed Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Lesson 2: Configuring Commands. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 A High-Level Procedure for Implementing a Command . . . . . . . . . . . . . . . . . . 73 Invoking Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Table of Contents
ix
Command Handlers and Command Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . .75 Creating Custom Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .78 Lab: Creating a Custom Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .80 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .83 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .84 Lesson 3: Configuring Application Settings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .86 Creating Settings at Design Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .87 Loading Settings at Run Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .88 Saving User Settings at Run Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .88 Lab: Practice with Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .89 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91 Chapter Review. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .94 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .94 Key Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .95 Case Scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .95 Case Scenario 1: Validating User Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .95 Case Scenario 2: Humongous Insurance User Interface. . . . . . . . . . . . . . . . . . . .96 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .96 Take a Practice Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .97
3
Building the User Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .99 Lesson 1: Using Content Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 WPF Controls Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 Content Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 Other Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 Using Attached Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 Setting the Tab Order for Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 Lab: Building a User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Lesson 2: Item Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 ListBox Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 ComboBox Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
x
Table of Contents
TreeView Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 ToolBar Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 StatusBar Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 Virtualization in Item Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 Lab: Practice with Item Controls. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 Lesson 3: Using Layout Controls. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Control Layout Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Layout Panels. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 Accessing Child Elements Programmatically . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 Aligning Content. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Lab: Practice with Layout Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 Chapter Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Key Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Case Scenarios. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 Case Scenario 1: Streaming Stock Quotes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 Case Scenario 2: The Stock Watcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 Take a Practice Test. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
4
Adding and Managing Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Lesson 1: Creating and Displaying Graphics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 Brushes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 Shapes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 Transformations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Clipping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Hit Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Lab: Practice with Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
Table of Contents
xi
Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 Lesson 2: Adding Multimedia Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 Using SoundPlayer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 MediaPlayer and MediaElement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 Handling Media-Specific Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 Lab: Creating a Basic Media Player . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 Lesson 3: Managing Binary Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Embedding Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Loading Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Retrieving Resources Manually . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 Content Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 Retrieving Loose Files with siteOfOrigin Pack URIs . . . . . . . . . . . . . . . . . . . . . . 190 Lab: Using Embedded Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Lesson 4: Managing Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 The Image Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 Stretching and Sizing Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 Transforming Graphics into Images. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Accessing Bitmap Metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 Lab: Practice with Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 Chapter Review. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 Key Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 Case Scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Case Scenario 1: The Company with Questionable Taste . . . . . . . . . . . . . . . . 205 Case Scenario 2: The Image Reception Desk . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Take a Practice Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
xii
Table of Contents
5
Configuring Databinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Lesson 1: Configuring Databinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 The Binding Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Binding to a WPF Element. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 Binding to an Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 Setting the Binding Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Setting the UpdateSourceTrigger Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 Lab: Practice with Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Lesson 2: Binding to Data Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Binding to a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Binding an Item Control to a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Binding a Single Property to a List. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 Navigating a Collection or List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 Binding to ADO.NET Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 Setting the DataContext to an ADO.NET DataTable . . . . . . . . . . . . . . . . . . . . . 226 Setting the DataContext to an ADO.NET DataSet . . . . . . . . . . . . . . . . . . . . . . . 227 Binding to Hierarchical Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 Binding to Related ADO.NET Tables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 Binding to an Object with ObjectDataProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 Binding to XML Using the XmlDataProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 Using XPath with XmlDataProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 Lab: Accessing a Database. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 Lesson 3: Manipulating and Displaying Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 Data Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 Setting the Data Template. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 Sorting Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 Applying Custom Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 Grouping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 Creating Custom Grouping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
Table of Contents
xiii
Filtering Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 Filtering ADO.NET Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 Lab: Practice with Data Templates and Groups . . . . . . . . . . . . . . . . . . . . . . . . . 248 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 Chapter Review. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Key Terms. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 Case Scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 Case Scenario 1: Getting Information from the Field . . . . . . . . . . . . . . . . . . . . 256 Case Scenario 2: Viewing Customer Data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 Take a Practice Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
6
Converting and Validating Data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 Lesson 1: Converting Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 Implementing IValueConverter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 Using Converters to Format Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 Using Converters to Return Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268 Using Converters to Apply Conditional Formatting in Data Templates . . . . 269 Localizing Data with Converters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 Using Multi-value Converters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 Lab: Applying String Formatting and Conditional Formatting . . . . . . . . . . . . 276 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 Lesson 2: Validating Data and Configuring Change Notification . . . . . . . . . . . . . . 282 Validating Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 Binding Validation Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 Setting ExceptionValidationRule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 Implementing Custom Validation Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 Handling Validation Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 Configuring Data Change Notification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 Implementing INotifyPropertyChanged . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
xiv
Table of Contents
Using ObservableCollection. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 Lab: Configuring Change Notification and Data Validation . . . . . . . . . . . . . . 289 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 Chapter Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 Key Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 Case Scenarios. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 Case Scenario 1: The Currency Trading Review Console . . . . . . . . . . . . . . . . . 301 Case Scenario 2: Currency Trading Console . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302 Take a Practice Test. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
7
Styles and Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303 Lesson 1: Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 Using Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 Properties of Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 Setters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 Creating a Style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 Implementing Style Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 Triggers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312 Property Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 Multi-triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 Data Triggers and Multi-data-triggers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 Event Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 Understanding Property Value Precedence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 Lab: Creating High-Contrast Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 Lesson 2: Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 Using Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 Important Properties of Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 Storyboard Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Table of Contents
xv
Using Animations with Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327 Managing the Playback Timeline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 Animating Non-Double Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332 Creating and Starting Animations in Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335 Lab: Improving Readability with Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338 Chapter Review. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 Key Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 Case Scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 Case Scenario 1: Cup Fever. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 Case Scenario 2: A Far-Out User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 Take a Practice Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
8
Customizing the User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 Lesson 1: Integrating Windows Forms Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 Using Windows Forms Controls. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 Using Dialog Boxes in WPF Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 WindowsFormsHost . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 Using MaskedTextBox in WPF Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351 Using the PropertyGrid in WPF Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 Lab: Practice with Windows Forms Elements. . . . . . . . . . . . . . . . . . . . . . . . . . . 354 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357 Lesson 2: Using Control Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 Using Control Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 Creating Control Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 Inserting a Trigger in a Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362 Respecting the Templated Parent’s Properties . . . . . . . . . . . . . . . . . . . . . . . . . 363 Applying Templates with a Style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365 Viewing the Source Code for an Existing Template . . . . . . . . . . . . . . . . . . . . . 365
xvi
Table of Contents
Using Predefined Part Names in a Template . . . . . . . . . . . . . . . . . . . . . . . . . . . 366 Lab: Creating a Control Template. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 Lesson 3: Creating Custom and User Controls. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372 Control Creation in WPF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372 Choosing Among User Controls, Custom Controls, and Templates . . . . . . . . 373 Implementing and Registering Dependency Properties . . . . . . . . . . . . . . . . . 373 Creating User Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 Creating Custom Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 Consuming User Controls and Custom Controls . . . . . . . . . . . . . . . . . . . . . . . . 377 Rendering a Theme-Based Appearance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378 Lab: Creating a Custom Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383 Chapter Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 Key Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 Case Scenarios. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 Case Scenario 1: Full Support for Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 Case Scenario 2: The Pizza Progress Bar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 Take a Practice Test. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
9
Resources, Documents, and Localization. . . . . . . . . . . . . . . . . . . . . . . . . 389 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389 Lesson 1: Logical Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 Using Logical Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 Logical Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392 Creating a Resource Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 Retrieving Resources in Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396 Lab: Practice with Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
Table of Contents
xvii
Lesson 2: Using Documents in WPF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401 Flow Documents. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401 Creating Flow Documents. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402 XPS Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418 Viewing XPS Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418 Printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418 Printing Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419 The PrintDialog Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419 Lab: Creating a Simple Flow Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423 Lesson 3: Localizing a WPF Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 Localization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 Localizing an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427 Using Culture Settings in Validators and Converters . . . . . . . . . . . . . . . . . . . . 432 Lab: Localizing an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433 Lesson Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436 Chapter Review. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438 Key Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438 Case Scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439 Case Scenario: Help for the Beta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 Take a Practice Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
10
Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441 Before You Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441 Lesson 1: Creating a Setup Project with Windows Installer. . . . . . . . . . . . . . . . . . . . 443 Deploying a WPF Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443 Choosing Between Windows Installer and ClickOnce . . . . . . . . . . . . . . . . . . . 443 Deploying with Windows Installer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 Deploying a Stand-alone Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445 Creating the Setup Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
xviii
Table of Contents
Adding Files to the Setup Project with the File System Editor . . . . . . . . . . . . 445 Other Setup Project Editors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448 Lab: Creating a Setup Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 Lesson 2: Deploying Your Application with ClickOnce . . . . . . . . . . . . . . . . . . . . . . . . 451 Deploying with ClickOnce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451 Deploying an Application Using ClickOnce . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452 Configuring ClickOnce Update Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 Deploying an XBAP with ClickOnce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458 Configuring the Application Manifest. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461 Associating a Certificate with the Application . . . . . . . . . . . . . . . . . . . . . . . . . . 463 Lab: Publishing Your Application with ClickOnce . . . . . . . . . . . . . . . . . . . . . . . 464 Lesson Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 Lesson Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 Chapter Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 Key Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 Case Scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470 Case Scenario: Buggy Beta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470 Suggested Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470 Take a Practice Test. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471 Answers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473 Glossary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
What do you think of this book? We want to hear from you! Microsoft is interested in hearing your feedback so we can continually improve our books and learning resources for you. To participate in a brief online survey, please visit:
www.microsoft.com/learning/booksurvey/
Acknowledgments Thank you to my friends and family. It isn't easy dealing with a person who is going crazy trying to write a book. Thanks for understanding when I had to work all night. Thanks for understanding when I needed to stay home and write on Friday night instead of going to see a movie. Thanks for putting up with me when all I talked about was how I needed to work on this book. This especially means thanks to you, Libby.
xix
Introduction This training kit is designed for developers who plan to take the Microsoft Certified IT Professional (MCITP) Exam 70-502, as well as for developers who need to know how to develop Microsoft Windows Presentation Foundation (WPF)–based applications using Microsoft .NET Framework 3.5. We assume that before using this training kit, you already have a working knowledge of Windows, Microsoft Visual Basic or C# (or both), and Extensible Application Markup Language (XAML). By using this training kit, you will learn how to do the following: Q
Create a WPF application
Q
Build user interfaces using WPF controls
Q
Add and manage content in a WPF application
Q
Bind WPF controls to data sources
Q
Customize the appearance of your WPF application
Q
Configure a WPF application
Q
Deploy a WPF application to its intended audience
Hardware Requirements The following hardware is required to complete the practice exercises: Q
A computer with a 1.6-gigahertz (GHz) or faster processor
Q
A minimum of 384 megabytes (MB) of random access memory (RAM)
Q
A minimum of 2.2 gigabytes (GB) of available hard disk space is required to install VS 2008. Additionally, 50 megabytes (MB) of available hard disk space is required to install the labs.
Q
A DVD-ROM drive
Q
A 1024 x 768 or higher resolution display with 256 colors or more
Q
A keyboard and Microsoft mouse or compatible pointing device
xxi
xxii
Introduction
Software Requirements The following software is required to complete the practice exercises: Q
Q
One of the following operating systems: T
Windows Vista (any edition except Windows Vista Starter)
T
Windows XP with Service Pack 2 or later (any edition except Windows XP Starter)
T
Windows Server 2003 with Service Pack 1 or later (any edition)
T
Windows Server 2003 R2 or later (any edition)
T
Windows Server 2008
Microsoft Visual Studio 2008
A 90-day evaluation edition of Visual Studio 2008 Professional Edition is included on a DVD that comes with this training kit. NOTE
Using the CD and DVD A companion CD and an evaluation software DVD are included with this training kit. The companion CD contains the following: Q
Practice Tests
Q
Sample Files
You can reinforce your understanding of how to create WPF applications in Visual Studio 2008 with .NET Framework 3.5 by using electronic practice tests that you can customize to meet your needs from the pool of Lesson Review questions in this book. Alternatively, you can practice for the 70-502 certification exam by using tests created from a pool of 200 realistic exam questions, which will give you enough different practice tests to ensure you’re prepared.
Most chapters in this training kit include sample files that are associated with the lab exercises at the end of every lesson. For some exercises, you are instructed to open a project prior to starting the exercise. For other exercises, you create a project on your own and can reference a completed project on the CD if you have a problem following the exercise procedures. Sample files can be installed to your hard drive by simply copying them to the appropriate directory.
Introduction
xxiii
After copying the sample files from the CD to your hard drive you must clear the Read Only attribute in order to work with the files on your hard drive. Q
eBook An electronic version (eBook) of this training kit is included for use at times when you don’t want to carry the printed book with you. The eBook is in Portable Document Format (PDF) and you can view it by using Adobe Acrobat or Adobe Reader. You can use the eBook to cut and paste code as you work through the exercises.
The evaluation software DVD contains a 90-day evaluation edition of Visual Studio 2008 Professional Edition, in case you want to use it instead of a full version of Visual Studio 2008 to complete the exercises in this book. Digital Content for Digital Book Readers: If you bought a digital-only edition of this book, you can enjoy select content from the print edition’s companion CD. Visit http://go.microsoft.com/fwlink/?Linkld=120409 to get your downloadable content. This content is always up-to-date and available to all readers.
How to Install the Practice Tests To install the practice test software from the companion CD to your hard disk, perform the following steps: 1. Insert the companion CD into your CD drive and accept the license agreement that appears onscreen. A CD menu appears. If the CD menu or the license agreement doesn’t appear, AutoRun might be disabled on your computer. Refer to the Readme.txt f ile on the CD-ROM for alternative installation instructions. NOTE
2. Click Practice Tests and follow the instructions on the screen.
How to Use the Practice Tests To start the practice test software, follow these steps: 1. Click Start and select All Programs and Microsoft Press Training Kit Exam Prep. A window appears that shows all the Microsoft Press training kit exam prep suites that are installed on your computer. 2. Double-click the lesson review or practice test you want to use.
xxiv
Introduction
Lesson Review Options When you start a lesson review, the Custom Mode dialog box appears, allowing you to configure your test. You can click OK to accept the defaults or you can customize the number of questions you want, the way the practice test software works, which exam objectives you want the questions to relate to, and whether you want your lesson review to be timed. If you are retaking a test, you can select whether you want to see all the questions again or only those questions you previously skipped or answered incorrectly. After you click OK, your lesson review starts. You can take the test as follows: Q
To take the test, answer the questions and use the Next, Previous, and Go To buttons to move from question to question.
Q
After you answer an individual question, if you want to see which answers are correct, along with an explanation of each correct answer, click Explanation.
Q
If you would rather wait until the end of the test to see how you did, answer all the questions and then click Score Test. You see a summary of the exam objectives that you chose and the percentage of questions you got right overall and per objective. You can print a copy of your test, review your answers, or retake the test.
Practice Test Options When you start a practice test, you can choose whether to take the test in Certification Mode, Study Mode, or Custom Mode. Q
Certification Mode
Q
Study Mode Creates an untimed test in which you can review the correct answers and the explanations after you answer each question.
Q
Custom Mode
Closely resembles the experience of taking a certification exam. The test has a set number of questions, it is timed, and you cannot pause and restart the timer.
Gives you full control over the test options so that you can customize them as you like.
In all modes, the user interface you see when taking the test is basically the same, but different options are enabled or disabled, depending on the mode. The main options are discussed in the previous section, “Lesson Review Options.” When you review your answer to an individual practice test question, a “References” section is provided. This section lists where in the training kit you can find the information
Introduction
xxv
that relates to that question, and it also provides links to other sources of information. After you click Test Results to score your entire practice test, you can click the Learning Plan tab to see a list of references for every objective.
How to Uninstall the Practice Tests To uninstall the practice test software for a training kit, use the Add Or Remove Programs option in the Control Panel in Windows.
Microsoft Certified Professional Program Microsoft certifications provide the best method to prove your command of current Microsoft products and technologies. The exams and corresponding certifications are developed to validate your mastery of critical competencies as you design and develop or implement and support solutions with Microsoft products and technologies. Computer professionals who become Microsoft certified are recognized as experts and are sought after industry wide. Certification brings a variety of benefits to the individual and to employers and organizations. MORE INFO
default.mspx.
For a full list of Microsoft certif ications, go to http://www.microsoft.com/learning/mcp/
Technical Support Every effort has been made to ensure the accuracy of this book and the contents of the companion CD. If you have comments, questions, or ideas regarding this book or the companion CD, please send them to Microsoft Press by using either of the following methods: E-mail:
[email protected] Postal Mail: Microsoft Press Attn: MCITP Self-Paced Training Kit (Exam 70-502) Microsoft .NET Framework 3.5 – Windows Presentation Foundation Editor One Microsoft Way Redmond, WA, 98052-6399
xxvi
Introduction
For additional support information regarding this book and the CD-ROM (including answers to commonly asked questions about installation and use), visit the Microsoft Press Technical Support Web site at http://www.microsoft.com/learning/support/ books. To connect directly to the Microsoft Knowledge Base and enter a query, visit http://support.microsoft.com/search. For support information regarding Microsoft software, please connect to http://support.microsoft.com.
Evaluation Edition Software The 90-day evaluation edition provided with this training kit is not the full retail product and is provided only for the purposes of training and evaluation. Microsoft and Microsoft Technical Support do not support this evaluation edition. Information about any issues relating to the use of this evaluation edition with this training kit is posted in the Support section of the Microsoft Press Web site (http:// www.microsoft.com/learning/support/books/). For information about ordering the full version of any Microsoft software, please call Microsoft Sales at (800) 426-9400 or visit http://www.microsoft.com.
Chapter 1
WPF Application Fundamentals Windows Presentation Foundation (WPF) is the successor to Windows Forms for desktop application development. WPF applications differ from traditional Windows Forms applications in several ways. The most notable is that the code for the user interface is separate from the code for application functionality. Although the code for the functionality of a project can be defined using familiar languages such as Microsoft Visual Basic .NET or Microsoft Visual C#, the user interface of a WPF project is typically defined using a relatively new declarative syntax called Extensible Application Markup Language (XAML). This chapter introduces you to the fundamentals of creating a WPF application. Lesson 1 describes the different kinds of WPF applications that can be created, highlights the differences between them, and explains how to choose among them. Lesson 2 explores creating page-based applications, and Lesson 3 explains how to manage application responsiveness.
Exam objectives in this chapter: Q
Select an application type.
Q
Configure page-based navigation.
Q
Manage application responsiveness.
Lessons in this chapter: Q
Lesson 1: Selecting an Application Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Q
Lesson 2: Configuring Page-Based Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Q
Lesson 3: Managing Application Responsiveness . . . . . . . . . . . . . . . . . . . . . . . . 41
1
2
Chapter 1
WPF Application Fundamentals
Before You Begin To complete the lessons in this chapter, you must have: Q
A computer that meets or exceeds the minimum hardware requirements listed in the “Introduction” section at the beginning of the book
Q
Microsoft Visual Studio 2008 Professional Edition installed on your computer
Q
An understanding of Visual Basic or C# syntax and familiarity with the Microsoft .NET Framework version 3.5
Q
An understanding of XAML
Real World Matthew Stoecker WPF is a major step forward in desktop application development. Not only does WPF allow unprecedented control of the user interface through the use of advanced graphics, but the separation of code between the presentation layer and the business logic layer allows development to be split between designers and developers, enabling rapid development.
Lesson 1: Selecting an Application Type
3
Lesson 1: Selecting an Application Type WPF development supports three kinds of application: Windows applications, Navigation applications, and XAML Browser Applications (XBAPs). Each application type has its own benefits and drawbacks. In this lesson, you will learn the advantages and disadvantages of each application type, how to create each type of application, and the security implications for each type of application. After this lesson, you will be able to: Q
Describe the differences between a Windows application, a Navigation application, and an XBAP
Q
Create a new Windows application, Navigation application, or XBAP
Q
Describe the security implications for each type of application
Q
Choose the appropriate type of application for your situation
Estimated lesson time: 30 minutes
Application Type Overview Three basic types of applications can be created with WPF. Windows applications are the most similar to Windows Forms applications. Windows applications are Microsoft Windows–driven and provide a user experience that is familiar to Windows users and developers alike. Multiple windows can be open at any given time, and there is no built-in sense of navigation or history. Navigation applications provide a page-based user experience, similar to the experience of using a Web site. Typically, only a single page can be open at any given time, and the journal functionality keeps a record of pages visited and allows back-andforth navigation. Unlike a Web site, however, a Navigation application is a compiled application that runs on your desktop computer and, like a Windows application, has full access to the resources of your computer. XBAPs are similar to Navigation applications, but they are designed to run in Windows Internet Explorer. These applications can be deployed to a server or to a Web site and are downloaded when instantiated. Applications of this type do not have full access to a computer’s resources. XBAPs run under a partial-trust environment, and resources such as the file system and the registry are inaccessible by XBAPs.
4
Chapter 1
WPF Application Fundamentals
Windows Applications A Windows application consists of one or more windows and the associated business logic. The top-level user interface (UI) element of a window is a class that derives from the Window class. The Window class derives from ContentControl, meaning that it can contain a single element in its Content property. This element is usually a layout control, such as a Grid control, which can itself contain multiple child controls. Managing content and layout will be discussed in detail in Chapter 3, “Building the User Interface.” In addition, a Window object exposes several properties that allow you to manage the appearance and behavior of the window itself. You can create a new Windows application using Visual Studio.
To create a new Windows application
1. In Visual Studio, from the File menu, choose New and then Project to open the New Project dialog box. 2. In the New Project dialog box, select WPF Application, provide a name, and click OK. A new application is created in the Visual Studio integrated development environment (IDE), as shown in Figure 1-1.
Figure 1-1 A new Windows application in the Visual Studio IDE
Lesson 1: Selecting an Application Type
5
Figure 1-1 shows the Visual Studio IDE for WPF applications. Experienced Visual Studio users will note that the Toolbox, Properties window, Designer, and Solution Explorer are very similar to their counterparts in the Windows Forms designer. One notable addition is the XAML view, which in Figure 1-1 appears below the designer. The XAML view allows you to edit the XAML that defines the window in the designer. Changes made to the XAML are immediately applied to the window and vice versa.
Window Properties The Window class contains several properties that allow you to influence the general appearance and behavior of the window. Many of these properties will be familiar to Windows Forms programmers. Some of these properties are summarized in Table 1-1. Table 1-1
Properties of the Window Class
Property
Description
AllowsTransparency
When set to True, this property allows other windows to show through this window when the Background property is set to a transparent brush. When AllowsTransparency is set to True, WindowsStyle must be set to WindowsStyle.NoBorder.
Background
Gets or sets the Brush object used to paint the background of the form.
BorderBrush
Gets or sets the Brush object used to paint the border of the form.
BorderThickness
Gets or sets the thickness of the window border in device-independent pixels.
Cursor
Gets or sets the cursor used when the mouse pointer is over the window.
Foreground
Gets or sets the Brush object used to paint the foreground of the window.
Height
Gets or sets the height of the window in device-independent pixels.
6
Chapter 1
WPF Application Fundamentals
Table 1-1
Properties of the Window Class
Property
Description
Icon
Gets or sets the ImageSource object that is displayed as the icon for this window. This icon appears in the upper left-hand corner of the window when the WindowStyle property is set to a standard window style, in the taskbar if ShowInTaskbar is set to True, and in the selection window that is shown when the user presses Alt+Tab.
IsEnabled
When set to True, the window is able to receive user input. When set to False, the window is inactive and controls contained in the window appear dimmed.
Left
Gets or sets the distance between the left edge of the window and the left edge of the screen in device-independent pixels.
ResizeMode
Gets or sets how the user is allowed to resize the window at run time. When set to NoResize, no resizing is allowed. When set to CanMinimize, the user can minimize the window but cannot otherwise change the size. When set to CanResize, the user can resize the window. Setting the property to CanResizeWithGrip also allows the user to resize the window and adds a visual cue to the lower right-hand corner at run time.
ShowInTaskbar
When set to True, the window appears in the taskbar. This is generally useful only for the application’s main window.
SizeToContent
Determines whether the window resizes itself automatically to fit its content. When set to Manual, the window does not resize itself and instead appears at the size set by the Height and Width properties. When set to Height, it changes only the height. When set to Width, it changes only the width. When set to WidthAndHeight, the window resizes both width and height to fit the content.
Lesson 1: Selecting an Application Type
Table 1-1
7
Properties of the Window Class
Property
Description
Title
Gets or sets the text displayed at the top of the window.
Top
Gets or sets the distance between the top edge of the window and the top edge of the screen in device-independent pixels.
Topmost
When set to True, this window is always displayed on top of all other windows in the application.
Width
Gets or sets the width of the window in device-independent pixels.
WindowStartupLocation
Determines the startup location of the window. When set to Manual, the startup location is determined by the settings of the Top and Left properties. When set to CenterScreen, the window appears in the center of the screen. When set to CenterOwner, the window appears in the center of the owning form.
WindowState
Gets or sets the window state. The property can be set to Normal, Minimized, or Maximized.
WindowStyle
Gets or sets the style of the window. The window style can be set to NoBorder, SingleBorderWindow, ThreeDBorderWindow, or ToolWindow. SingleBorderWindow and ThreeDBorderWindow appear identical when run on Microsoft Windows Vista. ToolWindow appears with no icon and no minimize or maximize buttons. If AllowsTransparency is set to True, you must set WindowStyle to NoBorder.
Window properties can be set in Visual Studio at design time by selecting the window in the designer and setting the appropriate property in the Properties window. Alternatively, you can set a property by editing the XAML for the window in the XAML view. The following example demonstrates setting the WindowsState property (shown in bold) by setting it in the XAML for the window:
Displaying Windows The startup window of an application is displayed automatically when the application opens. To display other windows, you first must create an instance of the window and then use either the Show method or the ShowDialog method. The Show method displays the new window modelessly. This means that the user can switch between this window and other windows in the application, and each window can process user input. When the Show method is used, application execution proceeds normally. In contrast, the ShowDialog method displays the new window modally. When ShowDialog is called, the new window is the only window in the application that responds to user input until it is closed. In addition, the ShowDialog method stops application execution until the window is closed and the ShowDialog method returns. This method is used frequently to create custom dialog boxes or in other situations that require user input before an application can proceed. A window can be closed in code using the Close method. If the window is shown with the ShowDialog method, the ShowDialog method returns and the application execution proceeds.
To show a window modelessly
1. Create a new instance of the window, as shown in the following code example: ' VB ' Assumes you have previously defined a class called MyWindow that inherits ' the Window class. Dim aWindow As New MyWindow() // C# // Assumes you have previously defined a class called MyWindow that // inherits the Window class. MyWindow aWindow = new MyWindow();
2. Call the Show method to display the new window: ' VB aWindow.Show() // C# aWindow.Show();
Lesson 1: Selecting an Application Type
9
To show a window modally
1. Create a new instance of the window, as shown in the following code example: ' VB ' Assumes you have previously defined a class called MyWindow that inherits ' the Window class. Dim aWindow As New MyWindow() // C# // Assumes you have previously defined a class called MyWindow that // inherits the Window class. MyWindow aWindow = new MyWindow();
2. Call the ShowDialog method to display the new window: ' VB aWindow.ShowDialog() // C# aWindow.ShowDialog();
To close a window call the Close method, as shown here: ' VB aWindow.Close() // C# aWindow.Close();
Navigation Applications Navigation applications allow you to provide a different style of user interface to users. A navigation application is a desktop application that uses Page objects as its primary object instead of Window objects. The “look and feel” of a Navigation application is more like a Web site than a standard Windows application, but because they are desktop applications, Navigation applications can take advantage of the full functionality of the .NET Framework without security restrictions. Like Window objects, Page objects can contain a single control. This is usually a Grid control or some other layout-based control that allows the placement of multiple child controls to form the user interface. Page-based navigation applications are hosted automatically in a NavigationWindow at startup. The NavigationWindow provides some built-in functionality for navigating through the pages of an application, such as Back and Forward buttons, and the journal, which keeps track of the pages that have been visited recently. Pages and page-based navigation will be discussed in more detail in Lesson 2.
10
Chapter 1
WPF Application Fundamentals
To Create a Navigation application
1. From the File menu, choose New and then Project to open the New Project dialog box. 2. In the New Project dialog box, select WPF Application, provide a name, and click OK. 3. In Solution Explorer, right-click Window1.xaml and choose Delete. Click OK to confirm the deletion. 4. From the Project menu, choose Add Page and click Add in the Add New Item dialog box. 5. In Solution Explorer, double-click App.xaml to open the XAML Editor for the App.xaml page. 6. In the XAML Editor, change the StartupUri property to the name of your new page, as shown in bold in the following example:
The resulting application is shown in Figure 1-2.
Figure 1-2 A new Navigation application in the Visual Studio IDE
Lesson 1: Selecting an Application Type
11
XBAPs XBAPs are similar to Navigation applications. They use Page objects as the top-level UI object, and they allow a page-based navigation model that is similar to the experience of using a Web page. The primary difference between an XBAP and a Navigation application is that XBAPs are not installed. They are designed to run in Internet Explorer and do not require the user to copy them onto the hard drive. XBAPs can be deployed to a Web server and can be started simply by clicking a hyperlink that points to the .xbap file. Because XBAPs are not installed, they run under a limited set of permissions. In general, XBAPs will run under the permissions allowed by the Internet zone of Internet Explorer. Thus, XBAPs that are run under standard conditions cannot use the file system, they cannot access databases, they cannot access the registry, and in general are prohibited from using system resources that could result in a security risk. It is technically possible to run an XBAP under full trust, but it is not recommended because doing so gives full access of your system resources to an application from an untrusted location, thus creating a security risk. If you want to create a page-based application for secure and trusted deployment, a Navigation application is recommended.
To create an XBAP
1. From the File menu, choose New and then choose Project. 2. In the New Project dialog box, choose WPF Browser Application, provide a name, and click OK. A new XBAP is created. Figure 1-3 shows a new XBAP in Visual Studio.
XBAPs and Web Pages XBAPs are designed to run in Internet Explorer, and as of this writing, Internet Explorer 6.0 and higher is the only Web browser that supports XBAPs. While the most common scenario is to open an XBAP directly in Internet Explorer, you also can host an XBAP inside a Web page by embedding it in an tag. This allows you to run an XBAP alongside Hypertext Markup Language (HTML) content, or to have multiple XBAPs running in a single browser (in multiple frames). An example is shown here: This is HTML, the XBAP is loaded into the Frame
12
Chapter 1
WPF Application Fundamentals
Figure 1-3 A new XBAP in the Visual Studio IDE
XBAPs and Isolated Storage Although XBAPs generally are prohibited from accessing the file system, there is one exception. XBAPs are allowed to access isolated storage. Isolated storage is an application-specific, user-specific file store that is protected from the rest of the system. The size of the file store is relatively small (512 kilobytes), but it can be useful for reading and writing user preferences and other pertinent application data. Isolated storage behaves much like the regular file system. You can write to files and read from them using familiar StreamReader and StreamWriter classes. The following procedure describes how to read from and write to isolated storage.
To read from or write to isolated storage
1. Create an instance of the IsolatedStorageFile class that refers to the store you want to read from, as shown: ' VB ' Assumes: Imports System.IO.IsolatedStorage Dim store as IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication // C# // Assumes: using System.IO.IsolatedStorage; IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication;
Lesson 1: Selecting an Application Type
13
2. Create a new instance of IsolatedStorageFileStream that refers to the file you want to read or write, as shown: ' VB Dim fs As New IsolatedStorageFileStream("UserPreferences.txt", _ System.IO.FileMode.Open, store) // C# IsolatedStoragefileStream fs = new IsolatedStorageFileStream("UserPreferences.txt", System.IO.FileMode.Open, store);
3. Create a StreamReader to read the file, or a StreamWriter to write to it, as shown: ' VB ' To read an isolated storage file Dim sr As New System.IO.StreamReader(fs) Dim aString As String = sr.ReadToEnd ' To write to an isolated storage file Dim sw As New System.IO.StreamWriter(fs) sw.WriteLine("The user prefers cake") // C# // To read an isolated storage file System.IO.StreamReader sr = new System.IO.StreamReader(fs); string aString = sr.ReadToEnd(); // To write to an isolated storage file System.IO.StreamWriter sw = new System.IO.StreamWriter(fs); sw.WriteLine("The user prefers cake");
Quick Check Q
What is the difference between a Navigation application and an XBAP?
Quick Check Answer Q
While both are page-based applications, a Navigation application is hosted in a Navigation window and can be run easily under full trust, making it ideal for deployment in secure environments. XBAPs are designed to be run in Internet Explorer and generally are not installed locally. They are run under partial trust, making them ideal for wide distribution.
Security and WPF Applications When choosing an application type for your WPF application, it is important to consider the security context in which the application will run. Windows applications and Navigation applications are installed on your desktop computer. Thus they can run under full trust or under whatever security policy has been defined by the administrator.
14
Chapter 1
WPF Application Fundamentals
XBAPs, on the other hand, are not installed. Instead, they are executed under the security restrictions of the Internet security zone, which restricts a variety of actions. For example, under standard Internet security settings, you cannot perform the following actions in an XBAP: Q
Access the file system, except for isolated storage
Q
Access the registry
Q
Create stand-alone windows, such as dialog boxes
Q
Access local databases
Q
Use Windows drag-and-drop functionality
Q
Use WCF Web services
These restrictions can be quite limiting, so you should keep them in mind when choosing your application type.
Choosing an Application Type The choice of an application type depends upon several factors, the two most important of which are user experience and application requirements. The desired user experience determines whether you choose a Windows application or a page-based application. For a user experience that most closely resembles a traditional Windows Forms application, a Windows application is the best choice. This application type allows you to create a menu-driven, multiwindow application that combines the rich functionality of a desktop application with the rich UI experience that WPF provides. For a user experience that more closely resembles a Web site, you should choose a page-based application. Navigation applications and XBAPs provide built-in navigational functionality that allows you to structure the application paralleling a task, such as in an Internet shopping application or a wizard. Application requirements are another key factor. If an application requires access to system resources that fall outside the Internet security zone, then an XBAP is not a good choice—a better choice would be a Windows application or a Navigation application. On the other hand, XBAPs allow you to deploy the application to a Web server and have users start it from a hyperlink, thus making it easily accessible to a largescale audience. If your application does not require access to system resources, an XBAP might be a good choice.
Lesson 1: Selecting an Application Type
15
Lab: Creating WPF Applications In this lab, you will create three simple WPF applications: a Windows application, a Navigation application, and an XBAP. NOTE
You must run Visual Studio under an account that has File IO permission to complete this lab.
Exercise 1: Creating a Windows Application
1. From the File menu, choose New and then choose Project to open the New Project dialog box. Choose WPF Application, name the project, and then click OK. 2. In the designer, drag two buttons, a text box, and a label from the Toolbox to the Window1. 3. In either the Properties window or the XAML window, set the Content property of these controls as follows: Control
Content Property Value
Button1
Set Name
Button2
Retrieve Name
Label1
(nothing)
When completed, your designer should look similar to Figure 1-4. 4. Double-click Button1 and add the following code: ' VB Dim sw As New System.IO.StreamWriter("C:\username.txt") sw.WriteLine(TextBox1.Text) sw.Close() // C# System.IO.StreamWriter sw = new System.IO.StreamWriter("C:\\username.txt"); sw.WriteLine(textBox1.Text); sw.Close();
5. In the designer, double-click Button2 and add the following code: ' VB Dim sr As New System.IO.StreamReader("C:\username.txt") Label1.Content = "Hello " & sr.ReadToEnd sr.Close()
16
Chapter 1
WPF Application Fundamentals
// C# System.IO.StreamReader sr = new System.IO.StreamReader("C:\\username.txt"); label1.Content = "Hello " + sr.ReadToEnd(); sr.Close();
Figure 1-4 The Designer after adding and updating controls for a Windows application
6. Press F5 to run the application. Type your name in the text box and click Set Name. Then click Get Name. Your name and a greeting are displayed in the label.
Exercise 2: Creating a Navigation Application 1. From the File menu, choose New and then choose Project to open the New Project dialog box. Choose WPF Application, name the project, and then click OK. 2. In Solution Explorer, right-click Window1.xaml, choose Delete, and then click OK. 3. From the Project menu, choose Add Page and click Add. 4. In Solution Explorer, double-click Application.xaml (in Visual Basic) or App.xaml (in C#) to open it in XAML view. 5. In XAML view, change the StartupUri property to Page1.
Lesson 1: Selecting an Application Type
17
6. Repeat steps 2 through 5 from Exercise 1. Your designer should look like Figure 1-5 when completed.
Figure 1-5 The Designer after adding and updating controls for a Navigation application
7. Press F5 to run the application. Type your name in the text box and click Set Name. Then click Get Name. Your name and a greeting are displayed in the label.
Exercise 3: Creating an XBAP 1. From the File menu, choose New and then choose Project to open the New Project dialog box. Choose WPF Browser Application, name the project, and then click OK. 2. Repeat steps 2 through 5 from Exercise 1. Your designer should look like Figure 1-6 when finished. 3. Press F5 to run the application. Click Yes if you are presented with a Script Debugging Disabled dialog box. Type your name in the text box and press Set Name. A SecurityException is thrown. Because XBAPs run under Internet zone permissions, they cannot access the file system. To correct this error, you must implement isolated storage.
18
Chapter 1
WPF Application Fundamentals
Figure 1-6 The designer after adding and updating controls for an XBAP
4. Stop debugging (by choosing Stop Debugging from the Debug menu). In the Designer, double-click Button1 and replace the code in Button1_Click with the following: ' VB Dim fs As New System.IO.IsolatedStorage.IsolatedStorageFileStream ("username.txt", _ IO.FileMode.Create) Dim sw As New System.IO.StreamWriter(fs) sw.WriteLine(TextBox1.Text) sw.Close() // C# System.IO.IsolatedStorage.IsolatedStorageFileStream fs = new System.IO.IsolatedStorage.IsolatedStorageFileStream ("username.txt", System.IO.FileMode.Create); System.IO.StreamWriter sw = new System.IO.StreamWriter(fs); sw.WriteLine(textBox1.Text); sw.Close();
5. Replace the code in Button2_Click with the following: ' VB Dim fs As New _System.IO.IsolatedStorage.IsolatedStorageFileStream ("username.txt", _ IO.FileMode.OpenOrCreate)
Lesson 1: Selecting an Application Type
19
Dim sr As New System.IO.StreamReader(fs) Label1.Content = "Hello " & sr.ReadToEnd sr.Close() // C# System.IO.IsolatedStorage.IsolatedStorageFileStream fs = new System.IO.IsolatedStorage.IsolatedStorageFileStream ("username.txt", System.IO.FileMode.OpenOrCreate); System.IO.StreamReader sr = new System.IO.StreamReader(fs); label1.Content = "Hello " + sr.ReadToEnd(); sr.Close();
6. Press F5 to run the application. Type your name in the text box and click Set Name. Then click Get Name. Your name and a greeting are displayed in the label.
Lesson Summary Q
There are three primary types of WPF applications: Windows applications, Navigation applications, and XBAPs.
Q
WPF applications provide a traditional Windows experience. Navigation applications are page-based and provide an experience similar to that of a Web site. XBAPs are also page-based and run in Internet Explorer under limited permissions.
Q
XBAPs typically run under Internet zone security restrictions. You can use isolated storage to have limited file read/write capabilities that are application-specific.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 1, “Selecting an Application Type.” The questions also are available on the companion CD of this book if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following operations are allowed by an XBAP running under typical conditions? (Choose all that apply.) A. Reading from the registry B. Accessing isolated storage C. Writing to the registry D. Displaying graphics
20
Chapter 1
WPF Application Fundamentals
2. Which of the following code snippets will instantiate and display a new instance of the Window1 class? A. ' VB Window1.Show() // C# Window1.Show();
B. ' VB Window1.Show(New Window1()) // C# Window1.Show(new Window1());
C. ' VB Dim aWindow As New Window1() aWindow.Show(New Window1()) // C# Window1 aWindow = new Window1(); aWindow.Show(new Window1());
D. ' VB Dim aWindow As New Window1() Window1.Show // C# Window1 aWindow = new Window1(); aWindow.Show();
Lesson 2: Configuring Page-Based Navigation
21
Lesson 2: Configuring Page-Based Navigation In Lesson 1, we touched briefly on pages and page-based applications. You learned that there are two primary types of page-based applications: Navigation applications and XBAPs. This lesson looks at page-based applications in more depth. After this lesson, you will be able to: Q
Create and use a PageFunction
Q
Use the NavigationService
Q
Use the journal
Q
Incorporate hyperlinks into your page-based WPF application
Q
Handle navigation events
Q
Describe when to use structured navigation and when to use simple navigation
Estimated lesson time: 30 minutes
Using Pages Unlike Windows applications, which are toolbar- and menu-driven, page-based applications are navigation-driven, meaning that the flow of the program is driven by navigating through multiple pages rather than interacting with existing windows by using menu and toolbar commands. Although the page-based model is limited in some ways, it lends itself very well to lightweight applications that are focused around a single task, such as a wizard or a shopping cart application. This lesson will focus primarily on the navigation of page-based applications.
Hosting Pages in Frames You have seen that an XBAP is hosted in Internet Explorer and a Navigation application is hosted in a NavigationWindow, but you also can host a page inside a control called a frame. A frame is simply a host for a XAML page or a Web page and is itself hosted inside a page or window. The Source property of the Frame control indicates the page to be loaded into the frame. The following code demonstrates how to set the source for a frame in XAML:
22
Chapter 1
WPF Application Fundamentals
Using Hyperlinks The most familiar method of page-based navigation is by using hyperlinks. Hyperlinks are displayed as a section of text, usually underlined and in a different color than the surrounding text, which the user can click. When the user clicks a hyperlink, the application navigates to the page indicated by the hyperlink. Hyperlinks expose a property called NavigateUri that indicates the target of the hyperlink. You set the NavigateUri property in XAML to indicate the navigation target when the hyperlink is clicked, as shown here: This is a hyperlink
Hyperlinks are not controls themselves—rather, they are inline flow elements. That means they must be placed within another element that supports inline flow elements, such as a TextBlock element. When a hyperlink pointing to another XAML page is clicked, a new instance of that page is created and the application navigates to that page. A hyperlink also can point to a PageFunction, but it is not possible to return a value from a PageFunction using a hyperlink. (PageFunction objects are discussed in greater detail later in this lesson.) In addition to linking to other WPF pages, hyperlinks can link your application to Web pages. You can link an application to a Web page by supplying the Hypertext Transfer Protocol (HTTP) address in the NavigateUri property, as shown here: This is a hyperlink
Setting the NavigateUri Property Dynamically You can set the NavigateUri property of a hyperlink dynamically in code. This allows you to change the navigation target of a hyperlink in response to program conditions. To change the NavigateURI property dynamically, you must set the Name property of the hyperlink in XAML, as shown here in bold: This is a hyperlink
Then you can set the NavigateUri property in code, as shown here: ' VB myLink.NavigateUri = New System.Uri("Page2.xaml", System.UriKind.Relative) // C# myLink.NavigateUri = new System.Uri("Page2.xaml", System.UriKind.Relative);
Lesson 2: Configuring Page-Based Navigation
23
Fragment Navigation If the target page of a hyperlink is scrollable (that is, it is hosted in Internet Explorer or uses the ScrollViewer control), you can use fragment navigation to also specify an element in the target page. Specify the element by adding the number sign (#) followed by the name of the element, as shown here: This is a hyperlink
Using NavigationService Hyperlinks provide a fairly easy way to navigate between pages, but for more complicated navigational models, the NavigationService class provides finer control. You can obtain a reference to the NavigationService class by calling the static GetNavigationService method, as shown here: ' VB Dim myNav As NavigationService myNav = NavigationService.GetNavigationService(Me) // C# NavigationService myNav; myNav = NavigationService.GetNavigationService(this);
The NavigationService exposes a method called Navigate, which causes the application to navigate to the specified page. The most common way to use the Navigate method is to provide an instance of a Uniform Resource Identifier (URI), as shown here: ' VB myNav.Navigate(New System.Uri("Page2.xaml", UriKind.Relative)) // C# myNav.Navigate(new System.Uri("Page2.xaml", UriKind.Relative));
You also can create an instance of a new page in memory and navigate to it with the Navigate method, as shown here: ' VB Dim aPage As New Page2() myNav.Navigate(aPage) // C# Page2 aPage = new Page2(); myNav.Navigate(aPage);
24
Chapter 1
WPF Application Fundamentals
There are advantages and disadvantages to each method. When passing a URI to the Navigate method, the application’s journal can maintain the page data without having to maintain the entire page object in memory. Thus, memory overhead is lower using this method. However, it is not possible to pass information between pages using a URI. You can pass information between pages by creating a custom constructor for your page and using it to pass information or by setting properties on the page prior to navigating to it. You can use the NavigationService to refresh your page by calling NavigationService.Refresh. NavigationService also allows you to navigate forward and backward in the journal by calling NavigationService.GoForward and NavigationService.GoBack, respectively. These methods are demonstrated here: ' VB myNav.Refresh() myNav.GoForward() myNav.GoBack() // C# myNav.Refresh(); myNav.GoForward(); myNav.GoBack();
The NavigationService also exposes two boolean properties called CanGoBack and CanGoForward, which you can query to determine if the application can navigate backward or forward. An example is shown here: ' VB If myNav.CanGoBack = True Then myNav.GoBack() End If // C# if (myNav.CanGoBack) myNav.GoBack();
Navigation is asynchronous. Thus, you can cancel navigation before it is completed by calling NavigationService.StopLoading, as shown: ' VB myNav.StopLoading() // C# myNav.StopLoading();
Lesson 2: Configuring Page-Based Navigation
25
Quick Check Q
When would you use NavigationService to navigate instead of hyperlinks?
Quick Check Answer Q
NavigationService provides a finer level of control over navigation than hyperlinks. You would use hyperlinks when you don’t need to control navigation closely, but you would use NavigationService when you want to use the journal, or want to be able to halt or change navigation programmatically.
Using the Journal The journal is a bit of built-in technology in XBAPs and Navigation applications that keeps a list of the pages that have been visited and allows you to navigate this list. This will be familiar to anyone who uses Internet Explorer—the Back button navigates backward in the history to previously visited pages. The NavigationService allows you to manipulate the contents of the journal.
Removing Items from the Journal You might want to remove items from the journal. For example, suppose your application has a complex configuration step that runs through several pages initially, but is required only once. After configuration, you might want to remove these journal entries so the user could navigate the regular pages without reloading the configuration pages. Removing items from the journal is fairly straightforward. NavigationService provides a method called RemoveBackEntry, which removes the last entry in the journal and returns an instance of JournalEntry that describes the instance that was removed. The following example demonstrates how to remove the last item from the journal: ' VB myNav.RemoveBackEntry() // C# myNav.RemoveBackEntry();
You can use the CanGoBack property to remove all the items in the journal, as shown here: ' VB While myNav.CanGoback myNav.RemoveBackEntry() End While
26
Chapter 1
WPF Application Fundamentals
// C# while (myNav.CanGoBack) { myNav.RemoveBackEntry(); }
Adding Items to the Journal Adding items to the journal is considerably less straightforward than removing them. In general, you want to add items to the journal only when you want to take a “snapshot” of the state of a single page and allow the user to navigate back to previous states. For example, if you were performing a complex configuration task with multiple steps on a single page, you could provide custom journal entries to allow the user to roll back changes before they were committed. NavigationService provides a method called AddBackEntry, but it is more difficult to use than it appears and is considerably more complicated than RemoveBackEntry. It takes a single parameter, which is an instance of a class that derives from CustomContentState. This class, which you must implement, stores the state information for the page and reconstitutes the page state when the custom entry is navigated to. You also must implement the IProvideCustomContentState interface in the page for which you want to provide custom journal entries. Finally, you must add the custom journal entry manually at the point that you want to take the snapshot. The following is a high-level procedure that describes the general protocol for adding custom journal entries. Note that this is just an overview of the process—there is no sample code for this procedure. You will implement custom journal entries as part of this lesson’s lab. This procedure is not as complicated as it seems. After you complete the lab in this lesson, the procedure will seem much clearer.
To add custom journal entries
1. Create a class that inherits CustomContentState. (You need a separate class for each page for which you want to add custom entries.) This class also must be marked with the Serializable attribute. 2. Add member variables and public properties to this class that hold the state of each control on the page that you want to constitute. 3. Add code to override the JournalEntryName property, which indicates the name that will be displayed in the journal. Often you might want to set this value in the constructor for this class, or use a method to determine an automatic name. 4. Override the Replay method. This method is called when the application navigates backward or forward in the journal and is used to reconstitute the page. Although there are a few different approaches here, the best method is to create
Lesson 2: Configuring Page-Based Navigation
27
a callback method that executes a method in the page that receives the class instance as a parameter, thereby allowing the page access to the stored data. 5. Create a constructor for this class. The constructor should set the value of all data about the state of the page that needs to be stored. It also should indicate the address of the callback method for the Replay method and any other parameters that you need for this instance (such as the JournalEntryName). 6. In the page for which a custom journal entry will be created, create a method that handles the callback from the Replay method. This method should use the information in the passed parameter to restore the state of the page. 7. Implement the IProvideCustomContentState in the page. This involves implementing the method GetContentState. GetContentState must return a CustomContentState object—you return an instance of your class in this method. 8. Add code that calls the NavigationService.AddBackEntry method at each point for which you want to create a custom journal entry. Each time you call this method, you must provide a new instance of your class to save the custom state. Exam Tip
Adding items to the journal is a complicated procedure that requires several individual steps. Make sure that you understand how and when to perform each step in this process.
Handling Navigation Events Navigation in WPF applications occurs asynchronously. Thus, the NavigationService. Navigate method will return before navigation is complete. NavigationService exposes several events that allow your application to react at different points in the navigation process. You can handle these events to provide custom validation, to update navigation progress, or to add any other custom navigation functionality that is required. Table 1-2 summarizes the navigation events exposed by NavigationService. The events are listed in the order in which they occur. Table 1-2
Navigation Events Exposed by NavigationService
Event
Description
Navigating
The Navigating event occurs just as navigation begins.
Navigated
The Navigated event occurs after navigation has been initiated but before the target page has been retrieved.
28
Chapter 1
WPF Application Fundamentals
Table 1-2
Navigation Events Exposed by NavigationService
Event
Description
NavigationProgress
The NavigationProgess event is raised after every 1 kilobyte (KB) of data has been received from the new page.
LoadCompleted
The LoadCompleted event is raised after the page has finished loading but before any of the page events fire.
FragmentNavigation
The FragmentNavigation event occurs as the page is about to be scrolled to the target element. This event does not fire if you do not use a URI with a target element.
NavigationStopped
The NavigationStopped event fires when the StopLoading method is called. Note that this event is not fired if navigation is canceled in the Navigating event handler.
NavigationFailed
The NavigationFailed event is raised if the requested page cannot be located or downloaded.
Note that the NavigationService events fire whether navigation occurs through the NavigationService or through hyperlink clicks. Because NavigationService events are regular .NET events, and not routed events (which will be discussed in Chapter 2, “Events, Commands, and Settings”), you can create event handlers by creating methods with the correct signature and then attaching them to the event with the AddHandler operator (in Visual Basic) or the += operator (in C#), as shown here: ' VB Public Sub HandleNavigated(ByVal sender As Object, _ ByVal e As System.Windows.Navigation.NavigationEventArgs) ' Event Handling Code goes here End Sub
Lesson 2: Configuring Page-Based Navigation
29
Public Sub HookupEventHandler() ' Hookup the event handler AddHandler NavigationService.Navigated, AddressOf HandleNavigated End Sub // C# public void HandleNavigated(object sender, System.Windows.Navigation.NavigationEventArgs e) { // Event handling code goes here } public void HookupEventHandler() { NavigationService.Navigated += HandleNavigated; }
Passing Information to Navigation Events The NavigationService.Navigate method exposes overloads that allow you to pass additional information that becomes available when navigation events are being handled. For example, you might pass time stamp information, or an object that could be used to validate the page request. To pass additional information on to the event handlers, simply call one of the overloads of NavigationService.Navigate that takes an additional object parameter, as shown here: ' VB NavigationService.Navigate(New System.Uri("page2.xaml"), "user = Joe") // C# NavigationService.Navigate(new System.Uri("page2.xaml"), "user = Joe");
The additional information will be available in the Navigated, NavigationStopped, and LoadCompleted events through the e.ExtraData property, as shown here: ' VB Public Sub Navigate(ByVal sender As Object, ByVal e As _System.Windows.Navigation.NavigationEventArgs) If e.ExtraData.ToString = "user=Kilroy" Then Trace.WriteLine("Kilroy was here") End If End Sub // C# public void Navigate(object sender, System.Windows.Navigation.NavigationEventArgs e) { if (e.ExtraData.ToString() == "user=Kilroy") Trace.WriteLine("Kilroy was here"); }
30
Chapter 1
WPF Application Fundamentals
Cancelling Navigation You can cancel navigation in the Navigating event handler by setting the e.Cancel property to True, as shown here: ' VB Public Sub NavigatingHandler(ByVal sender As Object, _ ByVal e As System.Windows.Navigation.NavigatingCancelEventArgs) e.Cancel = True End Sub // C# public void NavigatingHandler(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e) { e.Cancel = true; }
Using PageFunction Objects The PageFunction class is very similar to the Page class. You can design a PageFunction in the designer, you can add controls to a PageFunction, and you can navigate to a PageFunction through hyperlinks or by using NavigationService. The principal difference between Page objects and PageFunction objects is that PageFunction objects can return a value. This allows you to create pages that act in an analogous manner to dialog boxes—they can collect user information and then return that information to the main page.
To add a PageFunction object to a project
1. From the Project menu, choose Add New Item to open the Add New Item dialog box. 2. In the Add New Item dialog box, select PageFunction (WPF). Name your PageFunction and click Add. A PageFunction can return any type of .NET object. When you add a PageFunction to your project, Visual Studio automatically configures it to return a String instance. Although this is frequently useful, you might want to return some other kind of object from a PageFunction, such as an integer or an object. Changing the return type of your PageFunction is relatively straightforward.
To change the return type of your PageFunction
1. In XAML view, locate the line in the PageFunction XAML that reads: x:TypeArguments="sys:String"
Then change the TypeArguments parameter to the type you want, as follows: x:TypeArguments="sys:Object"
For Visual Basic, that is all you need to do. For C#, an additional step is required.
Lesson 2: Configuring Page-Based Navigation
31
2. In Code view, locate the class declaration and change the type, as shown here: public partial class PageFunction1 : PageFunction
When you are ready for your PageFunction to return a value, you should call the OnReturn method. The OnReturn method takes a parameter of the type specified for the PageFunction. You also can return null for this parameter if no return value is required. The page that navigated to the PageFunction should handle the Returned event for that PageFunction. The instance of ReturnEventArgs returned by that event contains the returned value.
To return a value from a PageFunction
1. In the page that navigates to the PageFunction, create a method that handles the Returned method of that PageFunction. An example is shown here: ' VB Public Sub ReturnHandler(ByVal sender As Object, _ ByVal e As ReturnEventArgs(Of String)) myString = e.Result End Sub // C# public void ReturnHandler(object sender, ReturnEventArgs e) { myString = e.Result; }
2. In the page that navigates to the PageFunction, instantiate the PageFunction programmatically and add code to hook up the PageFunction.Returned event to the new event handler, as shown here: ' VB Dim myPage As New PageFunction1 AddHandler myPage.Return, AddressOf ReturnHandler // C# PageFunction1 myPage = new PageFunction1(); myPage.Return += ReturnHandler;
3. In the PageFunction, after the task is completed, call the OnReturn method and pass the return value in a new instance of ReturnEventArgs, as shown here: ' VB OnReturn(New ReturnEventArgs(Of String)("Kilroy was here")) // C# OnReturn(new ReturnEventArgs("Kilroy was here"));
32
Chapter 1
WPF Application Fundamentals
Removing PageFunction Entries from the Journal Because PageFunction objects are used frequently to collect user input, you might not want to allow the user to return to a PageFunction via the journal after the task is completed. The PageFunction class exposes a property called RemoveFromJournal. When RemoveFromJournal is set to True, PageFunction entries are deleted automatically from the journal once the user is finished with the task.
Simple Navigation and Structured Navigation Simple navigation is a common design model in lightweight page-based applications. An application with simple navigation has a start, an end, and a series of pages though which the user navigates. There is generally little or no branching, and after a page is visited, it generally is not returned to unless the user wants to back up. Although this paradigm is well suited to certain types of applications, such as a configuration wizard, other kinds of applications might find it lacking. Consider a shopping cart application. A user might want to add items to a shopping cart, return to shop for more items, add them to the shopping cart, repeat this a few more times, and then check out. Strictly linear navigation would be insufficient in this case. PageFunction objects allow you to build more structure into your application. With PageFunction objects, you can allow your users to leave the main line of execution to perform tasks and then return. Using PageFunction objects, you can create execution models with complex flow structures, and by manipulating the journal, you can control how a user is able to navigate back through the application.
Lab: The Pizza Kitchen In this lab, you create a page-based application that simulates an online pizza-ordering system. You implement a PageFunction that allows users to select toppings and implement custom journal entries to allow users to back up in their selections. You then return their selections to a main page and navigate to an order confirmation page.
Exercise 1: Returning Data from a PageFunction
1. From the companion CD, open the Chapter 1, Lesson 2 partial solution in Visual Studio. This partial solution contains one Page and one PageFunction. You implement functionality to pass information between the Page and PageFunction. 2. In the XAML view for PageFunction1, change the value of the x:TypeArguments property from sys:String to sys:Object.
Lesson 2: Configuring Page-Based Navigation
33
3. In C# only: In the code view, change the class declaration to read as follows: public partial class PageFunction1 : PageFunction
4. In the code view for Page1, locate the Button1_Click method. Add the following line of code beneath the first line already in this method: ' VB AddHandler apage.Return, New ReturnEventHandler(Of Object)(AddressOf _ apage_Return) // C# apage.Return += new ReturnEventHandler(apage_Return);
5. Beneath this method, add the following method to handle the apage.Return event: ' VB Private Sub apage_Return(ByVal sender As Object, _ ByVal e As ReturnEventArgs(Of Object)) Dim alist As List(Of String) = CType(e.Result, List(Of String)) For Each s As String In alist listBox1.Items.Add(s) Next End Sub // C# void apage_Return(object sender, ReturnEventArgs e) { List alist = (List)e.Result; foreach (string s in alist) listBox1.Items.Add(s); }
6. In the designer for PageFunction1, double-click the Finished With Toppings button to open the event handler for the Click event. Add the following code: ' VB Private Sub Button3_Click(ByVal sender As System.Object, _ ByVal e As System.Windows.RoutedEventArgs) Handles Button3.Click Dim alist As List(Of String) = New List(Of String) For Each l As ListBoxItem In listBox2.Items alist.Add(l.Content.ToString) Next Dim ee As ReturnEventArgs(Of Object) = _ New ReturnEventArgs(Of Object)(CType(alist, Object)) OnReturn(ee) End Sub // C# private void Button3_Click(object sender, RoutedEventArgs e) { List alist = new List();
34
Chapter 1
WPF Application Fundamentals
foreach (ListBoxItem l in listBox2.Items) alist.Add(l.Content.ToString()); ReturnEventArgs ee = new ReturnEventArgs((object)alist); OnReturn(ee); }
7. Press F5 to build and run your application. In the opening page, click Select Toppings to open the PageFunction. Select toppings in the top list box and click Add Topping to add a topping to the lower list box. When completed, click Finished With Toppings. The PageFunction returns and the list of toppings appears in the list box on Page1.
Exercise 2: Implementing Custom Journal Entries 1. In the code view for PageFunction1, beneath the PageFunction1 class declaration, create a new class that inherits CustomContentState, as shown here: ' VB _ Public Class CustomJournalEntry Inherits CustomContentState End Class // C# [Serializable()] public class CustomJournalEntry : CustomContentState { }
2. Create two properties and corresponding member variables to hold the state of the available and chosen toppings, as shown: ' VB private atops As List(Of ListBoxItem) private ctops As List(Of ListBoxItem) Public Property AvailableToppings As List(Of ListBoxItem) Get Return atops End Get Set(ByVal Value As List(Of ListBoxItem)) atops = Value End Set End Property Public Property ChosenToppings As List(Of ListBoxItem) Get Return ctops End Get Set(ByVal Value As List(Of ListBoxItem)) ctops = Value End Set End Property
Lesson 2: Configuring Page-Based Navigation
35
// C# private List atops; private List ctops; public List AvailableToppings { get { return atops; } set { atops = value; } } public List ChosenToppings { get { return ctops; } set { ctops = value; } }
3. Override the JournalNameEntry property to add code to set the journal entry name, as shown here: ' VB Public Overrides ReadOnly Property JournalEntryName() As String Get Return "Custom Journal Entry" End Get End Property // C# public override string JournalEntryName { get { return "Custom Journal Entry"; } }
4. Create a new delegate called ReplayDelegate that takes a single parameter—an instance of CustomJournalEntry. Also create a private member variable that represents an instance of this delegate, as shown: ' VB Public Delegate Sub ReplayDelegate(ByVal c As CustomJournalEntry) Private rplaydelegate As ReplayDelegate
36
Chapter 1
WPF Application Fundamentals
// C# public delegate void ReplayDelegate(CustomJournalEntry c); private ReplayDelegate replaydelegate;
5. Override the Replay method to call the private member instance of the ReplayDelegate, as shown here: ' VB Public Overrides Sub Replay(ByVal navigationService As _ System.Windows.Navigation.NavigationService, ByVal mode As _ System.Windows.Navigation.NavigationMode) Me.rplaydelegate(Me) End Sub // C# public override void Replay(NavigationService navigationService, NavigationMode mode) { this.replaydelegate(this); }
6. Create a constructor for this class that sets the values for AvailableToppings and ChosenToppings and provides a delegate for the ReplayDelegate member variable. An example is shown here: ' VB Public Sub New(ByVal available As List(Of ListBoxItem), _ ByVal chosen As List(Of ListBoxItem), ByVal replay As ReplayDelegate) atops = available ctops = chosen rplaydelegate = replay End Sub // C# public CustomJournalEntry(List available, List chosen, ReplayDelegate replay) { atops = available; ctops = chosen; replaydelegate = replay; }
7. In the code for PageFunction1, add a method that will handle the callback from the replay method. This code should take the data passed in the CustomJournalEntry parameter to restore the state of the page that was represented by the CustomJournalEntry. An example is shown here: ' VB Private Sub ReplayCallback(ByVal c As CustomJournalEntry) listBox1.Items.Clear() listBox2.Items.Clear() For Each l As ListBoxItem In c.AvailableToppings listBox1.Items.Add(l) Next
Lesson 2: Configuring Page-Based Navigation
37
For Each ll As ListBoxItem In c.ChosenToppings listBox2.Items.Add(ll) Next End Sub // C# private void ReplayCallback(CustomJournalEntry c) { listBox1.Items.Clear(); listBox2.Items.Clear(); foreach (ListBoxItem l in c.AvailableToppings) listBox1.Items.Add(l); foreach (ListBoxItem ll in c.ChosenToppings) listBox2.Items.Add(ll); }
8. Prepare to implement IProvideCustomContentState in your PageFunction. In Visual Basic, you do this by adding the following line underneath the class declaration: Implements IProvideCustomContentState
In C#, you do this by appending the following line to your class declaration: , IProvideCustomContentState
9. Add the following implementation of the GetContentState method: ' VB Public Function GetContentState() As _ System.Windows.Navigation.CustomContentState Implements _ System.Windows.Navigation.IProvideCustomContentState.GetContentState Dim alist As New List(Of ListBoxItem) Dim blist As New List(Of ListBoxItem) For Each l As ListBoxItem In listBox1.Items alist.Add(l) Next For Each ll As ListBoxItem In listBox2.Items blist.Add(ll) Next Return New CustomJournalEntry(alist, blist, AddressOf ReplayCallback) End Function // C# public System.Windows.Navigation.CustomContentState GetContentState() { List alist = new List(); List blist = new List(); foreach (ListBoxItem l in listBox1.Items) alist.Add(l); foreach (ListBoxItem ll in listBox2.Items) blist.Add(ll); return new CustomJournalEntry(alist, blist, ReplayCallback); }
38
Chapter 1
WPF Application Fundamentals
10. Add the following code to both the Button1_Click and Button2_Click methods before any of the code already in those methods. This will save the current state as a journal entry before any changes are made to the page: ' VB Dim alist As New List(Of ListBoxItem) Dim blist As New List(Of ListBoxItem) For Each lll As ListBoxItem In listBox1.Items alist.Add(lll) Next For Each ll As ListBoxItem In listBox2.Items blist.Add(ll) Next NavigationService.AddBackEntry(New CustomJournalEntry(alist, blist, _ AddressOf ReplayCallback)) // C# List alist = new List(); List blist = new List(); foreach (ListBoxItem lll in listBox1.Items) alist.Add(lll); foreach (ListBoxItem ll in listBox2.Items) blist.Add(ll); NavigationService.AddBackEntry(new CustomJournalEntry(alist, blist, ReplayCallback));
11. Press F5 to build and run your application. In the PageFunction, note that a new journal entry is added each time you select or cancel a topping.
Lesson Summary Q
Pages can be hosted in Internet Explorer, in a NavigationWindow, or in a frame, which itself can be hosted in other pages or windows.
Q
Hyperlinks provide a familiar click-to-navigate experience for the user. You can set the NavigateUri property of hyperlinks dynamically to change the navigation target of the hyperlink.
Q
The NavigationService class provides greater functionality for navigation than hyperlinks. You can perform standard navigation tasks, as well as add and remove items from the journal and handle navigation events.
Q
PageFunction objects allow you to return a value from a page in a manner analogous to a dialog box. PageFunction objects enable you to build complex navigational structures in your applications.
Lesson 2: Configuring Page-Based Navigation
39
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 2, “Configuring Page-Based Navigation.” The questions also are available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following can host a page? (Choose all that apply.) A. Internet Explorer B. A NavigationWindow C. A frame D. A PageFunction 2. Which of the following is NOT required to implement custom back entries? A. A Page or PageFunction that implements IProvideCustomContentState B. Code that called NavigationService.AddBackEntry C. An instance of the JournalEntry class D. A class that inherits from CustomContentState 3. Which of the following is the correct firing order for navigation events? A. Navigating NavigationProgress Navigated FragmentNavigation LoadCompleted B. Navigating Navigated NavigationProgress LoadCompleted FragmentNavigation
40
Chapter 1
WPF Application Fundamentals
C. Navigating NavigationProgress Navigated LoadComplete FragmentNavigation D. Navigating Navigated NavigationProgress FragmentNavigation LoadCompleted
Lesson 3: Managing Application Responsiveness
41
Lesson 3: Managing Application Responsiveness Your application might be required to perform tasks, such as file downloads, that consume large amounts of time. Execution of these tasks on a separate thread allows you to maintain the responsiveness of the user interface while the task completes. The BackgroundWorker component is the recommended way to run time-consuming tasks in the background, leaving the user interface responsive. When the background thread needs to interact with the UI thread, you can use the Dispatcher to execute code safely on the UI thread. Freezing elements used in UI rendering makes them thread-safe and accessible by all threads. After this lesson, you will be able to: Q
Run a background process by using the BackgroundWorker component
Q
Cancel a background process by using the BackgroundWorker component
Q
Report the progress of a background process with the BackgroundWorker component
Q
Safely post data to the UI thread using the Dispatcher
Q
Explain what a freezable object is
Q
Freeze a freezable object
Estimated lesson time: 30 minutes
The BackgroundWorker component allows you to execute time-consuming operations on a separate, dedicated thread. This lets you run operations that take extended periods of time asynchronously so your application’s user interface can remain responsive. The key method of the BackgroundWorker component is the RunWorkerAsync method. When this method is called, the BackgroundWorker component raises the DoWork event. The code in the DoWork event handler is executed on a separate thread. Important members of the BackgroundWorker component are shown in Table 1-3.
42
Chapter 1
WPF Application Fundamentals
Table 1-3
Important Members of the BackgroundWorker Component
Member
Description
CancelAsync
This method requests the cancellation of a pending background operation.
CancellationPending
This property indicates whether the application has requested the cancellation of a background operation.
DoWork
This event occurs when the RunWorkerAsync method is called. Code in the DoWork event handler is run on a separate thread.
IsBusy
This property indicates whether the BackgroundWorker component is currently running an asynchronous operation.
ProgressChanged
This event occurs when ReportProgress is called.
ReportProgress
This method raises the ProgressChanged event.
RunWorkerAsync
This method starts the execution of a background operation by raising the DoWork event.
RunWorkerCompleted
This event occurs when the background operation has been completed or cancelled, or has raised an exception.
WorkerReportsProgress
This property indicates whether the BackgroundWorker component can report progress updates.
WorkerSupportsCancellation
This property indicates whether the BackgroundWorker component supports asynchronous cancellation.
Running a Background Process The RunWorkerAsync method of BackgroundWorker starts the execution of the background process by raising the DoWork event. The code in the DoWork event handler
Lesson 3: Managing Application Responsiveness
43
is executed on a separate thread. The following procedure explains how to implement and execute a background process.
To create a background thread with the BackgroundWorker component
1. In code, declare a new instance of a BackgroundWorker, as shown here: ' VB Dim WithEvents myWorker As New System.ComponentModel.BackgroundWorker // C# System.ComponentModel.BackgroundWorker myWorker = new System.ComponentModel.BackgroundWorker();
2. Create a method to handle the BackgroundWorker.DoWork event, as shown here: ' VB Private Sub myWorker_DoWork(ByVal sender As System.Object, _ ByVal e As System.ComponentModel.DoWorkEventArgs) Handles myWorker.DoWork ' Do time-consuming work here End Sub // C# private void myWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { // Do time-consuming work here }
3. In C#, in the constructor of your window or page, add code to hook up the event to the handler, as shown here: // C# only myWorker.DoWork += myWorker_DoWork;
4. Elsewhere in your code, start the time-consuming operation on a separate thread by calling the RunWorkerAsync method, as shown here: ' VB myworker.RunWorkerAsync() // C# myWorker.RunWorkerAsync();
Providing Parameters to the Process Your background process may require one or more parameters, such as the address of a file to be downloaded. You can provide a parameter in the RunWorkerAsync method that will be available as the Argument property of the instance of DoWorkEventArgs in the DoWork event handler.
44
Chapter 1
WPF Application Fundamentals
To provide a parameter to a background process
1. Include the parameter in the RunWorkerAsync call, as shown here: ' VB myWorker.RunWorkerAsync("C:\myFile.txt") // C# myWorker.RunWorkerAsync("C:\\myFile.txt");
2. Retrieve the parameter from the DoWorkEventArgs.Argument property and cast it appropriately to use it in the background process. An example is shown here: ' VB Private Sub aWorker_DoWork(ByVal sender As System.Object, _ ByVal e As System.ComponentModel.DoWorkEventArgs) Handles myWorker.DoWork Dim myPath As String myPath = CType(e.Argument, String) ' method continues here End Sub // C# private void myWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { string myPath; myPath = (string)e.Argument; // method continues here }
Note that if your process requires more than a single parameter, you may need to wrap multiple values in a custom array of structures or objects.
Returning a Value from a Background Process You might want to return a value from a background process. For example, if your process is a complex calculation, you would want to return the end result. You can return a value by setting the Result property of the DoWorkEventArgs in the DoWork event handler. This value then will be available in the RunWorkerCompleted event handler as the Result property of the RunWorkerCompletedEventArgs parameter, as shown here: ' VB Private Sub myWorker_DoWork(ByVal sender As System.Object, _ ByVal e As System.ComponentModel.DoWorkEventArgs) Handles myWorker.DoWork ' Assigns the return value of a method named ComplexCalculation to e.Result e.Result = ComplexCalculation End Sub Private Sub myWorker_RunWorkerCompleted(ByVal sender As System.Object, _ ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _ Handles myWorker.RunWorkerCompleted Dim result As Object result = e.Result End Sub
Lesson 3: Managing Application Responsiveness
45
// C# private void myWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { // Assigns the return value of a method named ComplexCalculation to // e.Result e.Result = ComplexCalculation(); } private void myWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) { object result; result = e.Result; }
Cancelling a Background Process You might want to implement the ability to cancel a background process. BackgroundWorker supports the ability to cancel a background process, but you must implement most of the cancellation code yourself. The WorkerSupportsCancellation property of the BackgroundWorker component indicates whether the component supports cancellation. You can call the CancelAsync method to attempt to cancel the operation; doing so sets the CancellationPending property of the BackgroundWorker component to True. By polling the CancellationPending property of the BackgroundWorker component, you can determine whether to cancel the operation.
To implement cancellation for a background process
1. In the Properties window, set the WorkerSupportsCancellation property to True to enable the BackgroundWorker component to support cancellation. 2. Create a method that is called to cancel the background operation. The following example demonstrates how to cancel a background operation in a Button.Click event handler: ' VB Private Sub btnCancel_Click(ByVal sender As System.Object, _ ByVal e As System.Windows.RoutedEventArgs) Handles btnCancel.Click myWorker.CancelAsync() End Sub // C# private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e) { myWorker.CancelAsync(); }
3. In the BackgroundWorker.DoWork event handler, poll the BackgroundWorker .CancellationPending property, and implement code to cancel the operation if it is
46
Chapter 1
WPF Application Fundamentals
True. You should also set the e.Cancel property to True, as shown in the following example: ' VB Private Sub myWorker_DoWork(ByVal sender As System.Object, _ ByVal e As System.ComponentModel.DoWorkEventArgs) Handles _ myWorker.DoWork For i As Integer = 1 to 1000000 TimeConsumingMethod() If myWorker.CancellationPending Then e.Cancel = True Exit Sub End If Next End Sub // C# private void myWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { for (int i = 0; i < 1000000; i++) { TimeConsumingMethod(); if (myWorker.CancellationPending) { e.Cancel = true; return; } } }
Reporting the Progress of a Background Process with BackgroundWorker For particularly time-consuming operations, you might want to report progress back to the primary thread. You can report progress of the background process by calling the ReportProgress method. This method raises the BackgroundWorker.ProgressChanged event and allows you to pass a parameter that indicates the percentage of progress that has been completed to the method that handles that event. The following example demonstrates the ReportProgress method: ' VB Private Sub myWorker_DoWork(ByVal sender As System.Object, _ ByVal e As System.ComponentModel.DoWorkEventArgs) Handles _ myWorker.DoWork For i As Integer = 1 to 10 TimeConsumingMethod() ' Calls the ReportProgress method, indicating the percentage complete myWorker.ReportProgress(I * 10) Next End Sub
Lesson 3: Managing Application Responsiveness
47
Private Sub myWorker_ProgressChanged(ByVal sender As System.Object, _ ByVal e As System.ComponentModel.ProgressChangedEventArgs) _ Handles myWorker.ProgressChanged Dim percentage As Integer percentage = e.ProgressPercentage End Sub // C# private void myWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { for (int i = 1; i Red Blue Green Tomato Change Background Color
5. In the designer, double-click button1 to open the code view to the default handler for the Click event. Add the following code: ' VB If Not listBox1.SelectedItem Is Nothing Then Dim astring As String = CType(listBox1.SelectedItem, _ ListBoxItem).Content.ToString Select Case astring Case "Red" My.Settings.BackgroundColor = Colors.Red
90
Chapter 2
Events, Commands, and Settings
Case "Blue" My.Settings.BackgroundColor = Colors.Blue Case "Green" My.Settings.BackgroundColor = Colors.Green Case "Tomato" My.Settings.BackgroundColor = Colors.Tomato End Select Me.Background = New _ System.Windows.Media.SolidColorBrush(My.Settings.BackgroundColor) My.Settings.Save() End If // C# if (!(listBox1.SelectedItem == null)) { String astring = ((ListBoxItem)listBox1.SelectedItem).Content.ToString(); switch (astring) { case "Red": Properties.Settings.Default.BackgroundColor = Colors.Red; break; case "Blue": Properties.Settings.Default.BackgroundColor = Colors.Blue; break; case "Green": Properties.Settings.Default.BackgroundColor = Colors.Green; break; case "Tomato": Properties.Settings.Default.BackgroundColor = Colors.Tomato; break; } this.Background = new System.Windows.Media.SolidColorBrush( Properties.Settings.Default.BackgroundColor); Properties.Settings.Default.Save(); }
6. Create or replace the constructor for this class with the following code to read and apply the settings: ' VB Public Sub New() InitializeComponent() Me.Title = My.Settings.ApplicationName Me.Background = New _ System.Windows.Media.SolidColorBrush(My.Settings.BackgroundColor) End Sub // C# public Window1() { InitializeComponent();
Lesson 3: Configuring Application Settings
91
this.Title = Properties.Settings.Default.ApplicationName; this.Background = new System.Windows.Media.SolidColorBrush( Properties.Settings.Default.BackgroundColor); }
7. Press F5 to build and run your application. Note that the title of the window is the value of your ApplicationName setting and the background color of your window is the value indicated by the BackgroundColor setting. You can change the background color by selecting an item in the ListBox and clicking the button. After changing the background color, close the application and restart it. Note that the background color of the application at startup is the same as it was when the previous application session ended.
Lesson Summary Q
Settings allow you to persist values between application sessions. You can add new settings at design time by using the Settings Editor.
Q
Settings can be one of two different scopes. Settings with Application scope are read-only at run time and can be changed only by altering the Settings file between application sessions. Settings with User scope are read-write at run time.
Q
You can access settings in code through My.Settings in Visual Basic, or Properties.Settings.Default in C#.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 3, “Configuring Application Settings.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following code snippets correctly sets the value of a setting called Title and persists it? A. ' VB My.Settings("Title") = "New Title" My.Settings.Save
92
Chapter 2
Events, Commands, and Settings
// C# Properties.Settings.Default["Title"] = "New Title"; Properties.Settings.Default.Save();
B. ' VB My.Settings("Title") = "New Title" // C# Properties.Settings.Default["Title"] = "New Title";
C. ' VB My.Settings.Title = "New Title" My.Settings.Save() // C# Properties.Settings.Default.Title = "New Title"; Properties.Settings.Default.Save();
D. ' VB My.Settings.Title = "New Title" // C# Properties.Settings.Default.Title = "New Title";
2. Which of the following code snippets reads a setting of type System.Windows .Media.Color named MyColor correctly? A. ' VB Dim aColor As System.Windows.Media.Color aColor = CType(My.Settings.MyColor, System.Windows.Media.Color) // C# System.Windows.Media.Color aColor; aColor = (System.Windows.Media.Color)Properties.Settings.Default.MyColor;
B. ' VB Dim aColor As System.Windows.Media.Color aColor = My.Settings.MyColor.ToColor() // C# System.Windows.Media.Color aColor; aColor = Properties.Settings.Default.MyColor.ToColor();
Lesson 3: Configuring Application Settings
C. ' VB Dim aColor As Object aColor = My.Settings.MyColor // C# Object aColor; aColor = Properties.Settings.Default.MyColor;
D. ' VB Dim aColor As System.Windows.Media.Color aColor = My.Settings.MyColor // C# System.Windows.Media.Color aColor; aColor = Properties.Settings.Default.MyColor;
93
94
Chapter 2 Review
Chapter Review To practice and reinforce the skills you learned in this chapter further, you can do any or all of the following: Q
Review the chapter summary.
Q
Review the list of key terms introduced in this chapter.
Q
Complete the case scenarios. These scenarios set up real-world situations involving the topics of this chapter and ask you to create a solution.
Q
Complete the suggested practices.
Q
Take a practice test.
Chapter Summary Q
Routed events can be raised by multiple UI elements in the visual tree. Bubbling events are raised first by the element in which they originate and then bubble up through the visual tree. Tunneling events are raised first by the topmost element in the visual tree and tunnel down to the element in which the event originates. Direct events are raised only by the element in which they originate.
Q
Elements in the visual tree can handle events that they do not themselves define. These are called attached events. You can define a handler for an attached event in the XAML that defines the element.
Q
You can use the EventManager class to register a new routed event and to register a class event handler.
Q
Commands provide an architecture that allows you to define high-level tasks, connect those tasks to a variety of inputs, define handlers that execute code when commands are invoked, and determine when a command is unavailable.
Q
You can use the built-in library of commands or create custom commands for your application. Commands can be triggered by controls, input gestures, or direct invocation.
Q
The CommandBinding object binds commands to command handlers.
Q
Settings allow you to create applications that persist between application sessions. Application scope settings are read-only at run time, and user scope settings are read-write at run time.
Q
Settings are exposed as strongly typed properties on the My.Settings object (in Visual Basic) and the Properties.Settings.Default object (in C#).
Chapter 2 Review
95
Key Terms Do you know what these key terms mean? You can check your answers by looking up the terms in the glossary at the end of the book. Q
Application Setting
Q
Bubbling Event
Q
Command
Q
Command Handler
Q
Direct Event
Q
Event Handler
Q
Gesture
Q
Routed Event
Q
Setting
Q
Tunneling Event
Q
User Setting
Case Scenarios In the following case scenarios, you will apply what you’ve learned about how to use commands, events, and settings to design user interfaces. You can find answers to these questions in the “Answers” section at the end of this book.
Case Scenario 1: Validating User Input You’re creating a form that will be used by Humongous Insurance data entry personnel to input data. The form consists of several TextBox controls that receive input. Data entry is expected to proceed quickly and without errors, but to help ensure this you will be designing validation for this form. This validation is somewhat complex—there is a set of characters that is not allowed in any text box on the form, and each text box has additional limitations that differ from control to control. You would like to implement this validation scheme with a minimum of code in order to make troubleshooting and maintenance simple.
96
Chapter 2 Review
Question Answer the following question for your manager: Q
What strategies can we use to implement these requirements?
Case Scenario 2: Humongous Insurance User Interface The front end for this database is just as complex as the validation requirements. You are faced with a front end that exposes many menu options. Furthermore, for expert users, some of the more commonly used menu items can be triggered by holding down the Ctrl key while performing various gestures with the mouse. Functionality invoked by the menu items sometimes will be unavailable. Finally, you need to allow the operator to edit data in this window quickly and easily.
Technical Requirements Q
All main menu items must have access keys, and some have mouse shortcuts.
Q
Availability of menu items must be communicated to the user in a way that is easy to understand but does not disrupt program flow.
Q
You must ensure that when a menu item is unavailable, corresponding shortcut keys and mouse gestures are also inactivated.
Q
Certain TextBox controls on the form must fill in automatically when appropriate keystrokes are entered.
Question Q
How can this functionality be implemented?
Suggested Practices Q
Create a rudimentary text editor with buttons that implement the Cut, Copy, and Paste commands.
Q
Create an application that stores a color scheme for each user and automatically loads the correct color scheme when the user opens the application.
Q
Build an application that consists of a window with a single button that the user can chase around the window with the mouse but can never actually click.
Chapter 2 Review
97
Take a Practice Test The practice tests on this book’s companion CD offer many options. For example, you can test yourself on just the content covered in this chapter, or you can test yourself on all the 70-502 certification exam content. You can set up the test so that it closely simulates the experience of taking a certification exam, or you can set it up in study mode so that you can look at the correct answers and explanations after you answer each question. MORE INFO
Practice tests
For details about all the practice test options available, see the section “How to Use the Practice Tests,” in this book’s Introduction.
Chapter 3
Building the User Interface Windows Presentation Foundation (WPF) technology provides the tools to build a dynamic and elegant user interface. There are three basic types of controls: individual and content controls, such as buttons and textboxes, which generally contain a single item; item controls, such as menus and toolbars, which typically group similar items; and layout controls, such as grids and stack panels, which provide the canvas on which you design your user interface. In this chapter, you will learn about each of these categories of controls and how to build a user interface with them.
Exam objectives in this chapter: Q
Select and configure content controls.
Q
Select and configure item controls.
Q
Select and configure layout controls.
Lessons in this chapter: Q
Lesson 1: Using Content Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Q
Lesson 2: Using Item Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
Q
Lesson 3: Using Layout Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Before You Begin To complete the lessons in this chapter, you must have Q
A computer that meets or exceeds the minimum hardware requirements listed in the “About This Book” section at the beginning of the book
Q
Microsoft Visual Studio 2008 Professional Edition installed on your computer
Q
An understanding of Microsoft Visual Basic or C# syntax and familiarity with the Microsoft .NET Framework
99
100
Chapter 3
Building the User Interface
Real World Matthew Stoecker The WPF user interface (UI) model has a lot of intuitive features built into the mix. I no longer have to write complicated resizing code, for example. My user interfaces that are built with WPF look cleaner and sleeker than their Windows Forms counterparts, and they are easier to design as well.
Lesson 1: Using Content Controls
101
Lesson 1: Using Content Controls After this lesson, you will be able to: Q
Explain what a content control is
Q
Describe and use several common WPF controls
Q
Use a dependency property
Estimated lesson time: 30 minutes
WPF Controls Overview There are three basic types of controls in WPF. First, there are individual controls, which are controls that correspond to many of the familiar controls from Windows Forms programming. Controls such as Button, Label, and Textbox are familiar to developers and users alike. These controls generally have a single purpose in an application—buttons get clicked, text boxes receive and display text, etc. A subset of these controls are content controls, which are designed to display a variety of kinds of content. Content controls typically can contain a single nested element. These controls will be discussed later in this lesson. A second kind of WPF control is the item control, which are designed to contain groups of related items. Examples of these include ListBox controls, Menu controls, and TreeView controls. These controls typically allow the user to select an item from a list and perform an action with that item. Item controls can contain multiple nested elements. These controls will be discussed in Lesson 2, “Using Item Controls.” Finally, there are layout controls, which contain multiple nested controls of any type and provide built-in logic for the visual layout of those controls. Examples include Grid, StackPanel, and Canvas. These controls will be discussed in Lesson 3, “Using Layout Controls.”
Content Controls Many of the controls you use to build your WPF application are content controls. Simply, a content control derives from the ContentControl class and can contain a single nested element. This nested element can be of any type and can be set or retrieved in code via the Content property. The following Extensible Application Markup Language
102
Chapter 3
Building the User Interface
(XAML) example (in bold) shows setting the content of a Button control to a string value: This is the content string
You also can set the content in code, as shown in the following example: ' VB Button2.Content = "This is the content string" // C# button2.Content = "This is the content string";
The type of the Content property is Object, so it can accept any object as content. How content is rendered, however, depends on the type of the object in the Content property. For items that do not derive from UIElement, the ToString method is called and the resulting string is rendered as the control content. Items that derive from UIElement, however, are displayed as contained within the content control. The following example code demonstrates how to render a button that has an image as its content:
Assuming that the path to the image is valid, this code will render a button that displays a picture file of a humpback whale named HumpbackWhale.jpg. Note that even though content controls can contain only a single nested element, there is no inherent limit on the number of nested elements that the content can contain. For example, it is possible for a content control to host a layout control that itself contains several additional UI elements. The following code shows a simple example of a Button with a nested StackPanel that itself has nested elements: This is a Humpback Whale
At run time, this will be rendered as an image of a humpback whale with text beneath it.
Label Control The Label control is one of the simplest WPF controls. It is mostly just a container for content. Typical usage for a Label control is as follows: This is a Label
Lesson 1: Using Content Controls
103
Labels and Mnemonic Keys
Labels contain built-in support for mnemonic keys. These are keys that move the focus to a designated control when the Alt key is pressed with the mnemonic key. For example, if R were the mnemonic key for a particular control, the focus would shift to that control when Alt+R is pressed.
Typical usage for mnemonic keys in labels occurs when the label designates a control that can receive the focus, such as TextBox. The mnemonic key is specified by preceding the desired key with the underscore (_) symbol, and appears underlined at run time when the Alt key is pressed. For example, the following code appears as Press Alt+A at run time when the Alt key has been pressed: Press Alt+_A
Although this code designates the mnemonic key for the label, it has no effect unless a target control is designated as well. You can designate a target control by setting the Target property of the Label control. The following example demonstrates how to create a mnemonic key with a target control named TextBox1: _Name
The syntax exemplified by {Binding ElementName=TextBox1} will be discussed further in Chapter 6, “Converting and Validating Data.”
Button Control The Button control should be familiar to most developers. This control is designed to be clicked to allow the user to make a choice, to close a dialog box, or to perform another action. You can execute code when the button is clicked by handling the Click event. (For information about handling events, see Chapter 2, “Events, Commands, and Settings.”) The Button control exposes two important properties that make it useful when building user interfaces: the IsDefault property and the IsCancel property. The IsDefault property determines whether a particular button is considered the default button for the user interface. When IsDefault is set to True, the button’s Click event is raised when the Enter key is pressed. Similarly, the IsCancel property determines whether the button should be considered a Cancel button. When IsCancel is set to True, the button’s Click event is raised when the Esc key is pressed.
104
Chapter 3
Building the User Interface
Access Keys
Buttons provide support for access keys, which are similar to the mnemonic keys supported by labels. When a letter in a button’s content is preceded by an underscore symbol (_), that letter will appear underlined when the Alt key is pressed, and the button will be clicked when Alt and that key are pressed together. For example, assume you have a button defined as follows: _Click Me!
The text in the button appears as “Click Me” when the Alt key is pressed and the button is clicked when Alt+C is pressed. If more than one button defines the same access key, neither is clicked when the access key combination is pressed, but focus alternates between the buttons that define that key. Checkbox Control
The Checkbox control actually inherits from the ButtonBase class and typically is used to allow the user to select whether an option is on or off. You can determine whether or not a check box is selected by accessing the IsChecked property. The IsChecked property is a Boolean? (bool? in C#), which is a data type similar to the Boolean type but allows an indeterminate state as well. A check box will be in the indeterminate state when a window first opens. Because Checkbox inherits from ButtonBase, it raises a Click event whenever the check box is selected or cleared by the user. The best way to react to a check box being selected or cleared is to handle the Click event.
RadioButton Control
Like Checkbox, RadioButton inherits from the ButtonBase class. RadioButton controls typically are used in groups to allow the user to select one option from a group. Clicking a radio button causes the Click event to be raised, which can be used to react to user choices. A fundamental feature of RadioButton controls is that they can be grouped. In a group of RadioButton controls, selecting one automatically clears all the others. Thus, it is not possible for more than one radio button in a group to be selected at one time. Usually, all RadioButton controls in a single container are automatically in the same group. If you want to have a single group of three RadioButton controls in a window, all you need to do is add them to your window—they automatically are grouped. You can have multiple groups in a single container by setting the GroupName property. The following example demonstrates two groups of two radio buttons each:
Button 1 Button 2
Lesson 1: Using Content Controls
105
Button 3 Button 4
You also can create groups of radio buttons by wrapping them in containers, such as in the code shown here: Button 1 Button 2 Button 3 Button 4
Exam Tip Content controls are an important new concept in the WPF programming paradigm. It is important to realize that even though a content control can host only a single element, the element that it hosts can itself host child elements. Thus, a content control might host a Grid, which in turn might host a number of objects.
Other Controls There are other controls in the WPF suite that are not content controls. They do not have a Content property and typically are more limited in how they are displayed or are more specialized in terms of the content that they display. For example, the TextBlock control is designed to display text, and the Image control represents an image.
TextBlock Control TextBlock is one of the simplest WPF elements. It just represents an area of text that appears in a window. The following example demonstrates a TextBlock control: Here is some text
If you want to change the text in a TextBlock in code, you must set the Name property of the TextBlock so that you can refer to it in code, as shown here: Here is some text
Then you can change the text or any other property by referring to it in the code, as shown here: ' VB TextBlock1.Text = "Here is the changed text"
106
Chapter 3
Building the User Interface
// C# TextBlock1.Text = "Here is the changed text";
By default, the font of the text in the TextBlock element will be the same as the font of the window. If you want different font settings for the TextBlock, you can set fontrelated properties, as shown below: Here is some text
Image Control The Image control represents an image. The chief property of the Image control is the Source property. This property takes a System.Windows.Media.ImageSource class in code, but when set in the XAML it can be set as the Uniform Resource Identifier (URI) from which the image is loaded. For example, look at the following code:
The URI can be either a local disk resource or a Web resource. The Image.Stretch property determines how an image is displayed, whether it is shown at actual size and cropped (if necessary) to fit the image bounds, or whether it is shrunk or stretched to fit the bounds of the Image control. Table 3-1 describes the possible values for the Stretch property. Table 3-1
Values for the Stretch Property
Value
Description
None
The image content is presented at its original size. If necessary, it is cropped to fit the available space.
Fill
The image content is resized (stretched or shrunk, as needed) to fit the Image control size.
Uniform
The image content is resized to fit the destination dimensions while preserving its native aspect ratio. No cropping will occur, but unfilled space on the Image control edges might result.
UniformToFill
The image content is resized to fit the destination dimensions while preserving its native aspect ratio. If the aspect ratio of the Image control differs from the image content, the content is cropped to fit the Image control.
Lesson 1: Using Content Controls
107
TextBox Control The TextBox control is designed for the editing and display of text. The Textbox control allows the user to type text into the user interface. That text is accessible later by the application in the TextBox.Text property. You can use a TextBox solely for text display by setting the IsReadOnly property to True, as shown in bold here:
The preceding code disables user input for the TextBox1 control. Although the TextBox control can be created as a rectangle of any size, it is single-line by default. To enable text wrapping in a TextBox, set the TextWrapping property to Wrap, as shown in bold here:
You can also set the TextWrapping property to WrapWithOverflow, which allows some words to overflow the edges of the text box if the wrapping algorithm is unable to break the text in an appropriate location. The TextBox control includes automatic support for scroll bars. You can enable vertical scroll bars by setting the VerticalScrollBarVisibility property to Auto or Visible, as shown in bold here:
Setting VerticalScrollBarVisibility to Visible will make the vertical scroll bar visible at all times, whereas setting it to Auto will make the vertical scroll bar appear only when scrollable content is present. You also can enable a horizontal scroll bar by setting the HorizontalScrollBar property, but doing this is less useful.
ProgressBar Control The ProgressBar control is designed to allow the application to provide visual feedback to the user regarding the progress of a time-consuming task. For example, you might use a progress bar to display progress for a file download. The progress bar appears as an empty box that gradually fills in to display progress. Important properties of the ProgressBar control are shown in Table 3-2.
108
Chapter 3
Building the User Interface
Table 3-2
Properties of the ProgressBar Control
Property
Description
IsEnabled
Determines whether the ProgressBar is enabled.
IsIndeterminate
Determines whether the progress bar is showing the actual value or generic progress. When IsIndeterminate is False, the progress bar will show the actual value represented by the Value property. When True, it will show generic progress.
LargeChange
Represents the amount added to or subtracted from the Value property when a large change is required.
Maximum
The Maximum value for the ProgressBar control. When the Value property equals the Maximum property, the ProgressBar control is filled.
Minimum
The Minimum value for the ProgressBar control. When the Value property equals the Minimum property, the ProgressBar control is empty.
Orientation
Determines whether the progress bar is shown horizontally or vertically.
SmallChange
Represents the amount added to or subtracted from the Value property when a small change is required.
Value
The Value displayed in the ProgressBar control. The Value will always be between the values of the Minimum and Maximum properties.
In code you can change the ProgressBar display by adding to or subtracting from the Value property, as shown here: ' VB ' Adds 1 to the Value ProgressBar1.Value += 1 // C# // Adds 1 to the Value ProgressBar1.Value += 1;
Slider Control The Slider control allows the user to set a value by grabbing a graphic handle, or thumb, with the mouse and moving it along a track. This is often used to control volume, color
Lesson 1: Using Content Controls
109
intensity, or other application properties that can vary along a continuum. Important Slider properties are shown in Table 3-3. Table 3-3
Properties of the Slider Control
Property
Description
IsDirectionReversed
Determines whether the direction is reversed or not. When set to False (the default), the minimum value is on the left and the maximum value is on the right. When set to True, the minimum is on the right and the maximum is on the left.
IsEnabled
Determines whether the slider is enabled.
LargeChange
Represents the amount added to or subtracted from the Value property when a large change is required. This amount is added or subtracted from the slider when the user clicks it on either side of the thumb or uses the PageUp or PageDown key.
Maximum
The maximum value for the Slider control. When the Value property equals the Maximum property, the thumb is completely on the right side of the slider (assuming the default direction and orientation of the control).
Minimum
The minimum value for the Slider control. When the Value property equals the Minimum property, the thumb is completely on the left side of the slider (assuming the default direction and orientation of the control).
Orientation
Determines whether the slider is shown horizontally or vertically.
SmallChange
Represents the amount added to or subtracted from the Value property when a small change is required. This amount is added to or subtracted from the slider when you use the arrow keys.
TickFrequency
Sets the interval between ticks that are displayed in the Slider control.
TickPlacement
Determines the location of ticks in the Slider control. The default setting is None, meaning that no tick marks appear.
110
Chapter 3
Building the User Interface
Table 3-3
Properties of the Slider Control
Property
Description
Ticks
Used in advanced applications. You can determine the exact number and placement of tick marks by setting the Ticks collection directly.
Value
The value displayed in the Slider control. The Value property always is between the values of the Minimum and Maximum properties.
The Slider control raises the ValueChanged event whenever its Value property changes. You can handle this event to hook up the slider with whatever aspect of the application the slider controls.
Using Attached Properties WPF introduces a new concept in properties: attached properties. Because WPF controls contain the information required for their own layout and orientation in the user interface, it is sometimes necessary for controls to define information about the control that contains them. For example, a Button control contained by a Grid control will define what grid column and row it appears in. This is done through attached properties. The Grid control attaches a number of properties to every control it contains, such as properties that determine the row and column in which the control exists. In XAML, you set an attached property with code like the following:
Note that you refer to the class name (that is, Grid) when setting an attached property, rather than the instance name (for example, grid1). This is because attached properties are attached by the class and not by the instance of the class. In some cases, such as with the TabIndex property (shown in the next section), the class name is assumed and can be omitted in XAML. Here’s a full example of a Grid control that defines two rows and two columns, and contains a single button that uses attached properties to orient itself in the grid:
Lesson 1: Using Content Controls
111
Setting the Tab Order for Controls A common mode of user interaction with the user interface is to cycle the focus through the controls by pressing the Tab key. By default, controls in the user interface will receive the focus from Tab key presses in the order in which they are defined in the XAML. You can set the tab order manually by setting the attached property TabIndex to an integer, as shown here:
When the user presses the Tab key, the focus cycles through the controls in the order determined by the value of the TabIndex property. Lower values receive focus first, followed by higher values. Controls that do not have the TabIndex property explicitly set receive the focus after controls for which the property has been set, in the order that they are defined in the XAML. If two controls have the same TabIndex value, they receive the focus in the order that the controls are defined in the XAML. You can keep a control from receiving focus when the user presses the Tab key by setting the KeyboardNavigation.IsTabStop attached property to False, as shown in bold here:
Lab: Building a User Interface In this lab, you build a simple user interface that collects a user’s name and e-mail address.
Exercise 1: Building the User Interface 1. In Visual Studio, create a new WPF application. 2. Add three TextBox controls to the window. Name them as follows: txtFirstName, txtLastName, and txtEmail.
112
Chapter 3
Building the User Interface
3. Add three Label controls to the interface and position them to the left of the TextBox controls. Set properties on the labels as follows: Label
Content Property
Target Property
label1
_First Name
{Binding ElementName=txtFirstName}
label2
_Last Name
{Binding ElementName=txtLastName}
label3
_Email
{Binding ElementName=txtEmail}
When finished, the XAML for these labels should be similar to the following: _First Name _Last Name _Email
4. Add a Button control to the interface. Set the Content property of the button to “_Submit”. 5. In the XAML, set the TabIndex property of txtFirstName, txtLastName, txtEmail, and button1 to 1, 2, 3, and 4, respectively. The XAML that you add to the element should resemble the following: TabIndex="1"
6. Double-click button1 to open the default event handler for the button. Add the following code: ' VB MessageBox.Show("Your name is " & txtFirstName.Text & " " & _ txtLastName.Text & " and your email address is " & txtEmail.Text) // C# MessageBox.Show("Your name is " + txtFirstName.Text + " " + txtLastName.Text + " and your email address is " + txtEmail.Text);
7. Press F5 to build and run your application. Navigate your user interface using the Tab key and the labels’ mnemonic keys. When you have filled in the form, use the access key you defined for your button to submit the information.
Lesson 1: Using Content Controls
113
Lesson Summary Q
Controls in WPF are primarily divided into three types: content controls, which can contain a single nested element; item controls, which can contain a list of nested elements; and layout controls, which are designed to host multiple controls and provide layout logic for those controls. Certain specialized controls, such as the TextBox, Image, and ProgressBar controls, are individual controls and can be considered part of the content control category.
Q
Virtually any type of object can be assigned to the Content property of a content control. If the object inherits from UIElement, the control is rendered in the containing control. Other types are rendered as a string—the string returned by their content’s ToString method.
Q
Label controls allow you to create mnemonic keys that shift the focus to a target control when the Alt key and the specified key are pressed. You can specify the target of the mnemonic key by setting the Target control, and you can specify the mnemonic key itself by preceding the mnemonic key in a label’s Content property with the underscore (_) symbol.
Q
A Button control allows you to define an access key that when pressed in combination with the Alt key has the same effect as clicking the button. You can specify the access key by preceding the appropriate character in a button’s Content property with the underscore (_) symbol.
Q
Attached properties are properties that are provided to a control by its container, or by another class. Controls have these properties only when they are in the correct context to express them. Examples of attached properties include the Grid.Row, Grid.Column, and KeyboardNavigation.TabIndex properties.
Q
The KeyboardNavigation.TabIndex attached property allows you to set the tab index for the controls in your window.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 1, “Using Content Controls.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
114
Chapter 3
1.
Building the User Interface
How many child controls can a content control contain? A. 0 B. 1 C. No limit D. Depends on the control
2.
Which of the following is required to set a working mnemonic key for a label? (Choose all that apply.) A. Set the Target property to the target control. B. You must place both the Label control and the Target property in the same container. C. Precede the letter for the mnemonic key with an underscore symbol in the Content property of the Label control. D. Set the MnemonicKey property on the Label control.
3.
Which of the following XAML snippets correctly shows a button in a cell created by the intersection of the second column and the second row of a grid with four cells? A.
B.
Lesson 1: Using Content Controls
C.
D.
115
116
Chapter 3
Building the User Interface
Lesson 2: Item Controls Item controls, also known as list-based controls, are designed to contain multiple child elements. Item controls are a familiar part of any user interface. Data is displayed frequently in item controls, and lists are used to allow the user to choose from a series of options. Item controls in WPF take the idea of lists one step further. Like content controls, item controls do not have restrictions on the kind of content that they can present. Thus, an item control could present a list of strings, or something more complex, such as a list of Checkbox controls, or even a list that included various kinds of controls. In this lesson, you will learn to create and configure item controls in your user interface. After this lesson, you will be able to: Q
Create and use an item control in your user interface
Q
Create a menu
Q
Create a toolbar
Q
Create a status bar
Estimated lesson time: 30 minutes
ListBox Control The simplest form of item control is ListBox. As the name implies, ListBox is a simple control designed to display a list of items. A ListBox control typically displays a list of ListBoxItem controls, which are content controls and each of which hosts a single nested element. The simplest way to populate a ListBox control is by adding items directly in the XAML, as shown here: This Is A List
The ListBox control automatically lays out its content in a stack and adds a vertical scroll bar if the list is longer than the available space in the control. By default, the ListBox control allows you to select a single item. You can retrieve the index of the selected item from the ListBox.SelectedIndex property or you can retrieve the selected item itself through the ListBox.SelectedItem property. The ListBoxItem control also exposes an IsSelected property that is positive when the item is selected.
Lesson 2: Item Controls
117
You can set the SelectionMode property to allow the user to select multiple items. Table 3-4 shows the possible values for the SelectionMode property. Table 3-4
Values for the SelectionMode Property
Value
Description
Single
The user can select only one item at a time.
Multiple
The user can select multiple items without holding down a modifier key. Modifier keys have no effect.
Extended
The user can select multiple consecutive items while holding down the Shift key or nonconsecutive items by holding down the Ctrl key and clicking the items.
You can set the SelectionMode property in XAML as shown here:
When multiple items are selected, you can retrieve the selected items through the ListBox.SelectedItems property. Although the ListBox control is used most commonly with ListBoxItem controls, it can display a list of any item types. For example, you might want to create a list of CheckBox controls. You can accomplish this by simply adding CheckBox controls to the ListBox control, as shown here: Option 1 Option 2 Option 3 Option 4
ComboBox Control The ComboBox control works very similarly to the ListBox control. It can contain a list of items, each of which can be an object of any type, as in the ListBox control. Thus, the ComboBox control can host a list of strings, a list of controls such as CheckBoxes, or any other kind of list. The difference between the ComboBox control and the ListBox control is how the control is presented. The ComboBox control appears as a drop-down list. Like the ListBox control, you can get a reference to the selected item via the SelectedItem property and you can retrieve the index of the selected item via the SelectedIndex property.
118
Chapter 3
Building the User Interface
When an item is selected, the string representation of the content of that item is displayed in the ComboBox control. Thus, if the ComboBox control hosts a list of strings, the selected string will be displayed. If the ComboBox control hosts a list of CheckBox controls, the string representation of the ComboBox.Content property will be displayed. Then the selected value is available via the ComboBox.Text property. Users also can edit the text displayed in the ComboBox control. They can even type in their own text, as in a text box. To make the ComboBox control editable, you must set the IsReadOnly property to False and set the IsEditable property to True. You can open and close the ComboBox control programmatically by setting the IsDropDownOpen property to True (to open it) and False (to close it).
TreeView Control TreeView is a simple item control that is very similar to ListBox in its implementation, but in practice, it is quite different. The primary purpose of the TreeView control is to host TreeViewItem controls, which allow the construction of trees of content.
TreeViewItem Control The TreeViewItem control is the primary control used in the construction of trees. It exposes a Header property that allows you to set the text that is displayed in the tree. The TreeViewItem control also itself hosts a list of items. The list of items hosted in a TreeViewItem can be expanded or collapsed by clicking the icon to the left of the header. The following XAML demonstrates a TreeView control populated by a tree of items:
You can create TreeView controls that have controls as the terminal nodes just as easily, as shown in this example:
Lesson 2: Item Controls
119
You can obtain a reference to the selected item in the TreeView control with the TreeView .SelectedItem property.
Menus Menus allow you to present the user with a list of controls that typically are associated with commands. Menus are displayed in hierarchical lists of items, usually grouped into related areas. WPF provides two types of menu controls—Menu, which is designed to be visible in the user interface, and ContextMenu, which is designed to function as a pop-up menu in certain situations. While the Menu control can be put anywhere in the user interface, it typically is docked to the top of the window. Menus expose an IsMainMenu property. When this property is True, pressing the Alt or F10 key causes the menu to receive focus, thereby enabling common Windows application behavior. Although a Menu control can contain controls of any kind, the Toolbar control is better suited for presenting controls to the user. The Menu control is designed for presenting lists of MenuItems.
MenuItem Control The MenuItem control is the main unit used to build menus. A MenuItem control represents a clickable section of the menu and has associated text. MenuItem controls are themselves item controls and can contain their own list of controls, which typically are also MenuItem controls. The following XAML example demonstrates a simple menu:
120
Chapter 3
Building the User Interface
The Command property indicates the command that is associated with that menu item. When the menu item is clicked the command specified by the Command property is invoked. If there is a shortcut key associated with the command, it is displayed to the right of the MenuItem header. Table 3.5 describes the important properties of the MenuItem control. Table 3-5
Properties of the MenuItem Control
Property
Description
Command
The command that is associated with the menu item. This command is invoked when the menu item is clicked. If a keyboard shortcut is associated with the command, it is displayed to the right of the menu item.
Header
The text that is displayed in the menu.
Icon
The icon that is displayed to the left of the menu item. If IsChecked is set to True, the icon is not displayed even if it is set.
IsChecked
When this property is True, a check will be displayed to the left of the menu item. If the Icon property is set, the icon is not displayed while IsChecked is True.
IsEnabled
Determines whether the menu item is enabled. When False, the item appears dimmed and does not invoke the command when clicked.
Items
The list of items contained by the MenuItem control. The list typically contains more MenuItem controls.
As with many other WPF controls, you can create an access key for a menu item by preceding the letter in the header property with an underline (_) symbol, as shown here:
The underline symbol will not appear at run time, but when the Alt key is held down, it appears under the key it precedes. Pressing that key with the Alt key held down has the same effect as clicking the menu item. Each MenuItem control can contain its own set of items, which are also typically MenuItem controls. These can be created in XAML by nesting MenuItem elements inside the parent MenuItem control. When a menu item that has sub-items is clicked, those items are shown in a new menu. Note that it is best practice not to assign a command to
Lesson 2: Item Controls
121
MenuItems that contain sub-items. Otherwise, the command is executed every time the user wants to view the list of sub-items. You can add a separator bar between menu items by using the Separator control, as shown here:
The separator bar appears as a horizontal line between menu items.
ContextMenu Control Unlike Menu controls, the ContextMenu control does not have a fixed location in the user interface. Rather, it is associated with other controls. To create a ContextMenu control for a control, you define it in the XAML code for the Control.ContextMenu property, as shown in the following example with a ListBox control:
Once a ContextMenu control has been set for a control, it will be displayed whenever the user right-clicks the control or presses Shift+F10 while the control has the focus. Another common scenario for adding ContextMenus to a control is to add them as a resource in the Window.Resources collection. Resources will be discussed in Chapter 9, “Resources, Documents, and Localization.”
ToolBar Control Like menus, the ToolBar control is designed to present controls to the user. The ToolBar control is ideally suited to host controls such as Button, ComboBox, TextBox, CheckBox and RadioButton controls. The ToolBar control also can use the Separator control described in the previous section. Toolbars automatically override the style of some of the controls they host. Buttons, for example, appear flat when shown in a toolbar, and are highlighted in blue when the mouse is over the control. This gives controls in a toolbar a consistent appearance by default.
122
Chapter 3
Building the User Interface
You add items to the ToolBar control in the same manner as any other item control. An example is shown here: Back Forward
Where more controls are added to a ToolBar control than can fit, controls are removed until the controls fit in the space. Controls that are removed from the ToolBar control are placed automatically in the Overflow menu. The Overflow menu appears as a drop-down list at the right-hand size of the toolbar when the toolbar is in the horizontal configuration. You can manage how controls are placed in the Overflow menu by setting the ToolBar.OverflowMode attached property. The possible values for this property are shown in Table 3-6. Table 3-6
Values for the ToolBar.OverflowMode Property
Value
Description
OverflowMode.Always
The control will always appear in the Overflow menu, even if there is space available in the toolbar.
OverflowMode.AsNeeded
The control will be moved to the Overflow menu as needed. This is the default setting for this property.
OverflowMode.Never
Controls with this value will never be placed in the Overflow menu. If there are more controls with the Toolbar.OverflowMode property set to Never than can be displayed in the space allotted to the toolbar, some controls will be cut off and unavailable to the user.
The following example demonstrates how to set the Toolbar.OverflowMode property: Back
ToolBarTray Class WPF provides a special container class for ToolBar controls called ToolBarTray. The ToolBarTray allows the user to resize or move ToolBar controls that are contained in the tray at run time. When ToolBar controls are hosted in a ToolBarTray, the user can
Lesson 2: Item Controls
123
move the ToolBar controls by grabbing the handle on the left side of the toolbar. The following example demonstrates the ToolBarTray control: Back Forward Stop Go
StatusBar Control The StatusBar control is very similar to the ToolBar control. The primary difference is in usage. StatusBar is used most commonly to host controls that convey information, such as Label and ProgressBar controls. Like the toolbar, the status bar overrides the visual style of many of the controls it hosts, but it provides a different look and feel than the toolbar. The following example demonstrates a simple StatusBar control with hosted controls: Application is Loading
Virtualization in Item Controls When dealing with short lists of items, there is seldom a problem with the performance of item controls. The story can be different, however, when dealing with very long lists of items. WPF item controls typically use a StackPanel internally for the layout of their items. While this allows the expected behavior from item controls, WPF creates each object contained in the item control whether or not the object is visible. In cases where a single item control might contain thousands of objects, considerable system resources could be used to create and maintain all these objects in memory. In cases where objects in an item control are data-bound, you can increase performance significantly by replacing the internal StackPanel with a VirtualizingStackPanel. VirtualizingStackPanel is a control similar to StackPanel, but it has the added functionality that when displaying data-bound objects, it creates only the items that are visible in the user interface. That is, the other items hosted by the list control are “virtual” and are created only when displayed in the user interface.
124
Chapter 3
Building the User Interface
The following procedure uses templates to alter the functionality of a standard control. Templates will be discussed in detail in Chapter 8, “Customizing the User Interface.” NOTE
To Enable Virtualization in Item Controls Q
Create a new ItemsPanelTemplate and specify a VirtualizingStackPanel in that template, as shown in the following example:
When a VirtualizingStackPanel control is present, it is virtualizing by default, but some internal processes can turn off virtualization. You can enable virtualization by setting the attached property VirtualizingStackPanel.IsVirtualizing to True, and you can disable it by setting VirtualizingStackPanel.IsVirtualizing to False. Some WPF controls, such as ListBox and ListView, provide for virtualizing by default. To ensure that virtualization is on for these controls, you should set VirtualizingStackPanel.IsVirtualizing to True.
Quick Check Q
Describe the difference between a Menu control and a ContextMenu control.
Quick Check Answer Q
Both Menu elements and ContextMenu elements are list controls that host MenuItem elements. The primary difference between them is that Menu elements are visible elements that are part of the visual tree and can be hosted by content controls. ContextMenu elements, on the other hand, have no direct visual representation and are added to an other individual control by setting the other control's ContextMenu property.
Lab: Practice with Item Controls In this lab, you create a simple application that allows you to change the font of text in a RichTextBox control by using controls in a toolbar.
Lesson 2: Item Controls
125
Exercise 1: Using Item Controls 1. In Visual Studio, create a new WPF application. 2. From the Toolbox, drag a ToolBar control onto the form. Using the mouse, position the toolbar so that it is flush with the upper edge of the window. Stretch the sides of the toolbar so that the edges are flush with the left and right edges of the window. 3. In the XAML editor, remove the abbreviated closing tab ( />) from the ToolBar element and replace it with a full-length closing tab by adding >. When complete, your ToolBar code should resemble the following:
4. From the Toolbox, drag a RichTextBox element onto the Grid. Using the mouse, set the edges of the RichTextBox frame such that the top edge is flush with the bottom edge of the toolbar and the other edges are flush with the edges of the window. Note that the Margin property is set automatically by this action. 5. In the XAML editor, position the cursor between the opening and closing tags for the ToolBar control. Add the following XAML to add four controls to the ToolBar control: Bold Italic
6. From the Build menu, choose Build Solution to build your application. 7. In the Designer, double-click the button labeled Bold to open the default Click event handler for the button. Add the following code: ' VB richTextBox1.Selection.ApplyPropertyValue(FontWeightProperty, _ FontWeights.Bold) // C# richTextBox1.Selection.ApplyPropertyValue(FontWeightProperty, FontWeights.Bold);
8. In the Designer, double-click the button labeled Italic to open the default Click event handler for the button. Add the following code: ' VB richTextBox1.Selection.ApplyPropertyValue(FontStyleProperty, _ FontStyles.Italic) // C# richTextBox1.Selection.ApplyPropertyValue(FontStyleProperty, FontStyles.Italic);
126
Chapter 3
Building the User Interface
9. In the Designer, double-click the slider to open the SliderChanged event handler. Add the following code: ' VB richTextBox1.Selection.ApplyPropertyValue(FontSizeProperty, _ Slider1.Value.ToString()) // C# richTextBox1.Selection.ApplyPropertyValue(FontSizeProperty, Slider1.Value.ToString());
10. In the Designer, double-click the combo box to open the ComboBoxChanged event handler. Add the following code: ' VB richTextBox1.Selection.ApplyPropertyValue(FontFamilyProperty, _ New FontFamily(comboBox1.Text)) // C# richTextBox1.Selection.ApplyPropertyValue(FontFamilyProperty, new FontFamily(comboBox1.Text));
11. In the Code Window, add the following code to the Window1 constructor beneath the InitializeComponent line. (Note that in Visual Basic, you must add the entire constructor.) ' VB Public Sub New() InitializeComponent() For Each F As FontFamily In Fonts.SystemFontFamilies comboBox1.Items.Add(F.ToString()) Next End Sub // C# foreach (FontFamily F in Fonts.SystemFontFamilies) { comboBox1.Items.Add(F.ToString()); }
12. In XAML view, cut the definition for the RichTextBox element and paste it above the definition for the toolbar. This initializes the RichTextBox before the Slider and prevents a NullReferenceException from being thrown by the SliderChanged event handler. 13. Press F5 to build and run your application. In the RichTextBox element, type some text and select it. Test each control; note the effect it has on the appearance of the text.
Lesson 2: Item Controls
127
Lesson Summary Q
Item controls are designed to present multiple child items. Examples of item controls include ListBox, ComboBox, and TreeView, as well as Menu, ToolBar, and StatusBar controls.
Q
ListBox controls display a list of items. Items displayed are typically text, but can be any UI element. You can even display different kinds of UI elements in the same control. You can retrieve selected items with the ListBox.SelectedItem or ListBox.SelectedItems property.
Q
ComboBox allows the user to select from a preset list of items in a drop-down list. You also can allow the user to type an entry in the combo box by setting the ComboBox.IsReadOnly property to False and the ComboBox.IsEditable property to True.
Q
TreeView presents a hierarchical list of items. While items in the TreeView can be of any type, the TreeViewItem control can contain a list of items that appear as sub-items in the TreeView control.
Q
Menu controls are designed to display hierarchical lists of MenuItem controls in the familiar menu format. Each MenuItem control can contain its own list of MenuItem controls and can have a command associated with it that is invoked when the MenuItem is clicked, though typically not both at once.
Q
The ContextMenu control appears near an associated control when the user right-clicks the associated control. You can define a ContextMenu in the XAML for the associated control's Control.ContextMenu property.
Q
ToolBar controls are designed for displaying groups of associated controls, usually with related functionality. Controls displayed in a toolbar by default conform to the look and feel of the toolbar itself. StatusBar controls are similar to ToolBar controls, but typically are used more often for presenting information than for presenting controls that are an active part of the user interface.
Q
You can replace the layout control in an item control with a VirtualizingStackPanel to provide virtualization for very long, data-bound lists.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 2, “Item Controls.” The questions are also available on the companion CD if you prefer to review them in electronic form.
128
Chapter 3
NOTE
Building the User Interface
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which XAML snippet correctly defines a context menu for Button1? A. MenuItem Button
B. MenuItem Button
C. MenuItem Button
D. MenuItem Button
Lesson 2: Item Controls
129
2. What is the maximum number of child elements that an item control can contain? A.
0
B.
1
C.
No limit
D.
Depends on the control
3. Which of the following XAML snippets demonstrates the best way to implement virtualization in a TreeView control? A.
B.
C.
D.
130
Chapter 3
Building the User Interface
Lesson 3: Using Layout Controls WPF offers unprecedented support for a variety of layout styles. The addition of several specialized controls allows you to create a variety of layout models, and panels can be nested inside each other to create user interfaces that exhibit complex layout behavior. In this lesson, you will learn to use these specialized controls. After this lesson, you will be able to: Q
Explain the properties of a control that manage layout
Q
Explain how to use the Grid control
Q
Explain how to use the UniformGrid control
Q
Explain how to use the StackPanel control
Q
Explain how to use the WrapPanel control
Q
Explain how to use the DockPanel control
Q
Explain how to use the Canvas control
Q
Configure control sizing
Q
Align content at design time
Q
Use the GridSplitter control
Estimated lesson time: 30 minutes
Control Layout Properties Controls in WPF manage a great deal of their own layout and positioning and further interact with their container to determine their final positioning. Table 3-7 describes common control properties that influence layout and positioning. Table 3-7
Properties That Control Layout
Property
Description
FlowDirection
Gets or sets the direction in which text and other UI elements flow within any parent element that controls their layout.
Height
Gets or sets the height of the control. When set to Auto, the height is determined by other layout properties.
Lesson 3: Using Layout Controls
Table 3-7
131
Properties That Control Layout
Property
Description
HorizontalAlignment
Gets or sets the horizontal alignment characteristics applied to this element when it is composed within a parent element, such as a panel or item control.
HorizonalContentAlignment
Gets or sets the horizontal alignment of the control's content.
Margin
Gets or sets the distance between each of the control’s edges and the edge of the container or the adjacent controls depending on the layout control hosting the child control.
MaxHeight
Gets or sets the maximum height for a control.
MaxWidth
Gets or sets the maximum width for a control.
MinHeight
Gets or sets the minimum height for a control.
MinWidth
Gets or sets the minimum width for a control.
Padding
Gets or sets the amount of space between a control and its child element.
VerticalAlignment
Gets or sets the vertical alignment characteristics applied to this element when it is composed within a parent element, such as a layout or item control.
VerticalContentAlignment
Gets or sets the vertical alignment of the control's content.
Width
Gets or sets the width of the control. When set to Auto, the width is determined by other layout properties.
A few of these properties are worth a closer look.
Margin Property The Margin property returns an instance of the Thickness structure that describes the space between the edges of the control and other elements that are adjacent. Depending on what layout panel is used, the adjacent element might be the edge of
132
Chapter 3
Building the User Interface
the container, such as a panel or Grid cell, or it might be a peer control, as would be the case in the vertical margins in a StackPanel. The Margin property can be set asymmetrically to allow different amounts of margin on each side. Take the following example: Button
In this example, a different margin distance is set for each control edge. The order of edges in the Margin property is Left, Top, Right, Bottom, so in this example, the left margin is 0, the top margin is 48, the right margin is 96, and the bottom margin is 1. Margins are additive. For example, if you have two adjacent controls in a StackPanel and the topmost one has a bottom margin of 20 and the bottommost one has a top margin of 10, the total distance between the two control edges will be 30.
HorizontalAlignment and VerticalAlignment Properties The HorizontalAlignment and VerticalAlignment properties determine how a control is aligned inside its parent when there is extra horizontal or vertical space. The values for these properties are mostly self-explanatory. The HorizontalAlignment property has possible values of Left, Right, Center, and Stretch. The VerticalAlignment property has possible values of Top, Bottom, Center, and Stretch. As you might expect, setting the HorizontalAlignment property to Left, Right, or Center aligns the control in its container to the left, right, or center, respectively. Similar results are seen with the VerticalAlignment property. The setting that is worth noting is the Stretch value. When set to Stretch, the control will stretch in the horizontal or vertical directions (depending on the property) until the control is the size of the available space, after taking the value of the Margin property into account. Note that in some containers, setting these properties might have no effect. For example, in StackPanel, the vertical layout is handled by the container, so setting the VerticalAlignment property has no effect, though setting the HorizontalAlignment property still does.
Layout Panels WPF includes a variety of layout panels that can be used to design your user interface. In this section you will explore these panels and learn when to use them.
Grid Panel Grid is the most commonly used panel for creating user interfaces in WPF. The Grid panel allows you to create layouts that depend on the Margin, HorizontalAlignment, and VerticalAlignment properties of the child controls it contains. Controls hosted in
Lesson 3: Using Layout Controls
133
a Grid control are drawn in the order in which they appear in markup or code, thereby allowing you to create layered user interfaces. In the case of overlapping controls, the last control to be drawn will be on top. The Grid control allows you to define columns and rows in the grid. Then you can assign child controls to designated rows and columns to create a more structured layout. When assigned to a column or row, a control’s Margin, HorizontalAlignment, and VerticalAlignment properties operate with respect to the edge of the row or column, not the edge of the Grid container itself. Columns and rows are defined by creating ColumnDefinitions and RowDefinitions, as seen here:
Rows and columns can either be fixed or variable in their width and height. To designate a fixed width or height, simply set the Width or Height property to the size you would like, as shown here:
In contrast, you can make a variable-sized row or column by appending an asterisk (*) to the end of the Width or Height setting, as shown here:
This means that the row or column grows or shrinks proportionally to fit the available space. Look at the following example:
Both the rows created by this code grow and shrink to fit the available space, but one row is always twice the height of the other. These numbers are only proportional amongst themselves. Thus, using 1* and 2* will have the same effect as using 100* and 200*. You can have a Grid control that contains both fixed and variable rows or columns, as seen here:
134
Chapter 3
Building the User Interface
In this example, the first row always maintains a height of 125, and the second grows or shrinks as the window is resized. Grid Attached Properties
The Grid control provides attached properties to its child controls. You can position controls into specific Grid rows or Columns by setting the attached properties Grid.Column and Grid.Row, as shown in bold here:
Button
Occasionally, you might have a control that spans more than one column or row. To indicate this, you can set the Grid.ColumnSpan or Grid.RowSpan property as shown here: Button
Using the GridSplitter Control
The GridSplitter control allows the user to resize grid rows or columns at run time. The GridSplitter control appears at run time as a vertical or horizontal bar between two rows or columns that the user can grab with the mouse and move to adjust the size of the columns or rows that it is between. Important properties of the GridSplitter control are shown in Table 3-8.
Table 3-8
Properties of the GridSplitter Control
Property
Description
Grid.Column
This attached property from the Grid control determines the column that the grid splitter exists in.
Grid.ColumnSpan
This attached property from the Grid control determines the number of columns that the grid splitter spans. For horizontal grid splitters, this property should equal the number of columns in the grid.
Grid.Row
This attached property from the Grid control determines the row that the grid splitter exists in.
Lesson 3: Using Layout Controls
Table 3-8
135
Properties of the GridSplitter Control
Property
Description
Grid.RowSpan
This attached property from the Grid control determines the number of rows that the grid splitter spans. For vertical grid splitters, this property should equal the number of rows in the grid.
Height
Determines the height of the grid splitter. For vertical grid splitters, this property should be set to Auto.
HorizontalAlignment
Determines the horizontal alignment of the grid splitter. For horizontal grid splitters, this property should be set to Stretch. For vertical grid splitters, this property should be set to Top or Bottom.
Margin
Determines the margin around the grid splitter. Typically, your margin will be set to 0 to make the grid splitter flush with grid columns and rows.
ResizeBehavior
Gets or sets which columns or rows are resized relative to the column or row for which the GridSplitter control is defined. The default value is BasedOnAlignment, which sets the resize behavior based on the alignment of the GridSplitter control relative to the row(s) or column(s) that the grid splitter is adjacent to.
ResizeDirection
Gets or sets a value that indicates whether the GridSplitter control resizes rows or columns. The default value is Auto, which automatically sets the resize direction based on the positioning of the GridSplitter control.
ShowsPreview
Gets or sets a value that indicates whether the GridSplitter control updates the column or row size as the user drags the control.
136
Chapter 3
Building the User Interface
Table 3-8
Properties of the GridSplitter Control
Property
Description
VerticalAlignment
Determines the vertical alignment of the grid splitter. For vertical grid splitters, this property should be set to Stretch. For horizontal grid splitters, this property should be set to Left or Right.
Width
Determines the width of the grid splitter. For horizontal grid splitters, this property should be set to Auto.
While easy for the user to use, the GridSplitter control is not the most intuitive control for developers to use. While you can drag and drop the grid splitter onto your window from the Toolbox, you must do a fair amount of configuration to make the grid splitter useful. The GridSplitter control must be placed within a grid cell, even though it always resizes entire rows or columns, and it should be positioned either adjacent to the edge of the row or column that you want to resize, or put into a dedicated row or column that is between the rows or columns you want to resize. You can position the grid splitter manually in the designer by grabbing the handle that appears at the upper left-hand corner of the grid splitter. Figure 3-1 shows the grid splitter in the Designer.
Figure 3-1 The grid splitter in the Designer
Lesson 3: Using Layout Controls
137
When the ResizeBehavior property is set to Auto, WPF automatically sets the correct resize behavior based on the alignment of the grid splitter. The typical UI experience for the grid splitter is to have a visual element that spans all of the rows or columns in a grid. Thus, you must set the Grid.ColumnSpan property for horizontal grid splitters or the Grid.RowSpan property for vertical grid splitters manually to span all of the rows or columns that the grid contains. The following procedure describes how to add a grid splitter to your window at design time.
To add a grid splitter to your window
1. From the Toolbox, drag a grid splitter onto your window and drop it in a cell that is adjacent to the row or column for which you want to set resizing. You might want to create a dedicated row or column to hold the grid splitter alone so that there is no interference with other UI elements. 2. Set the Margin property of the grid splitter to 0. 3. For vertical grid splitters, set the VerticalAlignment property to Stretch. For horizontal grid splitters, set the HorizontalAlignment property to Stretch. Set the remaining alignment property to the appropriate setting to position the GridSplitter control adjacent to the column(s) or row(s) for which you want to enable resizing. 4. For horizontal grid splitters, set the Width property to Auto and set the Height property to the appropriate height. For vertical grid splitters, set the Height property to Auto and set the Width property to the appropriate width. 5. For vertical grid splitters, set the Grid.RowSpan property to the number of rows in the grid. For horizontal grid splitters, set the Grid.ColumnSpan property to the number of columns in the grid. Note that you can perform this configuration in the Properties window or in XAML, or (in most but not all cases) by manipulating the GridSplitter control in the Designer with the mouse.
UniformGrid Control Although similar in name, the UniformGrid control has very different behavior from the Grid control. In fact, the UniformGrid control is very limited. It automatically lays out controls in a grid of uniform size, adjusting the size and number of rows and columns as more controls are added. Grid cells are always the same size. The UniformGrid control
138
Chapter 3
Building the User Interface
typically is not used for designing entire user interfaces, but it can be useful for quickly creating layouts that require a grid of uniform size, such as a checkerboard or the buttons on a calculator. You can set the number of rows and columns in the UniformGrid control by setting the Rows and Columns properties, as shown here:
If you set the number of rows and columns in this manner, you fix the number of cells (and thus the controls that can be displayed) in a single UniformGrid. If you add more controls than a UniformGrid has cells, the controls will not be displayed. Cells defined first in XAML are the cells displayed in such a case. If you set only the number of rows, additional columns will be added to accommodate new controls. Likewise, if you set only the number of columns, additional rows will be added.
StackPanel Control The StackPanel control provides a simple layout model. It stacks the controls it contains one on top of the other, in the order that they are defined. Typically, StackPanel containers stack controls vertically. You also can create a horizontal stack by setting the Orientation property to Horizontal, as shown here:
This creates a stack of controls from left to right. If you want to create a right-to-left stack of controls, you can set the FlowDirection property to RightToLeft, as shown here:
There is no combination of property settings in the stack panel that creates a bottomto-top stack. Note that the layout properties of the controls contained in the StackPanel control also influence how the stack appears. For example, controls appear in the center of the StackPanel by default, but if the HorizontalAlignment property of a specific control is set at Left, that control appears on the left side of the StackPanel.
Lesson 3: Using Layout Controls
139
WrapPanel Control The WrapPanel control provides another simple layout experience which typically is not used for creating entire user interfaces. Simply, the WrapPanel control lays out controls in a horizontal row side-by-side until the horizontal space available in the WrapPanel is used up. Then it creates additional rows until all its contained controls are positioned. Thus, as text is wrapped in a text editor like Notepad, controls are wrapped in the user interface. Typical usage for this layout panel is to provide automatic layout for a related set of controls that might be resized frequently, such as in a toolbar. You can wrap controls from right to left by setting the FlowDirection property to RightToLeft, as shown here:
DockPanel Control The DockPanel control provides a container that enables you to dock contained controls to the edges of the DockPanel. In Windows Forms development, docking was accomplished by setting the Dock property on each individual dockable control. In WPF development, however, you use the DockPanel control to create interfaces with docked controls. Docking is typically useful for attaching controls such as toolbars or menus to edges of the user interface. The position of docked controls remains constant regardless of how the user resizes the user interface. The DockPanel control provides docking for contained controls by providing an attached property called Dock. The following example demonstrates how to set the DockPanel.Dock property in a contained control: Button
The DockPanel.Dock property has four possible values: Top, Bottom, Left, and Right, which indicate docking to the top, bottom, left, and right edges of the DockPanel, respectively. The DockPanel control exposes a property called LastChildFill, which can be set to True or False. When True (the default setting), the last control added to the layout will fill all remaining space. The order in which controls are added to the DockPanel container is crucial in determining the layout. When controls are laid out in a DockPanel, the first control to be laid out
140
Chapter 3
Building the User Interface
is allocated all the space on the edge that it is assigned. For example, Figure 3-2 shows a DockPanel with a single Button control docked to the top of the container.
Figure 3-2 A DockPanel with a single docked control
As subsequent controls are added to other edges, they occupy the remaining space on that edge, as demonstrated by Figures 3-3, 3-4, and 3-5.
Figure 3-3 A DockPanel with two docked controls
Lesson 3: Using Layout Controls
141
Figure 3-4 A DockPanel with three docked controls
Figure 3-5 A DockPanel with four docked controls
In this sequence of figures, the second control is docked to the left edge. It occupies all the edge that is not occupied by the first control. The next control is docked again to the top edge, where it is docked adjacent to the first control that already is docked to the top, and it occupies the remaining space on the top edge that was not taken by the button docked on the left edge. The fourth figure shows a similar progression, with another control docked to the left edge. DockPanel controls are typically not used as the sole basis for user interfaces but rather are used to dock key components to invariant positions. Typically, the LastChildFill
142
Chapter 3
Building the User Interface
property in a DockPanel is set to True, and the last child added is a Grid or other container control that can be used for the layout of the rest of the user interface. Figure 3-6 shows a sample user interface that has a menu docked to the top edge, a list box docked to the left edge, and a grid that fills the remaining space.
Figure 3-6 A DockPanel that contains a menu, a list box, and a grid
Canvas Control The Canvas control is a container that allows absolute positioning of contained controls. It has no layout logic of its own, and all contained controls are positioned on the basis of four attached properties: Canvas.Top, Canvas.Bottom, Canvas.Right, and Canvas.Left. The value of each of these properties indicates the distance between the indicated edge of the Canvas control and the corresponding edge of the child control. For example, the following XAML defines a button that is 20 units away from the top edge of the Canvas control and 30 units away from the left edge. Button
You can define only one horizontal and one vertical attached property for each contained control. Thus, you cannot set the value of both Canvas.Left and Canvas.Right for a single control, nor both Canvas.Top and Canvas.Bottom. When the Canvas container is resized, contained controls retain their fixed distance from the Canvas edges, but may move relative to one another if different edges have been fixed for different controls. Because the Canvas control allows for a freeform layout and does not incorporate any complex layout functionality of its own, contained controls can overlap in a Canvas control. By
Lesson 3: Using Layout Controls
143
default, controls that are declared later in the XAML are shown on top of controls declared earlier in the XAML. However, you can set the Z-order (that is, which control appears on top) manually by setting the attached property Canvas.ZIndex. Canvas.ZIndex takes an arbitrary integer value. Controls with a higher Canvas.ZIndex value always appear on top of controls with a lower Canvas.ZIndex value. An example is shown here: This one is on top This one is on the bottom
Quick Check Q
Describe what attached properties are and how they work.
Quick Check Answer Q
Attached properties are properties that a containing element, such as a layout control, attaches to a contained element, such as a content control. Properties are set by the contained element, but typically affect how that element is rendered or laid out in the containing element. An example is the Grid.Row attached property, which is attached to all elements contained by a Grid element. By setting the Grid.Row property on a contained element, you set what row of the grid that element is rendered in.
Accessing Child Elements Programmatically Layout controls expose a Children collection that allows you to programmatically access the child controls. You can obtain a reference to a child element by the index, as shown here: ' VB Dim aButton As Button aButton = CType(grid1.Children(3), Button) // C# Button aButton; aButton = (Button)grid1.Children[3];
You can add a control programmatically by using the Children.Add method, as shown here: ' VB Dim aButton As New Button() grid1.Children.Add(aButton) // C# Button aButton = new Button(); grid1.Children.Add(aButton);
144
Chapter 3
Building the User Interface
Similarly, you can remove a control programmatically with the Children.Remove method: ' VB grid1.Children.Remove(aButton) // C# grid1.Children.Remove(aButton);
And you can remove a control at a specified index by using the RemoveAt method, as shown here: ' VB grid1.Children.RemoveAt(3) // C# grid1.Children.RemoveAt(3);
Aligning Content Frequently, you want to align the content contained in different controls, as well as the edges of the controls themselves. You can align control edges and content at design time by using snaplines. Snaplines are visual aids in the Visual Studio Integrated Development Environment (IDE) that provide feedback to the developer when control edges are aligned or when control content is aligned. When you position controls manually with the mouse in the Designer, snaplines appear when the horizontal or vertical edges of the control are in alignment, as shown in Figures 3-7 and 3-8.
Figure 3-7 Horizontal snaplines
Lesson 3: Using Layout Controls
145
Figure 3-8 Vertical snaplines
Snaplines also indicate when content is aligned, allowing you to align content across multiple controls. An example of content snaplines is shown in Figure 3-9.
Figure 3-9 Content snaplines
146
Chapter 3
Building the User Interface
Lab: Practice with Layout Controls In this lab, you create an application similar to the application you created in the Lesson 2 lab but that takes advantage of the DockPanel and GridSplitter controls.
Exercise 1: Using Layout Controls 1. In Visual Studio, create a new WPF application. 2. In XAML view, change the Grid opening and closing tags to be DockPanel tags, as shown here:
3. From the Toolbox, drag a ToolBar onto the window. Add a full-length closing tag and set the DockPanel.Dock property to Top, as shown here:
Note that even though you have set the DockPanel.Dock property to Top, the toolbar remains in the center of the window. This is because the DockPanel.LastChildFill is set to True by default, and this setting overrides the DockPanel.Dock property. 4. In XAML view, use the following XAML to add a Grid container to the DockPanel:
Note that the toolbar now is at the top of the DockPanel. 5. In XAML view, add the following ColumnDefinition elements to the Grid control:
6. In XAML view, use the following XAML to add a ListBox to the first column:
7. In XAML view, use the following XAML to add a GridSplitter to the second column:
In this Lab, the GridSplitter is given a dedicated column. 8. In XAML view, use the following XAML to add a RichTextBox to the third column:
Lesson 3: Using Layout Controls
147
9. In the XAML for the ToolBar control, use the following XAML to add three controls to the ToolBar: Bold Italic
10. Double-click the button labeled Bold to open the Click event handler. Add the following code: ' VB richTextBox1.Selection.ApplyPropertyValue(FontWeightProperty, _ FontWeights.Bold) // C# richTextBox1.Selection.ApplyPropertyValue(FontWeightProperty, FontWeights.Bold);
11. In the Designer, double-click the button labeled Italic to open the Click event handler. Add the following code: ' VB richTextBox1.Selection.ApplyPropertyValue(FontStyleProperty, _ FontStyles.Italic) // C# richTextBox1.Selection.ApplyPropertyValue(FontStyleProperty, FontStyles.Italic);
12. In the Designer, double-click the slider to open the ValueChanged event handler. Add the following code: ' VB Try richTextBox1.Selection.ApplyPropertyValue(FontSizeProperty, _ Slider1.Value.ToString()) Catch End Try // C# try { richTextBox1.Selection.ApplyPropertyValue(FontSizeProperty, Slider1.Value.ToString()); } catch { }
13. In the Window1 constructor, add the following code after InitializeComponent. Note that in Visual Basic, you will have to add the entire constructor: ' VB Public Sub New() InitializeComponent() For Each F As FontFamily In Fonts.SystemFontFamilies Dim l As ListBoxItem = New ListBoxItem()
148
Chapter 3
Building the User Interface
l.Content = F.ToString() l.FontFamily = F listBox1.Items.Add(l) Next End Sub // C# foreach (FontFamily F in Fonts.SystemFontFamilies) { ListBoxItem l = new ListBoxItem(); l.Content = F.ToString(); l.FontFamily = F; listBox1.Items.Add(l); }
14. In the Designer, double-click the ListBox control to open the SelectionChanged event handler. Add the following code: ' VB richTextBox1.Selection.ApplyPropertyValue(FontFamilyProperty, _ CType(listBox1.SelectedItem, ListBoxItem).FontFamily) // C# richTextBox1.Selection.ApplyPropertyValue(FontFamilyProperty, ((ListBoxItem)listBox1.SelectedItem).FontFamily);
15. Press F5 to build and run your application. Note that you can resize the columns containing the RichTextBox and the ListBox by manipulating the grid splitter with the mouse.
Lesson Summary Q
Layout controls are containers that provide layout logic for contained controls. A layout control is typically the child element in a window.
Q
How controls are arranged in a layout panel depends largely on the layout properties of the contained controls. The HorizontalAlignment and VerticalAlignment properties of child controls determine how a control is aligned in the horizontal and vertical directions, and the Margin property defines an area of space that surrounds the control. The impact of a control’s layout properties can differ depending on the control in which they are hosted.
Q
The Grid control is the most commonly used layout panel for the development of user interfaces. The Grid control allows you to define grid rows and columns and to host multiple elements in each cell. The Grid control provides attached properties to child controls that determine the grid column and row in which they are hosted.
Q
The GridSplitter control allows the user to resize grid columns and rows at run time.
Lesson 3: Using Layout Controls
149
Q
Layout panels such as the UniformGrid, StackPanel, WrapPanel, DockPanel, and Canvas controls are commonly used to create specialized parts of the user interface, and are usually not the highest-level panel in the user interface.
Q
You can use snaplines to align controls and content in the user interface at design time.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 3, “Using Layout Panels.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which layout panel would be the best choice for a user interface that requires evenly spaced controls laid out in a regular pattern? A. Grid B. Canvas C. UniformGrid D. WrapPanel 2. You are working with a Button control contained in a Canvas control. Which XAML snippet will position the Button edges 20 units from the bottom edge of the canvas and 20 units from the right edge of the Canvas, and maintain that positioning when the Canvas is resized? (Choose all that apply.) A.
B.
C.
D.
150
Chapter 3 Review
Chapter Review To practice and reinforce the skills you learned in this chapter further, you can: Q
Review the chapter summary.
Q
Review the list of key terms introduced in this chapter.
Q
Complete the case scenarios. These scenarios set up real-world situations involving the topics of this chapter and ask you to create a solution.
Q
Complete the suggested practices.
Q
Take a practice test.
Chapter Summary Q
There are three primary types of elements in WPF programming. Content controls can contain a single nested element. Item controls can host a group of nested elements. Layout controls are designed to host multiple controls and to provide layout logic for those controls. Attached properties are properties that are provided to a control by its container or by another class. Controls have these properties only when they are in the correct context to express them.
Q
Item controls are designed to present multiple child items. Controls such as ListBox, ComboBox, and TreeView primarily are intended to present lists of data or choices to the user, whereas controls such as the Menu, ToolBar, and StatusBar controls are designed to host controls in a specialized context.
Q
Layout controls are containers that provide layout logic for contained controls. A layout control is typically the child element in a window, and the Grid control is most commonly used for the top-level layout controls. Layout controls such as StackPanel, WrapPanel, UniformGrid, Canvas, and DockPanel provide specialized layout experiences that frequently are used to create dedicated subsections of a user interface. The way that controls are arranged in a layout panel depends largely on the layout properties of the contained controls.
Key Terms Q
Access Key
Q
Content Control
Q
Item Control
Q
Layout Panel
Q
Mnemonic Key
Chapter 3 Review
151
Case Scenarios In the following case scenarios, you apply what you’ve learned about how to use controls to design user interfaces. You can find answers to these questions in the “Answers” section at the end of this book.
Case Scenario 1: Streaming Stock Quotes You’re creating an application for a client that can be used to view streaming stock quotes. The geniuses in the Control Development department already have created a control that connects to the stock quote server and displays real-time streaming stock quotes, as well as a Chart control that displays live information about stocks in chart form. Your job is to create a simple application that hosts these controls, along with a few controls (a text box and a pair of buttons) that the client can use to select the stock or stocks in which he is interested.
Technical Requirements Q
Users always must be able to access the controls that allow them to select the stock quote.
Q
The Chart control requires a fair amount of two-dimensional room and cannot be hosted in a toolbar.
Q
The Stock Quote control behaves like a stock ticker and requires linear space but minimal height.
Answer the following questions for your manager: 1. What kinds of item controls can be used to organize the controls that need to go into this application? 2. What kind of layout controls enable the design of an easy-to-use user interface?
Case Scenario 2: The Stock Watcher Well, the client loves the application you created to watch his stocks, and now he wants you to expand it. He loves the way you laid out the Stock Quote and Chart controls, but he would like to expand the stock-watching program somewhat. He wants to be able to select stock symbols from a list of his favorite stocks and to open a replica of the layout you already created. Because he likes to watch several stocks at once, he wants to be able to have several different charts displayed at the same time. What layout strategies allow a readily accessible list of stock symbols and a layout of the stock chart/stock quote interface that adjusts to multiple copies being added at run time?
152
Chapter 3 Review
Suggested Practices Q
Build a calculator program that uses the UniformGrid control for the number button layout and a toolbar for the function key layout. Host both in a single Grid control. Then modify your solution to create a version that hosts both in a DockPanel control.
Q
Create a simple Web browser in a Navigation application. Add a menu that implements a Favorites list and create a context menu that allows the user to right-click the current page she or he is visiting and add it to the Favorites list.
Take a Practice Test The practice tests on this book’s companion CD offer many options. For example, you can test yourself on just the content covered in this chapter or you can test yourself on all the 70-502 certification exam content. You can set up the test so that it closely simulates the experience of taking a certification exam, or you can set it up in study mode so that you can look at the correct answers and explanations after you answer each question. MORE INFO
Practice tests
For details about all the practice test options available, see the section “How to Use the Practice Tests,” in this book’s Introduction.
Chapter 4
Adding and Managing Content Windows Presentation Foundation (WPF) provides unprecedented support for the construction and display of graphics, images, and media files. In this chapter, you learn to use WPF technology to create and display graphics, to add sound and video content, and to add and retrieve binary resources from your application.
Exam objectives in this chapter: Q
Create and display two-dimensional and three-dimensional graphics.
Q
Add multimedia content.
Q
Manage binary resources.
Q
Manage images.
Lessons in this chapter: Q
Lesson 1: Creating and Displaying Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
Q
Lesson 2: Adding Multimedia Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
Q
Lesson 3: Managing Binary Resources. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Q
Lesson 4: Managing Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
Before You Begin To complete the lessons in this chapter, you must have Q
A computer that meets or exceeds the minimum hardware requirements listed in the “About This Book” section at the beginning of the book
Q
Microsoft Visual Studio 2008 Professional Edition installed on your computer
Q
An understanding of Microsoft Visual Basic or C# syntax and familiarity with the Microsoft .NET Framework
153
154
Chapter 4
Adding and Managing Content
Real World Matthew Stoecker In sharp contrast to .NET 2.0, WPF makes it downright easy to add sound and video to your application. Combine that with the improved support for graphics and I find it easier than ever before to create content-rich applications.
Lesson 1: Creating and Displaying Graphics
155
Lesson 1: Creating and Displaying Graphics WPF provides a previously unseen level of support for creating and displaying graphics. In this lesson, you learn to take advantage of this new support by creating your own graphics effects. You learn about using different brushes to create effects, creating a variety of shapes and transforming those shapes, and determining when the mouse is interacting with those shapes. After this lesson, you will be able to: Q
Create brushes that cause different effects and describe how brushes differ from each other
Q
Apply a brush to various properties that affect the visual appearance of controls
Q
Create different lines and shapes
Q
Apply transformations to different lines and shapes
Q
Determine when the mouse is over a shape
Estimated lesson time: 30 minutes
Brushes Brushes are the primary object that WPF uses to paint the user interface. Every control has properties that accept a Brush object. You can affect the appearance of a control by assigning a different brush to one of these properties. Some of these properties are described in Table 4-1. Note that these properties represent several different controls, and no control includes all these properties. Table 4-1
Element Properties That Accept a Brush Object
Property
Description
Background
The brush assigned to this property paints the background of the control.
BorderBrush
The brush assigned to this property paints the border of the control.
Fill
The brush that paints the interior of a shape.
Foreground
The brush assigned to this property paints the foreground of the control, including the content of the control if the content is a string.
156
Chapter 4
Adding and Managing Content
Table 4-1
Element Properties That Accept a Brush Object
Property
Description
OpacityMask
This property accepts a Brush object, but only the opacity component of this brush is considered. The opacity of the control will be determined by the opacity of the brush assigned to this property. For example, if you assign a transparent brush to this property, the entire control appears transparent. You can use exotic brushes to create exotic transparency effects with this property.
Stroke
The brush that paints the edge of a shape.
Brushes are freezable objects. Thus you can make changes to Brush objects so long as the Freeze method has not been called. After the Brush.Freeze method is called, the brush becomes read-only and no further changes can be made.
SolidColorBrush SolidColorBrush is the simplest of the Brush classes. The SolidColorBrush object, as the name implies, paints a single solid color without any patterns or gradients. Several solid color brushes are available, and they are named the color in the Brushes class. You can access these brushes as shown here: ' VB Dim aBrush As Brush aBrush = Brushes.AliceBlue // C# Brush aBrush; aBrush = Brushes.AliceBlue;
In Extensible Application Markup Language (XAML), you can assign a named brush to a property simply by referring to the name, as shown here:
You also can use hexadecimal notation to set the brush in XAML. When using hexadecimal notation, you specify an eight-digit hexadecimal number that defines the color. The first pair of digits denotes the value (from 00 to FF) of the opacity. The second pair of digits indicates the strength of the Red channel. The third set indicates the strength of the Green channel, and the final pair indicates the strength of the Blue channel. The hexadecimal number is preceded by a #. The following example shows setting the background of a button to a pure red SolidColorBrush object:
Lesson 1: Creating and Displaying Graphics
157
You also can create a new SolidColorBrush object by setting the value of each channel directly, as shown here:
In code, you can use the Color.FromArgb method to define the individual channels, as shown here: ' VB Dim aBrush As SolidColorBrush aBrush = New SolidColorBrush(Color.FromArgb(255, 0, 255, 0)) // C# SolidColorBrush aBrush; aBrush = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0));
LinearGradientBrush LinearGradientBrush allows you to create a brush that blends two or more colors along a gradient, allowing a variety of effects. Figure 4-1 shows the effect of a LinearGradientBrush object painting the background of a window.
Figure 4-1 A window background painted by a LinearGradientBrush
158
Chapter 4
Adding and Managing Content
The code that creates this effect is shown here:
The LinearGradientBrush object uses a system of coordinates to determine how the gradient is composed. The coordinate system is based on a rectangle that encloses the area to be painted. The upper left corner of this rectangle is coordinate (0,0), and the lower right corner is coordinate (1,1). Thus, coordinates are relative to the size of the drawn area and are not related to actual pixels. Each LinearGradientBrush contains a collection of GradientStop objects, each of which exposes two important properties: Color and Offset. The Color property determines the color to be blended, and the Offset is a number that specifies the point in the coordinate system where the indicated color is pure and not blended with other colors. In Figure 4-1, the upper left corner is completely black and the lower right corner is completely white, and the two colors are blended along the gradient. The gradient along which colors are blended occurs in a line traveling from the start point to the end point. By default, the start point is (0,0), and the end point is (1,1), which creates a diagonal gradient that blends from the upper left corner to the lower right. You can specify other starting and ending points for the LinearGradientBrush by using the LinearGradientBrush.StartPoint and LinearGradientBrush.EndPoint properties. For example, the following code creates a LinearGradientPoint object that paints a horizontal gradient instead of a diagonal one (shown in Figure 4-2):
You can have more than two GradientStop objects in a LinearGradientBrush. The following example demonstrates a LinearGradientBrush that blends several colors along the gradient. The result of this code is shown in Figure 4-3.
Lesson 1: Creating and Displaying Graphics
Figure 4-2 A horizontal LinearGradientBrush
Figure 4-3 A multicolored LinearGradientBrush
159
160
Chapter 4
Adding and Managing Content
The line defining the gradient need not start on a corner of the coordinate system. You could, for example, create a LinearGradientBrush with a StartPoint of (.3,.3) and an EndPoint of (.7,.7), or any other pair of coordinates with values between 0 and 1. When the line defining the gradient does not stretch from one end of the coordinate system to the other, the value of the LinearGradientBrush.Spread property determines how the rest of the area is painted. Table 4-2 describes the possible values and effects of this property. Table 4-2
Values of the LinearGradientBrush.Spread Property
Value
Description
Pad
This is the default value for the Spread property. It uses solid color on either side of the starting point of the gradient.
Reflect
When SpreadMethod is set to Reflect, the gradient is extended in the opposite orientation, like a mirror image.
Repeat
When SpreadMethod is set to Repeat, the gradient is repeated in the same orientation.
RadialGradientBrush The RadialGradientBrush object is very similar to the LinearGradientBrush object—it blends a series of colors along a gradient. The RadialGradientBrush contains a collection of GradientStop objects that determine how the gradient is constructed. The key difference is that the gradient radiates out in concentric circles from a point in the coordinate system. The center of the gradient is defined by the RadialGradientBrush.GradientOrigin Property (.5, .5 by default). The outermost circles of the RadialGradientBrush are defined by the RadiusX and RadiusY properties. RadiusX determines the distance of the outermost circle in the vertical dimension, and RadiusY determines it in the horizontal dimension. The GradientStop objects function as they do in the LinearGradientBrush, indicating points where the colors are blended. Here’s an example of code that defines a RadialGradientBrush:
Lesson 1: Creating and Displaying Graphics
161
The result of that code is shown in Figure 4-4.
Figure 4-4 The RadialGradientBrush
Quick Check Q
How is the gradient of a RadialGradientBrush or a LinearGradientBrush determined?
Quick Check Answer Q
The gradient in these brushes is formed by a collection of GradientStop objects. Each GradientStop object defines a Color property and an Offset property that indicate what color is to be blended and at what point in the gradient the color reaches maximum saturation.
ImageBrush The ImageBrush object allows you to paint objects using an image as the source for the brush. You can specify the source image for an ImageBrush by setting the ImageSource property, as shown here:
162
Chapter 4
Adding and Managing Content
The ImageBrush uses the designated image to paint the visual objects that are associated with it. If the designated image is smaller than the visual object that the brush is assigned to, how the brush paints the object is controlled by the value of the Stretch property. The possible values of the Stretch property are shown in Table 4-3. Table 4-3
Values of the Stretch Property
Value
Description
Fill
The image is stretched in both dimensions to fill available space. The aspect ratio of the image is not preserved.
None
The image is painted at original size. Any area outside the original image size is not painted.
Uniform
The image is stretched to fill the dimensions of the available space, but the aspect ratio of the image is preserved. Any area outside the stretched size is not painted.
UniformToFill
The image is expanded to fill all the available painting space. The aspect ratio is preserved, but content can be clipped if the aspect ratio of the image is different from the aspect ratio of the container.
You can select a portion of the source image to be used for painting by setting the ViewBox property. The ViewBox property defines a rectangle (usually relative to the imaginary box that surrounds the image) that is clipped to use for painting. The following example demonstrates cropping the upper left quarter of an image and using it for the ImageBrush:
If the Stretch property is set to None, you can also change the behavior of the ImageBrush by setting the TileMode property. Possible values for the TileMode property are shown in Table 4-4. You can set the Viewport property to select a portion of the original source image to use as the tile source. An example is shown here:
Lesson 1: Creating and Displaying Graphics
Table 4-4
163
Values of the TileMode Property
Value
Description
FlipX
The image is tiled in the original orientation in the vertical dimension and alternates original orientation and mirror-image orientation in the horizontal dimension.
FlipXY
The image is tiled in alternating original and mirror-image orientations in both the horizontal and vertical dimensions. Note that tiles to the lower right of an original orientation tile appear as though they have been rotated 180 degrees relative to the original orientation.
FlipY
The image is tiled in the original orientation in the horizontal dimension and alternates original orientation and mirror-image orientation in the vertical dimension.
None
No tiling takes place.
Tile
The image is tiled in the original orientation.
VisualBrush The VisualBrush object is very similar to the ImageBrush object, but instead of using an image as the source for painting, it uses a visual element, such as a WPF control or any other element that inherits the Visual class. The following demonstrates a VisualBrush object that uses a Button control as the source for the visual:
You can use a VisualBrush just as you would use an ImageBrush, applying Stretch or TileMode properties.
Shapes Shapes are drawing primitives. They represent individual drawn elements such as rectangles, ellipses, polygons, and lines. While shapes appear as simple drawn elements, they support all the user interaction events that other WPF elements support and thus are used easily in the creation of new controls. All shapes in WPF derive from the abstract Shape class. This class provides several properties to all the WPF shapes, some of which are described in Table 4-5.
164
Chapter 4
Adding and Managing Content
Table 4-5
Properties of the Shape Class
Property
Description
Fill
The Brush object that paints the interior of the shape
Stroke
The Brush object that paints the edge of the shape
StrokeThickness
Sets the thickness of the border in device-independent units
Stretch
Determines how a shape fills the space it occupies
Like other controls, the Shape class provides Width, Height, Margin, and other properties that control the sizing and spacing of the visual element. If the Width and Height values are not explicitly set, the Stretch property describes how a shape will be stretched to fill the available space. Table 4-6 describes the possible values for the Stretch property. Table 4-6
Values for the Stretch Property
Value
Description
None
The shape is rendered at full size.
Uniform
The shape is stretched to fill the dimensions of the available space, but the aspect ratio of the shape is preserved.
Fill
The shape is stretched in both dimensions to fill the available space. The aspect ratio of the shape is not preserved.
UniformToFill
The shape is expanded to fill all the available painting space. The aspect ratio is preserved. The shape may be clipped to fit the space.
Rectangle and Ellipse Classes The Rectangle class represents a rendered rectangle and the Ellipse class represents a rendered ellipse. Both shapes are very simple. The visual depiction is defined primarily by the Height and Width properties. The following examples represent a rectangle and an ellipse that are each 100 units high and 200 units wide:
If the Height and Width properties are not set explicitly, you can use the Stretch property to describe how the shape in question fills the available space. You can create a rectangle with rounded edges by setting the RadiusX and RadiusY properties. These properties represent the x and y radii of an ellipse, which are used
Lesson 1: Creating and Displaying Graphics
165
to round the corners of the rectangle. The following XAML demonstrates drawing a rectangle with rounded edges:
Line and Polyline Classes The Line class represents a line that connects two coordinate points. The coordinate system used for drawing Line objects is relative to the upper left corner of whatever contains the line. For example, if the line is contained in a grid cell, then the point (0,0) represents the upper left corner of the grid cell. You create a Line object by setting the X1, Y1, X2, and Y2 properties, as shown here:
The Polyline class is a somewhat more complex shape. It is essentially a set of collected points. The Polyline class exposes a Points collection that defines the points in the shape. The shape begins at the first point in the collection and then connects straight lines between the rest of the points in the collection before finally finishing with the last point. The following example demonstrates a Polyline with a sawtooth shape:
The commas between the x and y coordinates of each point are not required, but are allowed and facilitate readability of your code.
Polygon Class The Polygon class is very similar to the Polyline class. Like the Polyline, it exposes a Points collection that defines the points of the shape and connects those points. The chief difference, however, is that the Polygon also connects the first point with the last point and paints the interior of the shape with the Brush defined in the Fill property. The following example demonstrates a Polygon that is very similar to the previous Polyline example:
Filling Polygon and Polyline Shapes
When lines in a Polyline or Polygon cross one another, they create closed areas that can be filled by the shape’s Fill brush. How enclosed areas are filled is determined by the value of the FillRule property. Possible values for the FillRule property are shown in Table 4-7.
166
Chapter 4
Adding and Managing Content
Table 4-7
Values for the FillRule Property
Value
Description
EvenOdd
WPF counts the number of lines that it must cross to get to the bounded region. If a closed region can be reached by crossing an odd number of lines, it is filled. If it can be reached by crossing an even number of lines, it is not filled.
NonZero
WPF counts the number of lines that must be crossed to reach a bounded area and takes the direction in which they are drawn into account. If the number of lines that must be crossed that travel in one direction equals the number of lines that must be crossed that travel in the opposite direction, the area is filled. Otherwise, the area is not filled.
Path Path is the most complex shape in WPF. A Path object describes a complex shape that can be built from one or more Geometry objects. A Geometry object can be thought of as a blueprint for a Shape. It contains all the data regarding the physical appearance of a shape, such as coordinates and size, but it incorporates none of the event-handling or control-based functionality of a Shape. Table 4-8 describes the types of Geometry objects that can be used to create a Path. Table 4-8
Geometry Objects That Can Create a Path
Value
Description
CombinedGeometry
Combines two Geometry objects into a single object and allows you to apply different effects by setting the CombineMode property.
EllipseGeometry
Contains data that represents the shape of an Ellipse.
GeometryGroup
Represents a group of Geometry objects that is added to the Path.
LineGeometry
Represents a straight line.
PathGeometry
Represents a complex shape or figure that is composed of lines, arcs, and curves.
Lesson 1: Creating and Displaying Graphics
Table 4-8
167
Geometry Objects That Can Create a Path
Value
Description
RectangleGeometry
Represents a rectangle.
StreamGeometry
A lightweight equivalent to PathGeometry. StreamGeometry differs from PathGeometry in that it is read-only after being created.
The simplest way to create a Path is to add a single Geometry object to the Path.Data property, as shown here:
You can create paths that consist of multiple shapes by using the GeometryGroup class. The following example adds a rectangle and an ellipse:
When a GeometryGroup contains overlapping Geometry objects, it determines how the overlapping areas are filled by the value of the FillRule property, as with Polygon and Polyline shapes (see Table 4-7, cited previously). The CombinedGeometry class allows you to create Geometry objects that represent combinations of two Geometry objects. The type of combination is determined by the GeometryCombineMode property. Values for the GeometryCombineMode property are shown in Table 4-9. Table 4-9
Values of the GeometryCombineMode Property
Value
Description
Exclude
The resulting Geometry object represents the area that results when the second Geometry object is subtracted from the first Geometry object.
Intersect
The resulting Geometry object represents the intersection of the two input Geometry objects.
168
Chapter 4
Adding and Managing Content
Table 4-9
Values of the GeometryCombineMode Property
Value
Description
Xor
The resulting Geometry object represents the area that is not shared between the two input Geometry objects. This is the reverse of the Intersect value.
Union
The resulting Geometry object represents the union of the two input Geometry objects.
The following example demonstrates a Path created using a CombinedGeometry object:
Extremely complex shapes are possible using the PathGeometry class. A full exploration of the capabilities of the PathGeometry class is beyond the scope of this lesson, but further reading is recommended for those interested in creating complicated graphical shapes.
Transformations Transformations (also referred to as transforms) are objects that allow you to alter the shape of an element by altering the coordinate system that is used to draw it. You can use transforms to apply a variety of effects to shapes and elements, such as rotation, displacement, skewing, and more complicated effects. Types of Transforms Table 4-10 shows the kinds of Transform objects that can be used to alter the appearance of a shape or element. Transforming Shapes
You can apply a Transform object to a shape by setting the RenderTransform property, as shown in this example that transforms a rectangle into a parallelogram:
Lesson 1: Creating and Displaying Graphics
169
Table 4-10 Kinds of Transform Objects
Class
Description
MatrixTransform
Modifies the coordinate system of the object it is applied to by applying a 3 x 3 affine transformation matrix to the underlying coordinate system. The matrix is defined by the Matrix property.
RotateTransform
Rotates the coordinate system around the point represented by the CenterX and CenterY properties. The Angle property determines the angle of rotation.
ScaleTransform
Transforms the coordinate system by increasing or reducing the scale of the transformation. The ScaleX property determines horizontal scaling and the ScaleY property determines vertical scaling. The CenterX and CenterY properties determine the center point of the transformation.
SkewTransform
Skews the coordinate system of your object. The AngleX and AngleY properties determine the angle of skewing in the horizontal and vertical directions, respectively, and the CenterX and CenterY properties determine the center of the transformation.
TranslateTransform
Shifts the coordinate system of your object by the horizontal and vertical amounts indicated by the X and Y properties, respectively.
TransformGroup
Groups a series of Transform objects and applies each to the underlying coordinate system.
Typically, a transformation initiates at the upper left corner of the transformed element. You can specify a different origin for the transformation by setting the RenderTransformOrigin property. The coordinate system used by transformations sets (0,0) as the upper left corner of the rectangle bounding the element and (1,1) as the lower right corner. The code shown in bold in the following example sets the origin of the Transform to the center of the element:
170
Chapter 4
Adding and Managing Content
Transforming Elements
Just as shapes can be transformed, you can apply Transform objects to elements to alter their appearance. Because all WPF elements are drawn by WPF, you can apply a Transform to any WPF element. Note that a Transform affects only the appearance of a control. A control’s functionality remains unaffected, although applying dramatic Transforms to user interface (UI) elements might affect how well a user can comprehend and use the user interface. You can apply a Transform to an element in the same way as you would to a shape—by setting the RenderTransform property. The following example demonstrates a Button object with SkewTransform applied:
Flipping A common requirement for visual transformations is to flip an element, either horizontally across the y-axis or vertically across the x-axis. You can flip an element by using ScaleTransform. To flip an element horizontally but maintain its size and shape, you use ScaleTransform with a ScaleX property of –1. To flip vertically, you use ScaleTransform with a ScaleY property of –1. Setting both ScaleX and ScaleY to –1 results in an element that is flipped across both axes. The following example demonstrates flipping a button horizontally: Flipped Button
Note that using this Transform displaces your element across the axis as though it were a mirror image. To flip an element but maintain its original position, set the element’s RenderTransformOrigin property to .5, .5, as shown here in bold: Flipped Button
Exam Tip Be sure that you understand the difference between each of the kinds of geometric transforms and know when each one is used. In particular, note that you use ScaleTransform to flip an element, rather than RotateTransform, which might not seem intuitive at f irst.
Lesson 1: Creating and Displaying Graphics
171
Clipping In the section “Path,” earlier in this chapter, you saw how Geometry objects can be used to create complex shapes. You also can use Geometry objects to clip the shape of elements by setting the Clip property, which is exposed by all WPF elements. Setting the Clip property constrains the visual appearance of the affected control to the shape described by the Geometry object to which the Clip property is set. This does not transform the element in any way; it merely clips the visible bounds of the control to conform to the Geometry object. Figure 4-5 shows two identically sized Button elements. The Button on the left has not had the Clip property set, whereas the Button on the right has had the Clip property set to an EllipseGeometry object.
Figure 4-5 Two Button elements, one clipped, one not
The following example demonstrates how to apply clipping to an element: Button
Hit Testing Hit testing allows you to determine if a selected point or shape falls within the bounds of a visual element. You use the VisualTreeHelper.HitTest method to perform a hit test.
172
Chapter 4
Adding and Managing Content
VisualTreeHelper.HitTest returns a HitTestResult, from which you can retrieve the Dependency object that has been hit. Shapes, elements, and everything else that is displayed in a user interface inherit the DependencyObject class, so this method allows you to retrieve the hit object regardless of class. The simplest form of hit testing allows you to specify a visual element and a point that is relative to the location of that element. The method searches the visual tree of that element and returns a HitTestResult that contains the topmost dependency object hit by that point. This DependencyObject can be retrieved from the HitTestResult.VisualHit property. If no visual element is hit, this property returns null. Note that this method tests only the visual tree of the visual element supplied as a parameter. If the point specified falls outside the element’s visual tree, null is returned even if there is a visual element located at that point. The following example demonstrates a simple hit test. It tests the current window with a point relative to the upper left corner of that window and returns the topmost object hit at that point: ' VB Dim myHit As HitTestResult = VisualTreeHelper.HitTest(Me, _ New Point(0,70)) // C# HitTestResult myHit = VisualTreeHelper.HitTest(this, new Point(20,20));
Lab: Practice with Graphics In this lab, you take an application you completed in Chapter 1, “WPF Application Fundamentals,” and add a variety of effects. While the result might not be useful in a business setting, it demonstrates some of the techniques you have learned in this lesson. Please note that this exercise requires appropriate file system access.
Exercise: Practice with Graphics 1. From the CD, load the partial solution for Chapter 4, Lesson 1. 2. In XAML view, modify the XAML for Button1 so that it defines a new SolidColorBrush for the Background property, as shown in bold here: Set Name
Lesson 1: Creating and Displaying Graphics
173
3. Do the same with Button2, setting the background to a SolidColorBrush of a different color. 4. Modify the XAML for the TextBox to add a ScaleTransform that flips the TextBox across the y-axis but maintains the same position, as shown in bold here:
5. Add a SkewTransform to Label1 to skew the rendering of the control, as shown in bold here:
6. Just before the declaration, add the following code to set the window background to a LinearGradientBrush:
7. Press F5 to build and run your application. Note that while the functionality remains the same, the user interface has a completely different appearance.
Lesson Summary Q
Brush is the primary object that WPF uses to paint the user interface, and all Brush objects inherit the Brush abstract class. SolidColorBrush objects paint with a solid color, and LinearGradientBrush and RadialGradientBrush objects paint color gradients. ImageBrush and VisualBrush objects can be used to paint with images or other visual elements.
Q
WPF has built-in support for the creation of drawing primitive shapes. Shapes support all the user interaction events common to UI elements. Shapes range from the simple, such as Rectangle and Ellipse, to the complex, such as Polygon and Path.
174
Chapter 4
Adding and Managing Content
Q
Transforms enable you to apply mathematical transformations to shapes and elements. You can apply a Transform to a shape or element by setting the RenderTransform property of the object you want to transform.
Q
You can set the Clip property of an element to a specified Geometry object to clip the visual representation of that object. An element with the Clip property set will be painted only in the area represented by the intersection of the element’s natural shape and the specified Geometry object.
Q
You can use the VisualTreeHelper.HitTest method to retrieve the object hit by a specified point. The point specified is relative to the upper left corner of the specified visual element.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 1, “Creating and Displaying Graphics.” The questions are also available on the companion CD of this book if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following XAML snippets renders the Button control flipped across the y-axis but renders it in the original coordinates? A. Button
B. Button
C.
Lesson 1: Creating and Displaying Graphics
175
Button
D. Button
Navigating a Collection or List When individual properties, such as the Content property of a Label, are bound to a collection, they are capable of displaying only a single member of that collection at a time. This is a common pattern seen in data access applications: a window might be designed to display one record at a time and have individual controls display each of the individual data columns. Because only one record is displayed at a time, it becomes necessary to have a mechanism to allow the user to navigate through the records.
224
Chapter 5
Configuring Databinding
WPF has a built-in navigation mechanism for data and collections. When a collection is bound to by a WPF Binding, an ICollectionView is created behind the scenes. The ICollectionView interface contains members that manage data currency, as well as managing views, grouping, and sorting, which will be discussed in Lesson 3 of this chapter. Members involved in navigation are described in Table 5-6. Table 5-6
ICollectionView Members Involved in Navigation
Member
Description
CurrentItem
This property returns the current item.
CurrentPosition
This property returns the numeric position of the current item.
IsCurrentAfterLast
This property indicates whether the current item is after the last item in the collection.
IsCurrentBeforeFirst
This property indicates whether the current item is before the first item in the collection.
MoveCurrentTo
This method sets the current item to the indicated item.
MoveCurrentToFirst
This method sets the current item to the first item in the collection.
MoveCurrentToLast
This method sets the current item to the last item in the collection.
MoveCurrentToNext
This method sets the current item to the next item in the collection.
MoveCurrentToPosition
This method sets the current item to the item at the designated position.
MoveCurrentToPrevious
This method sets the current item to the previous item.
You can get a reference to the ICollectionView by calling the CollectionViewSource.GetDefaultView method, as shown here: ' VB ' This example assumes a collection named myCollection
Lesson 2: Binding to Data Sources
225
Dim myView As System.ComponentModel.ICollectionView myView = CollectionViewSource.GetDefaultView (myCollection) // C# // This example assumes a collection named myCollection System.ComponentModel.ICollectionView myView; myView = CollectionViewSource.GetDefaultView (myCollection);
When calling this method, you must specify the collection or list for which to retrieve the view (which is myCollection in the previous example). The CollectionViewSource.GetDefaultView returns an ICollectionView object that is actually one of three different classes depending on the class of the source collection. If the source collection implements IBindingList, the view returned is a BindingListCollectionView object. If the source collection implements IList but not IBindingList, the view returned is a ListCollectionView object. If the source collection implements IEnumerable but not IList or IBindingList, the view returned is a CollectionView object. For navigational purposes, working with the ICollectionView should be sufficient. If you need to access members of the other classes, you must cast the ICollectionView object to the correct class. When an item control or content control is bound to a data source, the default collection view handles currency and navigation by default. The current item in the view is returned by the CurrentItem property. This is the item that is currently displayed in all content controls bound to this view. Navigation backward and forward through the list is accomplished by using the MoveCurrentToNext and MoveCurrentToPrevious methods, as shown here: ' VB Dim myView As System.ComponentModel.ICollectionView myView = CollectionViewSource.GetDefaultView (myCollection) ' Sets the CurrentItem to the next item in the list myView.MoveCurrentToNext() ' Sets the CurrentItem to the previous item in the list myView.MoveCurrentToPrevious() // C# System.ComponentModel.ICollectionView myView; myView = CollectionViewSource.GetDefaultView (myCollection); // Sets the CurrentItem to the next item in the list myView.MoveCurrentToNext(); // Sets the CurrentItem to the previous item in the list myView.MoveCurrentToPrevious();
Because item controls typically are bound to all the items in a list, navigation through the list is not usually essential. However, you can use an item control to allow the user
226
Chapter 5
Configuring Databinding
to navigate records by setting the IsSynchronizedWithCurrentItem to True, as shown in bold here:
When the IsSynchronizedWithCurrentItem property is set to True, the SelectedItem of the item control always is synchronized with the CurrentItem of the view. Thus, if the user selects an item in an item control, the CurrentItem property of the view is set to that item. This change then is reflected in all other controls bound to the same data source. This concept is illustrated in the lab for this lesson.
Binding to ADO.NET Objects Binding to ADO.NET objects is basically the same as binding to any other collection or list. Because ADO.NET objects usually are initialized in code, the general pattern is to initialize the ADO.NET objects in code and set the data context for the user interface. Bindings in the XAML should point to the appropriate ADO.NET object.
Setting the DataContext to an ADO.NET DataTable When setting the DataContext to an ADO.NET DataTable that contains the data to which you want to bind, you should set the ItemsSource property of any item controls you are binding to that table to an empty Binding and specify the column to be displayed in the DisplayMemberPath property. For content controls or other single properties, you should set the appropriate property to a Binding that specifies the appropriate column in the Path property. The following example demonstrates the code to initialize an ADO.NET dataset and set the DataContext, and the XAML to bind a ListBox and a Label to that dataset: ' VB Public Class Window1 Dim aset As NwindDataSet = New NwindDataSet() Dim custAdap As _ NwindDataSetTableAdapters.CustomersTableAdapter = _ New NwindDataSetTableAdapters.CustomersTableAdapter() Dim ordAdap As NwindDataSetTableAdapters.OrdersTableAdapter _ = New NwindDataSetTableAdapters.OrdersTableAdapter() Public Sub New() InitializeComponent() custAdap.Fill(aset.Customers) OrdAdap.Fill(aset.Orders) Grid1.DataContext = aset.Customers End Sub
Lesson 2: Binding to Data Sources
227
// C# public partial class Window1 : Window { NwindDataSet aset = new NwindDataSet(); NwindDataSetTableAdapters.CustomersTableAdapter custAdap = new NwindDataSetTableAdapters.CustomersTableAdapter(); NwindDataSetTableAdapters.OrdersTableAdapter ordAdap = new NwindDataSetTableAdapters.OrdersTableAdapter(); public Window1() { InitializeComponent(); custAdap.Fill(aset.Customers); ordAdap.Fill(aset.Orders); Grid1.DataContext = aset.Customers; } }
Setting the DataContext to an ADO.NET DataSet You can also set the DataContext to an ADO.NET DataSet instead of a DataTable. Because a DataSet object is a collection of DataTable objects, you must provide the name of the DataTable as part of the Path property. Examples are shown here: ' VB ' The rest of the code is identical to the previous example ' and has been omitted. Grid1.DataContext = aset // C# // The rest of the code is identical to the previous example // and has been omitted. Grid1.DataContext = aset;
228
Chapter 5
Configuring Databinding
Binding to Hierarchical Data When binding to lists of complex objects, you might want to create a master-detail view that allows the user to select one item from an upper-level list and view the details about the selected object. For lists of complex objects, this is as simple as setting the Path property of each detail binding to the correct property to display and making sure that the upper-level item controls in the user interface have the IsSynchronizedWithCurrentItem property set to True so that the selected item is set to the current item and the detail lists are updated automatically. The following example demonstrates a simple master-detail view. Each Division object contains a list of Group objects, each Group object contains a list of Employee objects, and all objects in this example have a Name property: Divisions
Binding to Related ADO.NET Tables When hierarchical data is retrieved from databases, it typically is presented in related tables. In ADO.NET, these relationships between tables are established by DataRelation objects, which link a column in one table to a column in another table. In WPF, you can retrieve related records by binding to the data relation. The pattern for creating a binding to a relation is as follows, where ParentTable represents a table, Relation represents a child relation of that table, and relation2 represents a child relation of the data returned by Relation: {Binding Path=ParentTable/Relation/Relation2}
Lesson 2: Binding to Data Sources
229
You can bind through child relations only—you cannot bind up through parent relations. The following example demonstrates initializing a data set with two related tables: Customers and Orders. The Orders table has a foreign key called CustomerID that relates to the primary key of the Customers table, also called CustomerID. The name of the relation in the database is CustomersOrders. The code shows the initializing of this data set and the setting of DataContext to the parent table. The XAML shows how to bind the related table to the data relation to view related records automatically: ' VB Public Class Window1 Dim aset As NwindDataSet = New NwindDataSet() Dim custAdap As _ NwindDataSetTableAdapters.CustomersTableAdapter = _ New NwindDataSetTableAdapters.CustomersTableAdapter() Dim ordAdap As NwindDataSetTableAdapters.OrdersTableAdapter = _ New NwindDataSetTableAdapters.OrdersTableAdapter() Public Sub New() InitializeComponent() custAdap.Fill(aset.Customers) OrdAdap.Fill(aset.Orders) Grid1.DataContext = aset.Customers End Sub // C# public partial class Window1 : Window { NwindDataSet aset = new NwindDataSet(); NwindDataSetTableAdapters.CustomersTableAdapter custAdap = new NwindDataSetTableAdapters.CustomersTableAdapter(); NwindDataSetTableAdapters.OrdersTableAdapter ordAdap = new NwindDataSetTableAdapters.OrdersTableAdapter(); public Window1() { InitializeComponent(); custAdap.Fill(aset.Customers); ordAdap.Fill(aset.Orders); Grid1.DataContext = aset.Customers; } }
230
Chapter 5
Configuring Databinding
Binding to an Object with ObjectDataProvider The ObjectDataProvider class allows you to bind a WPF element or property to a method called on an object. You can specify an object type and a method on that type, then bind to the results of that method call. Important properties of ObjectDataProvider are listed in Table 5-7. Table 5-7
Important Properties of ObjectDataProvider
Property
Description
ConstructorParameters
Represents the list of parameters to pass to the constructor.
IsAsynchronous
Indicates whether object creation and method calls are performed on the foreground thread or on a background thread.
MethodName
Represents the name of the method of the source object to call.
MethodParameters
Represents the list of parameters to pass to the method specified by the MethodName property.
ObjectInstance
Gets or sets the object used as the binding source.
ObjectType
Gets or sets the type of object of which to create an instance.
The following example demonstrates how to create a simple ObjectDataProvider for an object with a method that takes no parameters:
And you can bind to this ObjectDataProvider using a Binding, as shown here:
If the method requires parameters, you can provide them in the MethodParameters property. Likewise, if the constructor requires parameters, they are provided in the ConstructorParameters property. Examples are shown here: 12 Items
While you can bind to data presented by the ObjectDataProvider, you cannot update data—that is, the binding is always going to be read-only.
Binding to XML Using the XmlDataProvider The XmlDataProvider allows you to bind WPF elements to data in the XML format. Important properties of the XmlDataProvider class are shown in Table 5-8. Table 5-8
Important Properties of XmlDataProvider
Property
Description
Document
Gets or sets the XmlDocument to be used as the binding source.
Source
Gets or sets the Uniform Resource Indicator (URI) of the XML file to be used as the binding source.
XPath
Gets or sets the XPath query used to generate the XML data.
The following example demonstrates an XmlDataProvider providing data from a source file called Items.xml:
You can also provide the XML data inline as an XML data island. In this case you wrap the XML data in XData tags, as shown here:
232
Chapter 5
Configuring Databinding
You can bind elements to the data provided by an XmlDataProvider in the same way that you would bind to any other data source—namely, using a Binding object and specifying the XmlDataProvider in the Source property, as shown here:
Using XPath with XmlDataProvider You can use XPath expressions to filter the results exposed by the XmlDataProvider, or to filter the records displayed in the bound controls. By setting the XPath property of the XmlDataProvider to an XPath expression, you can filter the data provided by the source. The following example filters the results exposed by an XmlDataProvider object to include only those nodes called in the top-level node:
You can also apply XPath expressions in the bound controls. The following example sets the XPath property to Diamond (shown in bold), which indicates that only data contained in tags will be bound:
Exam Tip
There are different methods for binding to different data sources. Be sure that you understand how to bind not only to lists of data, but also to ADO.NET objects, ObjectDataProviders, and XmlDataProviders, because each one has its own individual subtleties.
Lab: Accessing a Database In this lab, you create an application to view related data in a database. You will use the Northwind sample database and create an application that allows you to choose a contact name, view a list of dates on which orders were placed, and see the details about shipping and what products were ordered. In Lesson 3 you will build on this application.
Exercise 1: Adding a Data Source 1. From the companion CD, copy the Nwind database to a convenient location. The complete solution assumes that the database will be copied to the root directory on your C drive.
Lesson 2: Binding to Data Sources
233
2. Open the partial solution for this lesson. 3. In Visual Studio, from the Data menu choose Add New Data Source. The Data Source Configuration Wizard opens, with Database selected in the first page. Click Next to continue. 4. On the Choose Your Data Connection page, click New Connection to open the Add Connection dialog box. Change the Data Source to Microsoft Access Database File, click Continue, and then click Browse to browse to the location of your database file. After selecting your database file, click Open. In the Add Connection dialog box, click Test Connection to test the connection. If the test is successful, click OK, click Next, and then click Yes in the pop-up window. Click Next again. 5. On the Choose Your DataBase Objects page, expand the Tables node and select the Customers table and the Orders table. Then expand the Views node, select Order Details Extended, and click Finish. A new class called NwindDataSet is added to your solution, and table adapters are created. 6. In Solution Explorer, double-click NwindDataSet.xsd to open the Dataset Designer. Right-click the Dataset Designer and choose Add and then Relation to open the Relation window. 7. In the Relation window, set Parent Table to Orders and set Child Table to Order Details Extended. Verify that the KeyColumn and ForeignKeyColumn are set to OrderID. Name this relation Orders_Order_Details_Extended and click OK to create the new data relation. 8. From the Build menu, choose Build Solution to build and save your solution.
Exercise 2: Binding to Relational Data 1. In code view, add the following code to your Window class to create an instance of the Nwind typed data set and the table adapters for your database: ' VB Dim aset As NwindDataSet = New NwindDataSet() Dim custAdap As NwindDataSetTableAdapters.CustomersTableAdapter = _ New NwindDataSetTableAdapters.CustomersTableAdapter() Dim ordAdap As NwindDataSetTableAdapters.OrdersTableAdapter = _ New NwindDataSetTableAdapters.OrdersTableAdapter() Dim ord_det_extAdap As _ New NwindDataSetTableAdapters.Order_Details_ExtendedTableAdapter = _ NwindDataSetTableAdapters.Order_Details_ExtendedTableAdapter() // C# NwindDataSet aset = new NwindDataSet(); NwindDataSetTableAdapters.CustomersTableAdapter custAdap = new NwindDataSetTableAdapters.CustomersTableAdapter();
234
Chapter 5
Configuring Databinding
NwindDataSetTableAdapters.OrdersTableAdapter ordAdap = new NwindDataSetTableAdapters.OrdersTableAdapter(); NwindDataSetTableAdapters.Order_Details_ExtendedTableAdapter ord_det_extAdap = new NwindDataSetTableAdapters.Order_Details_ExtendedTableAdapter();
2. In the constructor, add the code in bold after the call to InitializeComponent to initialize the data set and set the data context for Grid1. (Note: Visual Basic users must add the constructor.) ' VB Public Sub New() ' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. custAdap.Fill(aset.Customers) ordAdap.Fill(aset.Orders) ord_det_extAdap.Fill(aset.Order_Details_Extended) Grid1.DataContext = aset.Customers End Sub // C# public Window1() { InitializeComponent(); custAdap.Fill(aset.Customers); ordAdap.Fill(aset.Orders); ord_det_extAdap.Fill(aset.Order_Details_Extended); Grid1.DataContext = aset.Customers; }
3. In XAML view, bind the first ListBox to the Customers table and display the ContactName field. Set the IsSynchronizedWithCurrentItem property to True. Your code for this element should resemble the following:
4. In XAML view, bind the second ListBox to the orders exposed by the CustomersOrders DataRelation and display the OrderDate. Set the IsSynchronizedWithCurrentItem property to True. An example is shown here:
5. Bind the Text property of the four TextBox elements to the ShipName, ShipAddress, ShipCity, and ShipCountry properties, respectively. The code should look like this:
Lesson 2: Binding to Data Sources
235
6. Bind the final ListBox to the records returned through the Orders_Order_Details_ Extended relation and display the ProductName, as shown here:
7. Press F5 to build and run your application. As you click either of the first two ListBox elements, note that the address and the products ordered change to reflect the newly selected ContactName and OrderDate.
Lesson Summary Q
When binding an item control to a list of data, you must set the ItemsSource property to a Binding that specifies the source of the list. The DisplayMemberPath indicates what property of the list should be displayed in the item control.
Q
Individual properties can be bound to lists of data. Initially, the first member of any such bound list is displayed, but the list can be navigated through the default ICollectionView retrieved through the CollectionViewSource object.
Q
ADO.NET objects typically must be initialized in code, making declarative Bindings difficult. You can create a Binding that specifies only the Path to the Binding object and then set the DataContext property in code to create the Binding.
Q
When binding to hierarchical data, you should set the IsSynchronizedWithCurrentItem property on each item control to True to enable automatic UI updates of hierarchical data. You can set a Binding to detail lists by specifying the detail list through the Path property.
Q
You can bind to the value returned by a method through ObjectDataProvider.
Q
You can bind to XML with XmlDataProvider. XmlDataProvider requires XML as a file, an XmlDocument object, or a data island.
236
Chapter 5
Configuring Databinding
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 2, “Binding to Data Sources.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following code snippets shows a ListBox correctly bound to the CustomerAddress field of a data table? The data table is named Customers in an ADO.NET data set named mySet. Take both XAML and code into account in the possible answers. A. ' VB Grid1.DataContext=mySet // C# Grid1.DataContext=mySet;
B. ' VB Grid1.DataContext=mySet // C# Grid1.DataContext=mySet;
C. ' VB Grid1.DataContext=mySet.Customers // C# Grid1.DataContext=mySet.Customers;
Lesson 2: Binding to Data Sources
237
D. ' VB Grid1.DataContext=mySet.Customers // C# Grid1.DataContext=mySet.Customers;
2. You’re binding a ListBox to the Price field in an ADO.NET data table called Details. This table is related to the Orders table through a parent relation called OrdersDetails. The Orders table is related to a table called Customers through a parent relation called CustomersOrders. The DataContext for your ListBox has been set to the Customers table, which is the parent table of the CustomersOrders relation. Which of the following correctly binds the ListBox to the Price field? A.
B.
C.
D.
238
Chapter 5
Configuring Databinding
Lesson 3: Manipulating and Displaying Data WPF has built-in functionality for sorting, grouping, and filtering data. In addition, it provides unprecedented support for customizing data display through data template technology. In this lesson, you learn to apply sorting and grouping through the ICollectionView interface. You learn to create and implement custom filters for data displayed in WPF, and you learn to use data templates to customize the appearance of data in your user interface. After this lesson, you will be able to: Q
Create and implement a data template
Q
Sort data through ICollectionView
Q
Apply grouping to data with ICollectionView
Q
Use an IComparer to create a custom sorting scheme
Q
Apply a custom filter to bound data
Q
Apply a filter to a bound ADO.NET object
Estimated lesson time: 30 minutes
Data Templates So far, you have seen how to bind content controls and item controls to data to display bound data in the user interface. The results, however, have been underwhelming. You have seen how to present data fields as simple text in content controls and lists of text in item controls. You can create a rich data presentation experience, however, by incorporating data templates into your UI design. A data template is a bit of XAML that describes how bound data is displayed. A data template can contain elements that are each bound to a data property, along with additional markup that describes layout, color, and other aspects of appearance. The following example demonstrates a simple data template that describes a Label element bound to the ContactName property. The Foreground, Background, BorderBrush, and BorderThickness properties are also set:
Figure 5-1 shows a ListBox displaying a list of bound data. Figure 5-2 shows the same ListBox displaying the same bound data with the previously cited data template set to the ListBox.ItemsTemplate property.
Lesson 3: Manipulating and Displaying Data
239
Figure 5-1 A bound ListBox without a data template
Figure 5-2 A bound ListBox with a data template applied
When binding a property or list directly to a control, you are limited to binding a single property. With data templates, however, you can bind more than one property in each item, thereby displaying multiple bits of related data together. The following example demonstrates this concept. It is a modification of the template shown in the previous example, but a header is added to each ContactName that consists of the text “Company Name:” and the CompanyName associated with that contact name. Additional properties are set to provide style, and both labels are placed within a StackPanel to facilitate layout: Company Name:
240
Chapter 5
Configuring Databinding
The results of applying this data template are shown in Figure 5-3.
Figure 5-3 A bound ListBox with a data template that includes a header with related data
You can apply data templates to content controls as well. Although a content control can display only a single record at a time, it can use all the formatting features of the data template technology.
Setting the Data Template You set the data template on a control by setting one of two properties. For content controls, you set the ContentTemplate property, as shown in bold here:
For item controls, you set the ItemsTemplate property, as shown in bold here:
Lesson 3: Manipulating and Displaying Data
241
Note that for item controls, the DisplayMemberPath and ItemTemplate properties are mutually exclusive—you can set one but not the other. A frequent pattern with data templates is to define them in a resource collection and reference them in your element, rather than defining them inline as shown in the previous examples. All that is required to reuse a data template in this manner is to define the template in a resource collection and set a Key for the template, as shown here:
Then you can set the template by referring to the resource, as shown in bold here:
Exam Tip Although data templates appear to be fairly simple, be sure that you have lots of practice implementing and using them. Data templates are an extremely powerful feature of WPF and promise to f igure prominently on the exam.
Sorting Data When presenting data in the user interface, you want to sort it in various ways. Bound data can be sorted through the default ICollectionView element for the data list. You saw in Lesson 2 how to obtain a reference to the default collection view through the CollectionViewSource object, as shown again here: ' VB Dim myView As System.ComponentModel.ICollectionView myView = CollectionViewSource.GetDefaultView(myCollection) // C# System.ComponentModel.ICollectionView myView; myView = CollectionViewSource.GetDefaultView(myCollection);
ICollectionView exposes a property called SortDescriptions, which contains a collection of SortDescription objects. SortDescription objects describe the column name to sort by,
242
Chapter 5
Configuring Databinding
and a direction that specifies either an ascending or descending sort order. The following example demonstrates sorting by a column named LastName in ascending order: ' VB myView.SortDescriptions.Add(New _ System.ComponentModel.SortDescription("LastName", _ System.ComponentModel.ListSortDirection.Ascending) // C# myView.SortDescriptions.Add(new System.ComponentModel.SortDescription("LastName", System.ComponentModel.ListSortDirection.Ascending);
SortDescription objects are applied to a collection in the order that they are added to the collection view. Thus, if you wanted to sort first by the LastName column and then by the FirstName column, you first would add a SortDescription that specified sorting by LastName and then add a SortDescription that specified sorting by FirstName, as shown here: ' VB myView.SortDescriptions.Add(New _ System.ComponentModel.SortDescription("LastName", _ System.ComponentModel.ListSortDirection.Ascending) myView.SortDescriptions.Add(New _ System.ComponentModel.SortDescription("FirstName", _ System.ComponentModel.ListSortDirection.Ascending) // C# myView.SortDescriptions.Add(new System.ComponentModel.SortDescription("LastName", System.ComponentModel.ListSortDirection.Ascending); myView.SortDescriptions.Add(new System.ComponentModel.SortDescription("FirstName", System.ComponentModel.ListSortDirection.Ascending);
Applying Custom Sorting If you are binding to a class whose default view is a ListCollectionView (that is, it implements IList but not IBindingList), such as ObservableCollection, you can implement custom sort orders by setting the ListCollectionView.CustomSort property. The CustomSort property takes an object that implements the IComparer interface. The IComparer interface requires a single method called Compare that takes two arguments and returns an integer that represents the result of the comparison of those two objects. The following example demonstrates an implementation of the IComparer interface that takes two Employee objects and sorts by the length of the LastName property: ' VB Public Class myComparer Implements IComparer
Lesson 3: Manipulating and Displaying Data
243
Public Function Compare(ByVal x As Object, ByVal y As Object) As _ Integer Implements System.Collections.IComparer.Compare Dim empX As Employee = CType(x, Employee) Dim empY As Employee = CType(y, Employee) Return empX.LastName.Length.CompareTo(empY.LastName.Length) End Function End Class // C# public class myComparer : System.Collections.IComparer { public int Compare(object x, object y) { Employee empX = (Employee)x; Employee empY = (Employee)y; return empX.LastName.Length.CompareTo(empY.LastName.Length); } }
Then you can create an instance of this class to set to the CustomSort property of your ICollectionView, as shown here: ' VB Dim myView As ListCollectionView myView = CType(CollectionViewSource.GetDefaultView(myCollection), _ ListCollectionView) myView.CustomSort = New myComparer() // C# System.ComponentModel.ICollectionView myView; myView = (ListCollectionView)CollectionViewSource.GetDefaultView(myCollection); myView.CustomSort = new myComparer();
Grouping ICollectionView also supports grouping data. Grouping data is similar to sorting, but takes advantage of the built-in functionality of item controls to provide formatting for different groups, thus allowing the user to distinguish groups visually at run time. You can create a group by adding a PropertyGroupDescription object to the ICollectionView. GroupDescriptions collection. The following example demonstrates creating groups based on the value of the EmployeeTitle property: ' VB Dim myView As System.ComponentModel.ICollectionView myView = CollectionViewSource.GetDefaultView(myCollection) myView.GroupDescriptions.Add( _ New PropertyGroupDescription("EmployeeTitle"))
244
Chapter 5
Configuring Databinding
// C# System.ComponentModel.ICollectionView myView; myView = CollectionViewSource.GetDefaultView(myCollection); myView.GroupDescriptions.Add( new PropertyGroupDescription("EmployeeTitle"));
At first glance, applying this grouping seems to have the same effect as sorting by EmployeeTitle. The difference becomes evident when this data is displayed in an item control with the GroupStyle property set. The GroupStyle property provides formatting information for grouped items and allows you to set how they are displayed. Table 5-9 explains the properties of the GroupStyle object. Table 5-9
Properties of GroupStyle
Property
Description
ContainerStyle
Gets or sets the style that is applied to the GroupItem generated for each item.
ContainerStyleSelector
Represents an instance of StyleSelector that determines the appropriate style to use for the container.
HeaderTemplate
Gets or sets the template that is used to display the group header.
HeaderTemplateSelector
Represents an instance of StyleSelector that determines the appropriate style to use for the header.
Panel
Gets or sets a template that creates the panel used to lay out the items.
The following example demonstrates setting the GroupHeader and Panel properties. In this example, the Panel is replaced by a WrapPanel that causes the groups to wrap horizontally, and the HeaderTemplate is set to display the header in purple text on a red background:
Lesson 3: Manipulating and Displaying Data
245
Note that in the data template for the GroupStyle.HeaderTemplate property, the Content property is bound to the Name property. This is not the Name property of the objects bound in the ListBox, but actually the Name property of the PropertyGroupDescription. Thus, when you want the header to reflect the name of the category, you always bind to the Name property rather than the name of the property you are using to group your data.
Creating Custom Grouping In addition to creating groupings based on the values of object properties, you can create custom groups to arrange your items in an item control. To create a custom group, you must create a class that implements the IValueConverter interface. The IValueConverter interface exposes two methods: Convert and ConvertBack. For the purposes of creating custom grouping, you need to provide only a “real” implementation for the Convert method—the ConvertBack method will never be called. The Convert method takes an object as a parameter, which will be the object represented by the property named in the PropertyGroupDescription. In the Convert method, you should examine the value of the object, determine which custom group it belongs to, and then return a value that represents that custom group. The following example is a simple demonstration of this concept. It is designed to take a value from a Region property and determine if that region is the Isle of Wight. If it is, it returns “Isle of Wight” as the group header; otherwise it returns “Everyplace else.” ' VB Public Class RegionConverter Implements IValueConverter Public Function Convert(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.Convert If Not value.ToString = "" Then Dim region As String = value.ToString If region = "Isle of Wight" Then Return "Isle of Wight" End If End If
246
Chapter 5
Configuring Databinding
Return "Everyplace else" End Function Public Function ConvertBack(ByVal value As Object, ByVal targetType _ As System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.ConvertBack Throw New NotImplementedException() End Function End Class // C# public class RegionGrouper : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (!(value.ToString() == "")) { string Region = (string)value; if (Region == "Isle of Wight") return "Isle of Wight"; } return "Everyplace else"; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
Then you can add your custom PropertyGroupDescription by specifying the Region property and a new instance of the RegionGrouper property, as shown here: ' VB Dim myView As System.ComponentModel.ICollectionView myView = CollectionViewSource.GetDefaultView(myCollection) myView.GroupDescriptions.Add(New PropertyGroupDescription("Region", _ New RegionGrouper())) // C# System.ComponentModel.ICollectionView myView; myView = CollectionViewSource.GetDefaultView(myCollection); myView.GroupDescriptions.Add(new PropertyGroupDescription("Region", new RegionGrouper()));
Filtering Data ICollectionView has built-in functionality that allows you to designate a method to filter the data in your collection view. The ICollectionView property accepts a Predicate
Lesson 3: Manipulating and Displaying Data
247
delegate that specifies a Boolean method that takes the object from the collection as an argument, examines it, and returns True if the object is included in the filtered view and False if the object is excluded. The following two examples demonstrate setting a simple filter. The first example demonstrates setting the filter on an ICollectionView, and the second example demonstrates the method that implements that filter. These examples involve a collection of items named myItems: ' VB Dim myView As System.ComponentModel.ICollectionView myView = CollectionViewSource.GetDefaultView(myItems) myView.Filter = New Predicate(Of Object)(AddressOf myFilter) // C# System.ComponentModel.ICollectionView myView; myView = CollectionViewSource.GetDefaultView(myItems); myView.Filter = new Predicate(myFilter);
The next example shows the myFilter method. It is a simple method that excludes all objects whose ToString method returns a string length of eight characters or less: ' VB Public Function myFilter(ByVal param As Object) As Boolean Return (param.ToString.Length > 8) End Function // C# public bool myFilter(object param) { return (param.ToString().Length > 8); }
Note that the Predicate delegate to which the Filter property is set must be a Predicate [Predicate(Of Object) in Visual Basic], rather than a strongly typed Predicate such as Predicate. You must perform any necessary casting in the method that implements the filter.
Filtering ADO.NET Objects The filtering method described in the previous section does not work when binding to ADO.NET objects, or anytime the underlying ICollectionView object is actually a BindingListCollectionView. This is because the data collection bound to in this view actually is connected through an ADO.NET DataView layer, which implements its own filtering scheme. You can, however, take advantage of the filtering capabilities of DataView by setting the BindingListCollectionView.CustomFilter property. This property takes a string-based expression and applies it directly to the underlying DataView. The filter expression is a string expression in the following format:
248
Chapter 5
Configuring Databinding
For example, the following filter limits the rows returned to rows whose Sandwich property has a value of “Muffaletta”: Sandwich = 'Muffaletta'
To set the filter, you would create the following code: ' VB Dim myView As BindingListCollectionView myView = CType(CollectionViewSource.GetDefaultView(myItems), _ BindingListCollectionView; myView.CustomFilter = "Sandwich = 'Muffaletta'" // C# BindingListCollectionView myView; myView = (BindingListCollectionView)CollectionViewSource.GetDefaultView(myItems); myView.CustomFilter = "Sandwich = 'Muffaletta'";
String literals for the filter expression must be enclosed in single quotes, as shown in the preceding code. While the complete rules for filter expressions are beyond the scope of this lesson, they follow the same syntax as comparison expressions for the DataColumn.Expression property, and a complete reference can be found in the Microsoft reference topic of that name, which can be viewed at http://msdn2.microsoft .com/en-us/library/system.data.datacolumn.expression(VS.71).aspx.
Quick Check Q
What is the difference between sorting by a particular property and creating a group based on that property?
Quick Check Answer Q
When you create a group, you have access to the GroupStyle object that is created for that group. You can set individual templates for the header and container and supply a different layout panel.
Lab: Practice with Data Templates and Groups In this lab, you expand on the application you completed in the previous lab. You add data templates to display greater detail and visual appeal to the list of ContactNames and ProductNames. In addition, you create a custom group to group ContactNames by country.
Lesson 3: Manipulating and Displaying Data
249
Exercise 1: Adding Data Templates 1. Open the completed solution for Lesson 2 of this chapter, either from the CD or your completed exercise. 2. In XAML view, delete the following code from the declaration for listBox1: DisplayMemberPath="ContactName"
3. In XAML view, add the following data template and set it to the ItemTemplate property of listBox1: Company Name:
This data template does a lot of things. It creates a StackPanel, in which is bound the CompanyName field, and then lays it out so that it appears to the left of the ContactName for that company. It also adds some colorful UI effects. 4. Delete the following code from the declaration for listBox3: DisplayMemberPath="ProductName"
5. In XAML view, set the following data template to the ItemTemplate property of listBox3:
This data template binds to the Quantity and ExtendedPrice fields, as well as the ProductName fields, and sets the background color for each field.
250
Chapter 5
Configuring Databinding
6. Press F5 to build and run your application. Note that the display is now more colorful and that additional fields are bound in the ListBox elements. Note that although a Label in the data template is bound to the ExtendedPrice field, it appears as a regular number instead of being formatted as currency. In Chapter 6, you will learn how to use custom converters to apply data formatting.
Exercise 2: Adding a Custom Group and Sort 1. In code view, add the following class to your project. This class is a simple converter that takes an object and returns a string representation of that object with the string “Country:” prepended to it. Note that the ConvertBack method is not implemented because this class is used only for one-way conversions: ' VB Public Class CountryGrouper Implements IValueConverter Public Function Convert(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.Convert Dim country As String = value.ToString() Return "Country: " & country End Function Public Function ConvertBack(ByVal value As Object, ByVal targetType _ As System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.ConvertBack Throw New NotImplementedException() End Function End Class // C# public class CountryGrouper : IValueConverter { public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) { string country = value.ToString(); return "Country: " + country; } public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
Lesson 3: Manipulating and Displaying Data
251
2. Add the following code to the constructor for Window1 at the end of the method to create the default view and set a GroupDescription that uses the new CountryGrouper class. This custom class groups ContactNames by country: ' VB Dim aView As System.ComponentModel.ICollectionView aView = CollectionViewSource.GetDefaultView(aset.Customers) aView.GroupDescriptions.Add(New PropertyGroupDescription("Country", _ New CountryGrouper())) // C# System.ComponentModel.ICollectionView aView; aView = CollectionViewSource.GetDefaultView(aset.Customers); aView.GroupDescriptions.Add(new PropertyGroupDescription("Country", new CountryGrouper()));
3. Add code at the end of the preceding code to add a new SortDescription to the default view to sort in ascending order by country: ' VB aView.SortDescriptions.Add( _ New System.ComponentModel.SortDescription("Country", _ ComponentModel.ListSortDirection.Ascending)) // C# aView.SortDescriptions.Add( new System.ComponentModel.SortDescription("Country", System. ComponentModel.ListSortDirection.Ascending));
4. In XAML view, set the ListBox.GroupStyle property of listBox1 to the GroupStyle shown here. This GroupStyle provides a colorful header for the country groups created by the custom GroupDescription:
5. Press F5 to build and run your application. You might need to expand your window to see the full effect of the changes you’ve made.
252
Chapter 5
Configuring Databinding
Lesson Summary Q
Data templates are pieces of XAML markup that specify how bound data should be displayed. They allow you to set the look and feel of bound data, as well as to specify related fields to be displayed. You can set a data template for an item control by setting the ItemTemplate property, and you can set the data template for a content control by setting the ContentTemplate property.
Q
You can apply sorting to data by accessing the default collection view and adding a new SortDescription to the SortDescriptions property. Standard sorting allows you to sort based on a data field value in either ascending or descending mode. You can create custom sorts by creating a class that implements IComparer and setting it to the CustomSort property.
Q
You can create groups of data by adding a new PropertyGroupDescription to the GroupDescriptions property of the default collection view. You can set the header and other formatting for groups by setting the GroupStyle property of the item control that binds the data.
Q
You can specify a delegate to perform filtering by creating a Boolean method that performs the filtering and specifying a Predicate that points to that method. This does not work with ADO.NET objects, however, and you must set the CustomFilter property to a filter expression to filter ADO.NET records.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 3, “Manipulating and Displaying Data.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following code snippets correctly demonstrates a data template that binds the ContactName field set in a ListBox? Assume that the DataContext is set correctly. A.
Lesson 3: Manipulating and Displaying Data
253
B.
C.
D.
2. Which of the following code snippets correctly sets the filter on the CollectionView object myView? A. ' VB myView.Filter = New Predicate(Of Object)(AddressOf myFilter) Public Function myFilter(ByVal param As Object) As Boolean Return (param.ToString.Length > 8) End Function // C# myView.Filter = new Predicate(myFilter); public bool myFilter(object param) { return (param.ToString().Length > 8); }
B. ' VB myView.Filter = New Predicate(Of Object)(AddressOf myFilter) Public Function myFilter(ByVal param As Object) As Object Return (param.ToString.Length > 8) End Function
254
Chapter 5
Configuring Databinding
// C# myView.Filter = new Predicate(myFilter); public object myFilter(object param) { return (param.ToString().Length > 8); }
C. ' VB myView.CustomFilter = New Predicate(Of Object)(AddressOf myFilter) Public Function myFilter(ByVal param As Object) As Boolean Return (param.ToString.Length > 8) End Function // C# myView.CustomFilter = new Predicate(myFilter); public bool myFilter(object param) { return (param.ToString().Length > 8); }
D. ' VB myView.CustomFilter = New Predicate(Of Object)(AddressOf myFilter) Public Function myFilter(ByVal param As Object) As Object Return (param.ToString.Length > 8) End Function // C# myView.CustomFilter = new Predicate(myFilter); public object myFilter(object param) { return (param.ToString().Length > 8); }
Chapter 5 Review
255
Chapter Review To practice and reinforce the skills you learned in this chapter further, you can do any or all of the following: Q
Review the chapter summary.
Q
Review the list of key terms introduced in this chapter.
Q
Complete the case scenarios. These scenarios set up real-world situations involving the topics of this chapter and ask you to create a solution.
Q
Complete the suggested practices.
Q
Take a practice test.
Chapter Summary 1. The Binding object is the glue that holds all WPF bindings together. Bindings bind a source property of a source object to a target property on a target object. You can specify the source object by setting the Element, Source, or Relative property of the binding, or by setting the DataContext property of the target object. You specify the source property by using the Path property or by setting the DisplayMemberPath property for item controls. You set a binding to the target property on the target object, thereby establishing the binding. 2. You set the Binding object’s Mode property to configure how changes are propagated between source and target, and you set the Binding object’s UpdateSourceTrigger property to configure when changes are propagated. 3. Any object can be a data source object. You can bind to single objects, collections, or ADO.NET objects directly through WPF. Hierarchical data is available by setting the Path property of the binding to the appropriate path. You can bind to the result of a method call by binding to an ObjectDataProvider, and you can bind to XML data through the XmlDataProvider. 4. Data templates allow you to configure how bound data is displayed in your controls. You can set data templates for both item controls and content controls. 5. You can retrieve the default ICollectionView object from the CollectionViewSource object. This object can be used to navigate bound collections in code. You can establish sorting and grouping through the default ICollectionView object. You can create custom sorts by creating a class that implements IComparer. You can create custom groups by creating a class that implements IValueConverter. You can filter
256
Chapter 5 Review
a bound collection by creating a method to perform filtering and setting the Filter property to a new Predicate that specifies that method. This filtering is not available for ADO.NET objects—instead, you must set the CustomFilter property to an expression.
Key Terms Do you know what these key terms mean? You can check your answers by looking up the terms in the glossary at the end of the book. Q
Ancestor
Q
Binding
Q
DataContext
Q
ObjectDataProvider
Q
XmlDataProvider
Case Scenarios In the following case scenarios, you apply what you’ve learned about how to use controls to design user interfaces. You can find answers to these questions in the “Answers” section at the end of this book.
Case Scenario 1: Getting Information from the Field Our field agents are diligently scouring the world trying to locate new supplies of “stuff” for our ongoing research efforts. They need to relay regular reports to us in a specific format, which will be logged into our central database. Each of the agents is equipped with a laptop and a satellite Internet connection, but direct connections to our database are impossible. They also have a report generator that outputs XML files and emails them to our mail-handling program, which stores them as raw XML in a queue where they can be vetted by support staff before being saved in the database. You are in charge of creating an application to help these support staff handle the data processing.
Technical Requirements Q
XML files can be read, but they must not be altered in any way.
Q
The user must be able to edit to the information before saving it into the central database.
Chapter 5 Review
257
Questions Answer the following questions for your manager: 1. What is the ideal design for our application? 2. What WPF elements will be useful in building this application?
Case Scenario 2: Viewing Customer Data One of our clients has contracted with us to create an application to help maximize his business potential. He wants to view customer records that meet any of several different specific sets of criteria—customers who buy big-ticket items, customers who buy a lot of small items, customers who buy only in April, and so on. And he wants to view them all at once, but he wants them to be arranged in groups by these criteria. He does not want to see any records that do not match any of the criteria groups. All the information required by the application is available in the database that he is supplying.
Technical Requirements Q
The application should display only records that fall into one of the criteria groups.
Q
The records should be arranged by criteria group.
Question Answer the following question for your manager: Q
How can we design the application to achieve the grouping that the client wants with minimal development time?
Suggested Practices Q
Rewrite the application from Lesson 2 or Lesson 3 to allow the user to edit the quantity of products ordered.
Q
Create a Really Simple Syndication (RSS) reader that downloads and formats RSS data using XmlDataProvider.
Q
Create an application that accesses the Northwind database and allows the user to filter on the CompanyName element.
258
Chapter 5 Review
Take a Practice Test The practice tests on this book’s companion CD offer many options. For example, you can test yourself on just the content covered in this chapter, or you can test yourself on all the 70-502 certification exam content. You can set up the test so that it closely simulates the experience of taking a certification exam, or you can set it up in study mode so that you can look at the correct answers and explanations after you answer each question. MORE INFO
Practice tests
For details about all the practice test options available, see the section “How to Use the Practice Tests,” in this book’s Introduction.
Chapter 6
Converting and Validating Data In Chapter 5, “Configuring Databinding,” you learned how to bind your controls to a variety of different sources and to create data templates to configure how that data is displayed in the presentation layer. In this chapter, you learn how to apply custom conversions to your data to format strings, return objects, or apply conditional formatting to your presentation layer. You also learn how to validate data and configure custom data objects for data change notification.
Exam objectives in this chapter: Q
Convert and validate data.
Q
Configure notification of changes in underlying data.
Lessons in this chapter: Q
Lesson 1: Converting Data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Q
Lesson 2: Validating Data and Configuring Change Notification . . . . . . . . . . . 282
Before You Begin To complete the lessons in this chapter, you must have Q
A computer that meets or exceeds the minimum hardware requirements listed in the “About This Book” section at the beginning of the book
Q
Microsoft Visual Studio 2008 Professional Edition installed on your computer
Q
An understanding of Microsoft Visual Basic or C# syntax and familiarity with Microsoft .NET Framework version 3.5
Q
An understanding of Extensible Application Markup Language (XAML)
259
260
Chapter 6
Converting and Validating Data
Real World Matthew Stoecker The flexibility provided by the custom conversions available in the Windows Presentation Foundation (WPF) data model has made the creation of rich data applications a simple process for me. I now can return custom formats and create complex conditional formatting that really allows my applications to stand out. Additionally, I find the validation paradigm in WPF databinding to be much easier to use than previous versions. With data manipulation so intuitive, writing databound applications is much less of a chore than it used to be, and a lot more fun!
Lesson 1: Converting Data
261
Lesson 1: Converting Data WPF incorporates rich functionality for converting data that is displayed in the presentation layer. Using value converters, you can format data for display, localize data, create objects based on underlying data, and even create objects and values that derive from multiple bound elements. In this lesson, you learn to implement IValueConverter to create custom value converters for your data presentation layer. After this lesson, you will be able to: Q
Implement IValueConverter
Q
Use a converter to format data
Q
Use a converter to return an object
Q
Format data conditionally using a converter
Q
Localize data using a converter
Q
Implement IMultiValueConverter
Estimated lesson time: 30 minutes
Implementing IValueConverter In Chapter 5, you saw how to implement IValueConverter for the purpose of creating custom GroupDescription objects, but the capabilities of IValueConverter run much deeper than simply creating groups. By implementing this interface, you can create specialized classes for a variety of conversion and formatting duties. At the center of conversion is the IValueConverter interface. The IValueConverter interface has two member methods: Convert, which converts an input type to an output type; and ConvertBack, which reverses the conversion. An empty implementation of IValueConverter is shown here: ' VB Public Class myConverter Implements IValueConverter Public Function Convert(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.Convert Throw New NotImplementedException() End Function Public Function ConvertBack(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _
262
Chapter 6
Converting and Validating Data
System.Windows.Data.IValueConverter.ConvertBack Throw New NotImplementedException() End Function End Class // C# public class myConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
The Convert method converts the object represented by the value parameter into the output object, and the ConvertBack method is meant to reverse the conversion performed by the Convert method. In some cases, a reverse conversion is not possible, and in other cases you never call the ConvertBack method, so it is common practice not to provide an explicit implementation for the ConvertBack method if it will never be called. Note, however, that for cases in which two-way binding will be implemented, you must implement both the Convert and ConvertBack methods. Unless you are certain that ConvertBack will never be called, you should provide an implementation. In addition to implementing IValueConverter, you must decorate the class with the ValueConversion attribute, which specifies the source type and the target type for the converter. An example that specifies a source type of Integer and a target type of String is shown here: ' VB _ // C# [ValueConversion(typeof(int), typeof(string))]
The next example shows a simple converter that converts a numeric code representing a job title into a string containing that job title and vice versa: ' VB _ Public Class myConverter Implements IValueConverter
Lesson 1: Converting Data
Public Function Convert(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.Convert Dim a As Integer = CInt(value) Select Case a Case 1 Return "Group Manager" Case 2 Return "Manager" Case 3 Return "Programmer" Case Else Return "Title not defined" End Select End Function Public Function ConvertBack(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.ConvertBack Dim aString As String = value.ToString() Select Case aString Case "Group Manager" Return 1 Case "Manager" Return 2 Case "Programmer" Return 3 Case Else Return 4 End Select End Function End Class // C# [ValueConversion(typeof(int), typeof(string))] public class myConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { int a = int.Parse(value.ToString()); switch (a) { case 1: return "Group Manager"; case 2: return "Manager"; case 3: return "Programmer"; default: return "Title not defined"; } }
263
264
Chapter 6
Converting and Validating Data
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string a = value.ToString(); switch (a) { case "Group Manager": return 1; case "Manager": return 2; case "Programmer": return 3; default: return 4; } } }
To use a converter in your XAML code, you first must create a reference to the namespace that contains it in the XAML, as shown in bold here:
You then must create an object instance, typically by adding it to one of the element Resource collections, and assign a key value. Resources are discussed in depth in Chapter 9, “Resources, Documents, and Localization.” An example is shown here:
Once an object instance is created, you can set the Converter property of your binding to it by referring to the resource, as shown here:
Most of the cases discussed in this chapter require only one-way conversion of data for display purposes. It is unnecessary to provide a real implementation for ConvertBack in these cases.
Using Converters to Format Strings One of the most convenient uses for converters is to format strings. Because data is stored in a database without formatting most of the time, formatting generally is up to
Lesson 1: Converting Data
265
the data presentation layer to accomplish. Phone numbers, dates, currency, and Social Security numbers are all examples of data that might benefit from string formatting.
Formatting as Currency Currency is a prime example of the need for string formatting. Monetary values typically are stored in numeric data types, but most data presentation designs would want to present this data as a currency-formatted string. The ToString method of numeric data types allows you to specify a string that indicates how the resulting string should be formatted. To format a resultant string as currency, you insert “C”, as shown here: ' VB aString = aDouble.ToString("C") // C# aString = aDouble.ToString("C");
You can incorporate this functionality into a simple converter that takes a Decimal value and outputs a currency-formatted string. The Convert method of such a converter is shown here: ' VB Public Function Convert(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.Convert Dim a As Decimal = CDec(value.ToString) Return a.ToString("C") End Function // C# public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { decimal a = decimal.Parse(value.ToString()); return a.ToString("C"); }
The next example shows the ConvertBack method from this same converter: ' VB Public Function ConvertBack(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.ConvertBack Dim result As Decimal Dim a As String = value.ToString() If Decimal.TryParse(a, System.Globalization.NumberStyles.Any, Nothing, _ result) Then Return result
266
Chapter 6
Converting and Validating Data
Else ' Implement code to determine or return a default value here Return 0 End If End Function // C# public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { decimal result; string a = value.ToString(); if (decimal.TryParse(a, System.Globalization.NumberStyles.Any, null, out result)) return result; else // Implement code to determine or return a default value here return 0; }
Currency formatting takes the current culture setting into account. The currency symbol shown in the formatted string varies depending on the culture setting.
Formatting Dates You can use the same general scheme for formatting dates. The DateTime.ToString method accepts the format strings shown in Table 6-1. Table 6-1
Format Strings for the DateTime Structure
Format String
Format Name
Example
Usage Example
d
Short Date
07/14/1969
ToString("d")
D
Long Date
Monday, July 14, 1969
ToString("D")
f
Long Date and Short Time
Monday, July 14, 1969 11:07 PM
ToString("f")
F
Long Date and Long Time
Monday, July 14, 1969 11:07:17 PM
ToString("F")
G
General
07/14/1969 11:07:17 PM
ToString("G")
M
Month and Day
July 14
ToString("M")
s
ISO Sortable Standard
1969-07-14 11:07:17
ToString("s")
Lesson 1: Converting Data
267
The format returned by General formatting (“G”) varies according to the local culture setting. In addition to format strings, the DateTime structure contains several built-in methods for returning date and time strings in a variety of formats.
Other String Formatting The ToString method of numeric data types provides a variety of additional format strings, a complete list of which can be found in the Visual Studio documentation. However, you might need to create a converter to convert to a custom string format. In that case, you have to write code to perform the conversion explicitly. The following example demonstrates a converter that accepts a nine-digit integer and returns a string representation of that integer formatted as a Social Security number: ' VB _ Public Class SSConverter Implements IValueConverter Public Function Convert(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.Convert Dim a As String = value.ToString() If Not a.Length = 9 Then Throw New ArgumentException("Number is in the wrong format") End If a = a.Insert(5, "-") a = a.Insert(3, "-") Return a End Function Public Function ConvertBack(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.ConvertBack Dim a As String = value.ToString() a = a.Remove(6, 1) a = a.Remove(3, 1) Return CInt(a) End Function End Class // C# [ValueConversion(typeof(int), typeof(string))] public class SSConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
268
Chapter 6
Converting and Validating Data
{ string a = value.ToString(); if (!(a.Length==9)) throw new ArgumentException("Number is in the wrong format"); a = a.Insert(5, "-"); a = a.Insert(3, "-"); return a; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string a = value.ToString(); a = a.Remove(6, 1); a = a.Remove(3, 1); return int.Parse(a); } }
Using Converters to Return Objects In addition to formatting, you can return objects using converters. For example, you might store references to images as string paths in your database, but want to load and display that image when viewing data. Returning an object is as simple as creating the object in code and returning it as seen in previous examples. The following example takes a string formatted as a path and returns a BitmapImage object that represents the image stored at that path: ' VB _ Public Class ImageConverter Implements IValueConverter Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.Convert Try Dim myPath As String = CType(value, String) Dim myUri As New Uri(myPath) Dim anImage As New BitmapImage(myUri) Return anImage Catch ex As Exception Return New BitmapImage(New Uri("C:\ImageNotAvailable.jpg")) End Try End Function
_
Public Function ConvertBack(ByVal value As Object, ByVal targetType _ As System.Type, ByVal parameter As Object, ByVal culture As _
Lesson 1: Converting Data
269
System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.ConvertBack Throw New NotImplementedException() End Function End Class //C# [ValueConversion(typeof(string), typeof(BitmapImage))] public class ImageConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { try { string myPath = (string)value; Uri myUri = new Uri(myPath); BitmapImage anImage = new BitmapImage(myUri); return anImage; } catch { return new BitmapImage(new Uri("C:\\ImageNotAvailable.jpg")); } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
In this example, a string is converted to a Universal Resource Identifier (URI), and then that URI is used to generate the BitmapImage that is returned. In the event of an error, a default image is returned. Note that because it would be problematic to convert an image back to a path, the ConvertBack method is not implemented. Nevertheless, it is a best practice to implement ConvertBack whenever possible.
Using Converters to Apply Conditional Formatting in Data Templates One of the most useful things you can do with converters is to apply conditional formatting to displayed data. For example, suppose that you are writing an application that binds to a list of dates on which orders were placed. You might want orders that were placed in the current month to have a different foreground color than the other orders. You can accomplish this by binding the Foreground property of the control used to bind the date to the Date field and providing a converter that evaluates the
270
Chapter 6
Converting and Validating Data
date and returns a brush of the appropriate color. The following examples show such a converter, an instance of that converter added to the Window.Resources collection, and finally the Foreground property bound to the Date property using the converter to return a Brush object. First, here’s the converter: ' VB _ Public Class DateBrushConverter Implements IValueConverter Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.Convert Dim aDate As DateTime aDate = CType(value, DateTime) If aDate.Month = Now.Month Then Return New SolidColorBrush(Colors.Red) Else Return New SolidColorBrush(Colors.Black) End If End Function
_
Public Function ConvertBack(ByVal value As Object, ByVal targetType _ As System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.ConvertBack Throw New NotImplementedException() End Function End Class // C# [ValueConversion(typeof(DateTime), typeof(Brush))] public class DateBrushConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { DateTime aDate = (DateTime)value; if (aDate.Month == DateTime.Now.Month) return new SolidColorBrush(Colors.Red); else return new SolidColorBrush(Colors.Black); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
Lesson 1: Converting Data
271
Then, an instance is added to the Window.Resources collection, as shown in bold here:
Setters The most common class you will use in the construction of Styles is the Setter. As their name implies, Setters are responsible for setting some aspect of an element. Setters come in two flavors: property setters (or just Setters, as they are called in markup), which set values for properties; and event setters, which set handlers for events.
Lesson 1: Styles
307
Property Setters Property setters, represented by the tag in XAML, allow you to set properties of elements to specific values. A property setter has two important properties: the Property property, which designates the property that is to be set by the Setter, and the Value property, which indicates the value to which the property is to be set. The following example demonstrates a Setter that sets the Background property of a Button element to Red:
The value for the Property property must take the form of the following: Element.PropertyName
If you want to create a style that sets a property on multiple different types of elements, you could set the style on a common class that the elements inherit, as shown here:
This style sets the Background property of all elements that inherit from the Control to which it is applied.
Event Setters Event setters (represented by the tag) are similar to property setters, but they set event handlers rather than property values. The two important properties for an EventSetter are the Event property, which specifies the event for which the handler is being set; and the Handler property, which specifies the event handler to attach to that event. An example is shown here:
The value of the Handler property must specify an extant event handler with the correct signature for the type of event with which it is connected. Similar to property setters, the format for the Event property is Element.EventName
where the element type is specified, followed by the event name.
308
Chapter 7
Styles and Animation
Creating a Style You’ve seen the simplest possible implementation of a style: a single Setter between two Style tags, but you haven’t yet seen how to apply a style to an element. There are several ways to apply a style to an element or elements. This section examines the various ways to apply a style to elements in your user interface.
Setting the Style Property Directly The most straightforward way to apply a style to an element is to set the Style property directly in XAML. The following example demonstrates directly setting the Style property of a Button element:
While setting the Style directly in an element might be the most straightforward, it is seldom the best method. When setting the Style directly, you must set it for each element that you want to be affected by the Style. In most cases, it is simpler to set the properties of the element directly at design time. One scenario where you might want to set the Style directly on an element is to provide a set of Triggers for that element. Because Triggers must be set in a Style (except for EventTriggers, as you will see in the next section), you conceivably could set the Style directly to set triggers for an element.
Setting a Style in a Resources Collection The most common method for setting styles is to create the style as a member of a Resources collection and then apply the style to elements in your user interface by referencing the resource. The following example demonstrates creating a style as part of the Windows.Resources collection:
Lesson 1: Styles
309
Under most circumstances, you must supply a key value for a Style that you define in the Resources collection. Then you can apply that style to an element by referencing the resource, as shown in bold here:
The advantage to defining a Style in the Resources section is that you can then apply that Style to multiple elements by simply referencing the resource. Resources are discussed in detail in Chapter 9.
Applying Styles to All Controls of a Specific Type You can use the TargetType property to specify a type of element to be associated with the style. When you set the TargetType property on a Style, that Style is applied to all elements of that type automatically. Further, you do not need to specify the qualifying type name in the Property property of any Setters that you use—you can just refer to the property name. When you specify the TargetType for a Style that you have defined in a Resources collection, you do not need to provide a key value for that style. The following example demonstrates the use of the TargetType property:
When you apply the TargetType property, you do not need to add any additional markup to the elements of that type to apply the style. If you want an individual element to opt out of the style, you can set the style on that element explicitly, as seen here: No Style
This example explicitly sets the Style to Null, which causes the Button to revert to its default look. You also can set the Style to another Style directly, as seen earlier in this lesson.
Setting a Style Programmatically You can create and define a style programmatically. While defining styles in XAML is usually the best choice, creating a style programmatically might be useful when you want to create and apply a new style dynamically, possibly based on user preferences.
310
Chapter 7
Styles and Animation
The typical method for creating a style programmatically is to create the Style object in code; then create Setters (and Triggers, if appropriate); add them to the appropriate collection on the Style object; and then when finished, set the Style property on the target elements. The following example demonstrates creating and applying a simple style in code: ' VB Dim aStyle As New Style Dim aSetter As New Setter aSetter.Property = Button.BackgroundProperty aSetter.Value = Brushes.Red aStyle.Setters.Add(aSetter) Dim bSetter As New Setter bSetter.Property = Button.ContentProperty bSetter.Value = "Style set programmatically" aStyle.Setters.Add(bSetter) Button1.Style = aStyle // C# Style aStyle = new Style(); Setter aSetter = new Setter(); aSetter.Property = Button.BackgroundProperty; aSetter.Value = Brushes.Red; aStyle.Setters.Add(aSetter); Setter bSetter = new Setter(); bSetter.Property = Button.ContentProperty; bSetter.Value = "Style set programmatically"; aStyle.Setters.Add(bSetter); Button1.Style = aStyle;
You can also define a style in a Resources collection and apply that style in code, as shown here: ' VB Dim aStyle As Style aStyle = CType(Me.Resources("StyleOne"), Style) Button1.Style = aStyle // C# Style aStyle; aStyle = (Style)this.Resources["StyleOne"]; Button1.Style = aStyle;
Lesson 1: Styles
311
Implementing Style Inheritance You can use inheritance to create styles that conform to the basic look and feel of the original style but provide differences that offset some controls from others. For example, you might create one Style for all the Button elements in your user interface and create an inherited style to provide emphasis for one of the buttons. You can use the BasedOn property to create Style objects that inherit from other Style objects. The BasedOn property references another style and automatically inherits all the members of that Style and then allows you to build on that Style by adding additional members. The following example demonstrates two Style objects—an original Style and a Style that inherits it:
The result of applying these two styles is seen in Figure 7-1.
Figure 7-1 Two buttons—the original and an inherited style
312
Chapter 7
Styles and Animation
When a property is set in both the original style and the inherited style, the property value set by the inherited style always takes precedence. But when a property is set by the original style and not set by the inherited style, the original property setting is retained.
Quick Check Q
Under what circumstances is a Style automatically applied to an element? How else can a Style be applied to an element?
Quick Check Answer Q
A Style is applied to an element automatically when it is declared as a resource in the page and the TargetType property of the Style is set. If the TargetType property is not set, you can apply a Style to an element by setting that element’s Style property, either in XAML or in code.
Triggers Along with Setters, Triggers make up the bulk of objects that you use in creating styles. Triggers allow you to implement property changes declaratively in response to other property changes that would have required event-handling code in Windows Forms programming. There are five kinds of Trigger objects, as listed in Table 7-2. Table 7-2
Types of Trigger Objects
Type
Class Name
Description
Property trigger
Trigger
Monitors a property and activates when the value of that property matches the Value property.
Multi-trigger
MultiTrigger
Monitors multiple properties and activates only when all the monitored property values match their corresponding Value properties.
Data trigger
DataTrigger
Monitors a bound property and activates when the value of the bound property matches the Value property.
Lesson 1: Styles
Table 7-2
313
Types of Trigger Objects
Type
Class Name
Description
Multi-data-trigger
MultDataTrigger
Monitors multiple bound properties and activates only when all the monitored bound properties match their corresponding Value properties.
Event trigger
EventTrigger
Initiates a series of Actions when a specified event is raised.
A Trigger is active only when it is part of a Style.Triggers collection—with one exception. EventTrigger objects can be created within a Control.Triggers collection outside a Style. The Control.Triggers collection can accommodate only EventTriggers, and any other Trigger placed in this collection causes an error. EventTriggers are primarily used with animation and are discussed further in Lesson 2 of this chapter, “Animations.”
Property Triggers The most commonly used type of Trigger is the property trigger. The property trigger monitors the value of a property specified by the Property property. When the value of the specified property equals the Value property, the Trigger is activated. Important properties of property triggers are shown in Table 7-3. Table 7-3
Important Properties of Property Triggers
Property
Description
EnterActions
Contains a collection of Action objects that are applied when the Trigger becomes active. Actions are discussed in greater detail in Lesson 2 of this chapter.
ExitActions
Contains a collection of Action objects that are applied when the Trigger becomes inactive. Actions are discussed in greater detail in Lesson 2 of this chapter.
Property
Indicates the property that is monitored for changes.
Setters
Contains a collection of Setter objects that are applied when the Trigger becomes active.
Value
Indicates the value that is compared to the property referenced by the Property property.
314
Chapter 7
Styles and Animation
Triggers listen to the property indicated by the Property property and compare that property to the Value property. When the referenced property and the Value property are equal, the Trigger is activated. Any Setter objects in the Setters collection of the Trigger are applied to the style, and any Actions in the EnterActions collections are initiated. When the referenced property no longer matches the Value property, the Trigger is inactivated. All Setter objects in the Setters collection of the Trigger are inactivated, and any Actions in the ExitActions collection are initiated. Actions are used primarily in animations, and they are discussed in greater detail in Lesson 2 of this chapter. NOTE
The following example demonstrates a simple Trigger object that changes the FontWeight of a Button element to Bold when the mouse enters the Button:
In this example, the Trigger defines one Setter in its Setters collection. When the Trigger is activated, that Setter is applied.
Multi-triggers Multi-triggers are similar to property triggers in that they monitor the value of properties and activate when those properties meet a specified value. The difference is that multi-triggers are capable of monitoring several properties at a single time and they activate only when all monitored properties equal their corresponding Value properties. The properties that are monitored and their corresponding Value properties are defined by a collection of Condition objects. The following example demonstrates a MultiTrigger that sets the Button.FontWeight property to Bold only when the Button is focused and the mouse has entered the control:
Lesson 1: Styles
315
Data Triggers and Multi-data-triggers Data triggers are similar to property triggers in that they monitor a property and activate when the property meets a specified value, but they differ in that the property they monitor is a bound property. Instead of a Property property, data triggers expose a Binding property that indicates the bound property to listen to. The following shows a data trigger that changes the Background property of a Label to Red when the bound property CustomerName equals “Fabrikam”:
Multi-data-triggers are to data triggers as multi-triggers are to property triggers. They contain a collection of Condition objects, each of which specifies a bound property via its Binding property and a value to compare to that bound property. When all the conditions are satisfied, the MultiDataTrigger activates. The following example demonstrates a MultiDataTrigger that sets the Label.Background property to Red when CustomerName equals “Fabrikam” and OrderSize equals 500:
Event Triggers Event triggers are different from the other Trigger types. While other Trigger types monitor the value of a property and compare it to an indicated value, event triggers specify an event and activate when that event is raised. In addition, event triggers do not have a Setters collection—rather, they have an Actions collection. Although you have been exposed briefly to the SoundPlayerAction in Chapter 4, “Adding and Managing Content,” most actions deal with animations, which are discussed in detail in Lesson 2 of this chapter. The following two examples demonstrate the EventTrigger
316
Chapter 7
Styles and Animation
class. The first example uses a SoundPlayerAction to play a sound when a Button is clicked:
The second example demonstrates a simple animation that causes the Button to grow in height by 200 units when clicked:
Understanding Property Value Precedence By now, you have probably noticed that a property can be set in many different ways. They can be set in code; they can be set by styles; they can have default values; and so on. It might seem logical at first to believe that a property will have the value it was last set to, but this is actually incorrect. There is a defined and strict order of precedence that determines a property’s value based on how it was set, not when. The precedence order is summarized here, with highest precedence listed first: 1. Set by coercion by the property system. 2. Set by active animations or held animations. 3. Set locally, either by code, by direct setting in XAML, or through data binding. 4. Set by the TemplatedParent. Within this category, there is a sub-order of precedence, again listed in descending order: a. Set by Triggers from the templated parent b. Set by the templated parent through property sets 5. Implicit style—this applies only to the Style property. 6. Set by Style triggers. 7. Set by Template triggers.
Lesson 1: Styles
317
8. Set by Style setters. 9. Set by the default Style. There is a sub-order within this category, again listed in descending order: a. Set by Triggers in the default style b. Set by Setters in the default style 10. Set by inheritance. 11. Set by metadata. Exam Tip
The order of property precedence seems complicated, but actually it is fairly logical. Be sure that you understand the concept behind the property order in addition to knowing the order itself.
This may seem like a complicated and arbitrary order of precedence, but upon closer examination it is actually very logical and based upon the needs of the application and the user. The highest precedence is property coercion. This takes place in some elements if an attempt is made to set a property beyond its allowed values. For example, if an attempt is made to set the Value property of a Slider control to a value higher than the Maximum property, the Value is coerced to equal the Maximum property. Next in precedence come animations. For animations to have any meaningful use, they must be able to override preset property values. The next highest level of precedence is properties that have been set explicitly through developer or user action. Properties set by the TemplatedParent are next in the order of precedence. These are properties set on objects that come into being through a template. Templates are discussed further in Chapter 8, “Customizing the User Interface.” After this comes a special precedence item that applies only to the Style property of an element: Provided that the Style property has not been set by any item with a higher-level precedence, it is set to a Style whose TargetType property matches the type of the element in question. Then come properties set by Triggers—first those set by a Style, then those set by a Template. This is logical because for triggers to have any meaningful effect, they must override properties set by styles. Properties set by styles come next: first properties set by user-defined styles, and then properties set by the default style (also called the Theme, which typically is set by the operating system). Finally come properties that are set through inheritance and the application of metadata.
318
Chapter 7
Styles and Animation
For developers, there are a few important implications that are not intuitively obvious. The most important is that if you set a property explicitly—whether in XAML or in code—the explicitly set property blocks any changes dictated by a Style or Trigger. WPF assumes that you want that property value to be there for a reason and does not allow it to be set by a Style or Trigger, although it still can be overridden by an active animation. A second, less obvious implication is that when using the Visual Studio designer to drag and drop items onto the design surface from the ToolBox, the designer explicitly sets several properties, especially layout properties. These property settings have the same precedence as they would if you had set them yourself. So if you are designing a style-oriented user interface, you should either enter XAML code directly in XAML view to create controls and set as few properties explicitly as possible, or you should review the XAML that Visual Studio generates and delete settings as appropriate. You can clear a property value that has been set in XAML or code manually by calling the DependencyObject.ClearValue method. The following code example demonstrates how to clear the value of the Width property on a button named Button1: ' VB Button1.ClearValue(WidthProperty) // C# Button1.ClearValue(WidthProperty);
Once the value has been cleared, it can be reset automatically by the property system.
Lab: Creating High-Contrast Styles In this lab, you create a rudimentary high-contrast Style for Button, TextBox, and Label elements.
Exercise 1: Using Styles to Create High-Contrast Elements 1. Create a new WPF application in Visual Studio. 2. In XAML view, just above the declaration, create a Window.Resources section, as shown here:
Lesson 1: Styles
319
3. In the Window.Resources section, create a high-contrast Style for TextBox controls that sets the background color to Black and the foreground to White. The TextBox controls also should be slightly larger by default. An example is shown here:
4. Create similar styles for Button and Label, as shown here:
5. Type the following in XAML view. Note that you should not add controls from the toolbox because that automatically sets some properties in the designer at a higher property precedence than styles: High-Contrast Label High-Contrast TextBox High-Contrast Button
6. Press F5 to build and run your application. Note that while the behavior of these controls is unaltered, their appearance has changed.
Exercise 2: Using Triggers to Enhance Visibility 1. In XAML view for the solution you completed in Exercise 1, add a Style.Triggers section to the TextBox Style, as shown here:
320
Chapter 7
Styles and Animation
2. In the Style.Triggers section, add Triggers that detect when the mouse is over the control and enlarge the FontSize of the control, as shown here:
3. Add similar Style.Triggers collections to your other two styles. 4. Press F5 to build and run your application. The FontSize of a control now increases when you move the mouse over it.
Lesson Summary Q
Styles allow you to define consistent visual styles for your application. Styles use a collection of Setters to apply style changes. The most commonly used Setter type is the property setter, which allows you to set a property. Event setters allow you to hook up event handlers as part of an applied style.
Q
Styles can be set inline, but more frequently, they are defined in a Resources collection and are set by referring to the resource. You can apply a style to all instances of a control by setting the TargetType property to the appropriate type.
Q
Styles are most commonly applied declaratively, but they can be applied in code by creating a new style dynamically or obtaining a reference to a preexisting Style resource.
Q
You can create styles that inherit from other styles by using the BasedOn property.
Q
Property triggers monitor the value of a dependency property and can apply Setters from their Setters collection when the monitored property equals a predetermined value. Multi-triggers monitor multiple properties and apply their Setters when all monitored properties match corresponding specified values. Data triggers and multi-data-triggers are analogous but monitor bound values instead of dependency properties.
Q
Event triggers perform a set of Actions when a particular event is raised. They are used most commonly to control Animations.
Q
Property values follow a strict order of precedence depending on how they are set.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 1, “Styles.” The questions are also available on the companion CD if you prefer to review them in electronic form.
Lesson 1: Styles
NOTE
321
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Look at the following XAML snippet: Button
Assuming that the developer hasn’t set any properties any other way, what is the Background color of Button1? A. Blue B. Red C. LimeGreen D. System Default 2. Look at the following XAML snippet:
322
Chapter 7
Styles and Animation
When will TextBox1 appear with a red background? A. When the mouse is over TextBox1 B. When TextBox1 is focused C. When TextBox1 is focused and the mouse is over TextBox1 D. All of the above E. Never 3. Look at the following XAML snippet: Button
What does Button1 display when the mouse is NOT over the Button? A. Hello B. World C. Button D. How are you?
Lesson 2: Animations
323
Lesson 2: Animations Animations are another new feature of WPF. Animations allow you to change the value of a property over the course of a set period of time. Using this technique, you can create a variety of visual effects, including causing controls to grow or move abut the user interface, to change color gradually, or to change other properties over time. In this lesson, you learn how to create animations that animate a variety of property types and use Storyboard objects to control the playback of those animations. After this lesson, you will be able to: Q
Create and use animations
Q
Control animations with the Storyboard class
Q
Control timelines and playback of animations
Q
Implement simultaneous animations
Q
Use Actions to control animation playback
Q
Implement animations that use key frames
Q
Create and start animations in code
Estimated lesson time: 30 minutes
Using Animations The term animation brings to mind hand-drawn anthropomorphic animals performing amusing antics in video media, but in WPF, animation has a far simpler meaning. Generally speaking, an animation in WPF refers to an automated property change over a set period of time. You can animate an element’s size, location, color, or virtually any other property or properties associated with an element. You can use the Animation classes to implement these changes. The Animation classes are a large group of classes designed to implement these automated property changes. There are 42 Animation classes in the System .Windows.Media.Animation namespace, and each one has a specific data type that they are designed to animate. Animation classes fall into three basic groups: Linear animations, key frame–based animations, and path-based animations. Linear animations, which automate a property change in a linear way, are named in the format Animation, where is the name of the type being animated. DoubleAnimation is an example of a linear animation class, and that is the animation class you are likely to use the most.
324
Chapter 7
Styles and Animation
Key frame–based animations perform their animation on the basis of several waypoints, called key frames. The flow of a key-frame animation starts at the beginning, and then progresses to each of the key frames before ending. The progression is usually linear. Key-frame animations are named in the format AnimationUsingKeyFrames, where is the name of the Type being animated. An example is StringAnimationUsingKeyFrames. Path-based animations use a Path object to guide the animation. They are used most often to animate properties that relate to the movement of visual objects along a complex course. Path-based animations are named in the format AnimationUsingPath, where is the name of the type being animated. There are currently only three path-based Animation classes—PointAnimationUsingPath, DoubleAnimationUsingPath, and MatrixAnimationUsingPath.
Important Properties of Animations Although there are many different Animation classes, they all work in the same fundamental way—they change the value of a designated property over a period of time. As such, they share common properties. Many of these properties also are shared with the Storyboard class, which is used to organize Animation objects, as you will see later in this lesson. Important common properties of the Animation and Storyboard classes are shown in Table 7-4. Table 7-4
Important Properties of the Animation and Storyboard Classes
Property
Description
AccelerationRatio
Gets or sets a value specifying the percentage of the Duration property of the Animation that is spent accelerating the passage of time from zero to its maximum rate.
AutoReverse
Gets or sets a value that indicates whether the Animation plays in reverse after it completes a forward iteration.
BeginTime
Gets or sets the time at which the Animation should begin, relative to the time that the Animation is executed. For example, an Animation with a BeginTime set to 0:0:5 exhibits a 5-second delay before beginning.
DecelerationRatio
Gets or sets a value specifying the percentage of the duration of the Animation spent decelerating the passage of time from its maximum rate to zero.
Lesson 2: Animations
Table 7-4
325
Important Properties of the Animation and Storyboard Classes
Property
Description
Duration
Gets or sets the length of time for which the Animation plays.
FillBehavior
Gets or sets a value that indicates how the Animation behaves after it has completed.
RepeatBehavior
Gets or sets a value that indicates how the Animation repeats.
SpeedRatio
Gets or sets the rate at which the Animation progresses relative to its parent.
In addition, the linear animation classes typically implement a few more important properties, which are described in Table 7-5. Table 7-5
Important Properties of Linear Animation Classes
Property
Description
From
Gets or sets the starting value of the Animation. If omitted, the Animation uses the current property value.
To
Gets or sets the ending value of the Animation.
By
Gets or sets the amount by which to increase the value of the target property over the course of the Animation. If both the To and By properties are set, the value of the By property is ignored.
The following example demonstrates a very simple animation. This animation changes the value of a property that has a Double data type representation from 1 to 200 over the course of 10 seconds:
In this example, the Duration property specifies a duration of 10 seconds for the animation, and the From and To properties indicate a starting value of 1 and an ending value of 200. You might notice that something seems to be missing from this example. What property is this animation animating? The answer is that it is not animating any property— the Animation object carries no intrinsic information about the property that is being animated, but instead it is applied to a property by means of a Storyboard.
326
Chapter 7
Styles and Animation
Storyboard Objects The Storyboard is the object that controls and organizes animations in your user interface. The Storyboard class contains a Children collection, which organizes a collection of Timeline objects, which include Animation objects. When created declaratively in XAML, all Animation objects must be enclosed within a Storyboard object, as shown here:
Using a Storyboard to Control Animations In XAML, Storyboard objects organize your Animation objects. The most important feature of the Storyboard object is that it contains properties that allow you to specify the target element and target property of the child Animation objects, as shown in bold in this example:
This example is now usable. It defines a timeline where over the course of 10 seconds, the Height property of Button1 goes from a value of 1 to a value of 200. The TargetName and TargetProperty properties are attached properties, so instead of defining them in the Storyboard itself, you can define them in the child Animation objects, as shown in bold here:
Because a Storyboard can hold more than one Animation at a time, this configuration allows you to set separate target elements and properties for each animation. Thus, it is more common to use the attached properties.
Simultaneous Animations The Storyboard can contain multiple child Animation objects. When the Storyboard is activated, all child animations are started at the same time and run simultaneously.
Lesson 2: Animations
327
The following example demonstrates two simultaneous Animations that cause both the Height and Width of a Button element to grow over 10 seconds:
Using Animations with Triggers You now have learned most of the story about using Animation objects. The Animation object defines a property change over time, and the Storyboard object contains Animation objects and determines what element and property the Animation objects affect. But there is still one piece that is missing: How do you start and stop an Animation? All declaratively created Animation objects must be housed within a Trigger object. This can be either as a part of a Style, or in the Triggers collection of an Element, which accepts only EventTrigger objects. Trigger objects define collections of Action objects, which control when an Animation is started and stopped. The following example demonstrates an EventTrigger object with an inline Animation:
As you can see in the preceding example, the Storyboard object is enclosed in a BeginStoryboard tag, which itself is enclosed in the EventTrigger.Actions tag. BeginStoryboard is an Action—it indicates that the contained Storyboard should be started. The EventTrigger class defines a collection of Actions that should be initiated when the Trigger is activated, and in this example, BeginStoryboard is the action that is initiated. Thus, when the Button indicated in this trigger is clicked, the described Animation runs.
328
Chapter 7
Styles and Animation
Using Actions to Control Playback There are several Action classes that can be used to manage animation playback. These classes are summarized in Table 7-6. Table 7-6
Animation-Related Action Classes
Action
Description
BeginStoryboard
Begins the child Storyboard object.
PauseStoryboard
Pauses the playback of an indicated Storyboard at the current playback position.
ResumeStoryboard
Resumes playback of an indicated Storyboard.
SeekStoryboard
Fast-forwards to a specified position in a target Storyboard.
SetStoryboardSpeedRatio
Sets the SpeedRatio of the specified Storyboard.
SkipStoryboardToFill
Moves the specified Storyboard to the end of its timeline.
StopStoryboard
Stops playback of the specified Storyboard and returns the animation to the starting position.
PauseStoryboard, ResumeStoryboard, SkipStoryboardToFill, and StopStoryboard are all fairly self-explanatory. They cause the indicated Storyboard to pause, resume, stop, or skip to the end, as indicated by the Action name. The one property that all these Action classes have in common is the BeginStoryboardName property. This property indicates the name of the BeginStoryboard object that the action is to affect. The following example demonstrates a StopStoryboard action that stops the BeginStoryBoard object named stb1:
Lesson 2: Animations
329
All Actions that affect a particular Storyboard object must be defined in the same Triggers collection. The previous example shows both of these triggers being defined in the Button.Triggers collection. If you were to define these triggers in separate Triggers collections, storyboard actions would not function. The SetStoryboardSpeedRatio action sets the speed ratio for the entire Storyboard and all Animation objects in that Storyboard. In addition to BeginStoryboardName, you must set the SpeedRatio property of this Action as well. The following example demonstrates a SetStoryboardSpeedRatio action that speeds the referenced Storyboard by a factor of 2:
The SeekStoryboard action requires two additional properties to be set. The Origin property can be either a value of BeginTime or of Duration and specifies how the Offset property is applied. An Origin value of BeginTime specifies that the Offset is relative to the beginning of the Storyboard. An Origin value of Duration specifies that the Offset is relative to the Duration property of the Storyboard. The Offset property determines the amount of the offset to jump to in the animation. The following example shows a SeekStoryboard action that skips the referenced timeline to 5 seconds ahead from its current point in the timeline.
330
Chapter 7
Styles and Animation
Using Property Triggers with Animations In the examples shown in this section, you have seen Actions being hosted primarily in EventTrigger objects. You can also host Action objects in other kinds of Triggers. Trigger, MultiTrigger, DataTrigger, and MultiDataTrigger objects host two Action collections: EnterActions and ExitActions collections. The EnterActions collection hosts a set of Actions that are executed when the Trigger is activated. Conversely, the ExitActions collection hosts a set of Actions that are executed when the Trigger is deactivated. The following demonstrates a Trigger that begins a Storyboard when activated and stops that Storyboard when deactivated:
Managing the Playback Timeline Both the Animation class and the Storyboard class contain several properties that allow you to manage the playback timeline with a fine level of control. Each of these properties is discussed in this section. When a property is set on an Animation, the setting affects only that animation. Setting a property on a Storyboard, however, affects all Animation objects it contains.
Lesson 2: Animations
331
AccelerationRatio and DecelerationRatio The AccelerationRatio and DecelerationRatio properties allow you to designate a part of the timeline for acceleration and deceleration of the animation speed, rather than starting and playing at a constant speed. This is used sometimes to give an animation a more “natural” appearance. These properties are expressed in fractions of 1 and represent a percentage value of the total timeline. Thus, an AccelerationRatio with a value of .2 indicates that 20 percent of the timeline should be spent accelerating to the top speed. So the AccelerationRatio and DecelerationRatio properties should be equal to or less than 1 when added together. This example shows an Animation with an AccelerationRatio of .2:
AutoReverse As the name implies, the AutoReverse property determines whether the animation automatically plays out in reverse after the end is reached. A value of True indicates that the Animation will play in reverse after the end is reached. False is the default value. The following example demonstrates this property:
FillBehavior The FillBehavior property determines how the Animation behaves after it has completed. A value of HoldEnd indicates that the Animation holds the final value after it has completed, whereas a value of Stop indicates that the Animation stops and returns to the beginning of the timeline when completed. An example is shown here:
The default value for FillBehavior is HoldEnd.
RepeatBehavior The RepeatBehavior property determines if and how an animation repeats. The RepeatBehavior property can be set in three ways. First, it can be set to Forever, which indicates that an Animation repeats for the duration of the application. Second, it can be set to a number followed by the letter x (for example, 2x), which indicates the number of times to repeat the animation. Third, it can be set to a Duration, which indicates the amount of time that an Animation plays, irrespective of the number of iterations. The following three examples demonstrate these settings. The first demonstrates an Animation that
332
Chapter 7
Styles and Animation
repeats forever, the second an Animation that repeats three times, and the third an Animation that repeats for 1 minute:
SpeedRatio The SpeedRatio property allows you to speed up or slow down the base timeline. The SpeedRatio value represents the coefficient for the speed of the Animation. Thus, an Animation with a SpeedRatio value of 0.5 takes twice the standard time to complete, whereas a value of 2 causes the Animation to complete twice as fast. An example is shown here:
Animating Non-Double Types Most of the examples that you have seen in this lesson have dealt with the DoubleAnimation class, but in fact a class exists for every animatable data type. For example, the ColorAnimation class allows you to animate a color change, as shown here:
In this example, when the button is clicked, the background color of the button gradually changes from red to lime green over the course of 5 seconds.
Lesson 2: Animations
333
In the standard Windows theme, this animation may conflict with other animations in the button’s default template, so you might need to mouse out of the button and defocus it to see the full effect. NOTE
Animation with Key Frames Up until now, all the animations you have seen have used linear interpolation—that is, the animated property changes take place over a linear timeline at a linear rate. You also can create nonlinear animations by using key frames. Key frames are waypoints in an animation. Instead of allowing the Animation to progress linearly from beginning to end, key frames divide the animation up into short segments. The animation progresses from the beginning to the first key frame, then the next, and through the KeyFrames collection until the end of the animation is reached. Each key frame defines its own Value and KeyTime properties, which indicate the value that the Animation will represent when it reaches the key frame and the time in the Animation at which that frame will be reached. Every data type that supports a linear Animation type also supports a key-frame Animation type, and some types that do not have linear animation types have key-frame Animation types. The key-frame Animation types are named AnimationUsingKeyFrames, where represents the name of the Type animated by the Animation. Key-frame Animation types do not support the From, To, and By properties; rather, the course of the Animation is defined by the collection of key frames. There are three different kinds of key frames. The first is linear key frames, which are named LinearKeyFrame. These key frames provide points in an Animation that are interpolated between in a linear fashion. The following example demonstrates the use of linear key frames:
In the preceding example, the Height property goes from its starting value to a value of 10 in the first second, then to a value of 100 in the next second, and finally returns to a value of 30 in the last 2 seconds. The progression between each segment is interpolated linearly. In this example, it is similar to having several successive linear Animation objects.
334
Chapter 7
Styles and Animation
Discrete Key Frames Some animatable data types do not support gradual transitions under any circumstances. For example, the String type can only accept discrete changes. You can use discrete key frame objects to make discrete changes in the value of an animated property. Discrete key frame classes are named DiscreteKeyFrame, where is the Type being animated. Like linear key frames, discrete key frames use a Value and a KeyTime property to set the parameters of the key frame. The following example demonstrates an animation of a String using discrete key frames:
Spline Key Frames Spline key frames allow you to define a Bézier curve that expresses the relationship between animation speed and animation time, thus allowing you to create animations that accelerate and decelerate in complex ways. While the mathematics of Bézier curves is beyond the scope of this lesson, a Bézier curve is simply a curve between two points whose shape is influenced by two control points. Using spline key frames, the start and end points of the curve are always (0,0) and (1,1) respectively, so you must define the two control points. The KeySpline property accepts two points to define the Bézier curve, as seen here:
Spline key frames are difficult to create with the intended effect without complex design tools, and are most commonly used when specialized animation design tools are available.
Using Multiple Types of Key Frames in an Animation You can use multiple types of key frames in a single animation—you can freely intermix LinearKeyFrame, DiscreteKeyFrame, and SplineKeyFrame objects in the KeyFrames collection. The only restriction is that all key frames you use must be appropriate to the Type that is being animated. String animations, for example, can use only DiscreteStringKeyFrame objects.
Lesson 2: Animations
335
Quick Check Q
What are the different types of key frame objects? When would you use each one?
Quick Check Answer Q
There are LinearKeyFrame, DiscreteKeyFrame, and SplineKeyFrame objects. LinearKeyFrame objects indicate a linear transition from the preceding property value to the value represented in the key frame. DiscreteKeyFrame objects represent a sudden transition from the preceding property value to the value represented in the key frame. SplineKeyFrame objects represent a transition whose rate is defined by the sum of an associated Bézier curve. You would use each of these types when the kind of transition represented was the kind of transition that you wanted to incorporate into your user interface. In addition, some animation types can use only DiscreteKeyFrames.
Creating and Starting Animations in Code All the Animation objects that you have seen so far in this lesson were created declaratively in XAML. However, you can create and execute Animation objects just as easily in code as well. The process of creating an Animation should seem familiar to you; as with other .NET objects, you create a new instance of your Animation and set the relevant properties, as seen in this example: ' VB Dim aAnimation As New System.Windows.Media.Animation.DoubleAnimation() aAnimation.From = 20 aAnimation.To = 300 aAnimation.Duration = New Duration(New TimeSpan(0, 0, 5)) aAnimation.FillBehavior = Animation.FillBehavior.Stop // C# System.Windows.Media.Animation.DoubleAnimation aAnimation = new System.Windows.Media.Animation.DoubleAnimation(); aAnimation.From = 20; aAnimation.To = 300; aAnimation.Duration = new Duration(new TimeSpan(0, 0, 5)); aAnimation.FillBehavior = Animation.FillBehavior.Stop;
After the Animation has been created, however, the obvious question is: How do you start it? When creating Animation objects declaratively, you must use a Storyboard to
336
Chapter 7
Styles and Animation
organize your Animation and an Action to start it. In code, however, you can use a simple method call. All WPF controls expose a method called BeginAnimation, which allows you to specify a dependency property on that control and an Animation object to act on that dependency property. The following code shows an example: ' VB Button1.BeginAnimation(Button.HeightProperty, aAnimation) // C# button1.BeginAnimation(Button.HeightProperty, aAnimation);
Lab: Improving Readability with Animations In this lab, you improve upon your solution to the lab in Lesson 1 of this chapter. You remove the triggers that cause the FontSize to expand and instead use an Animation to make it look more natural. In addition, you create Animation objects to increase the size of the control when the mouse is over it.
Exercise: Animating High-Contrast Styles 1. Open the completed solution from the lab from Lesson 1 of this chapter. 2. In each of the Styles, remove the FontSize Setter that is defined in the Trigger and replace it with a Trigger.EnterActions and Trigger.ExitActions section, as shown here:
3. In each Trigger.EnterActions section, add a BeginStoryboard action, as shown here:
4. Add the following Storyboard and Animation objects to the BeginStoryboard object in the style for the TextBox. Note that the values for the ThicknessAnimation object are crafted specifically for the completed version of the Lesson 1 lab on the CD. If you created your own solution, you need to recalculate these values:
Lesson 2: Animations
337
5. Add a similar Storyboard to the style for the Label, as shown here:
6. Add a similar Storyboard to the style for the Button, as shown here:
7. Add the following line to the Trigger.ExitActions section of each Style:
8. Press F5 to build and run your application. Now the FontSize expansion is animated and the control expands as well.
Lesson Summary Q
Animation objects drive automated property changes over time. There are three different types of Animation objects—linear animations, key frame–based animations, and path-based animations. Every animatable type has at least one Animation type associated with it, and some types have more than one type of Animation that can be applied.
Q
Storyboard objects organize one or more Animation objects. Storyboard objects determine what objects and properties their contained Animation objects are applied to.
Q
Both Animation and Storyboard objects contain a variety of properties that control Animation playback behavior.
Q
Storyboard objects that are created declaratively are activated by a BeginStoryboard action in the Actions collection of a Trigger. Triggers also can define actions that pause, stop, and resume Storyboard objects, as well as performing other Storyboard-related functions.
338
Chapter 7
Styles and Animation
Q
Key frame animations define a series of waypoints through which the Animation passes. There are three kinds of key frames: linear key frames, discrete key frames, and spline key frames. Some animatable types, such as String, support only discrete key frames.
Q
You can create and apply Animation objects in code. When doing this, you do not need to define a Storyboard object; rather, you call the BeginAnimation method on the element with which you want to associate the Animation.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 2, “Animations.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. How many times does the Animation shown here repeat (not counting the first iteration)?
A. 0 B. 1 C. 2 D. 3 2. Look at this Animation:
Assuming that the element whose Height property it animates begins with a Height of 50, what is the value of the element after the animation has completed? A. 50 B. 110 C. 130 D. 200
Chapter 7 Review
339
Chapter Review To practice and reinforce the skills you learned in this chapter further, you can do any or all of the following: Q
Review the chapter summary.
Q
Review the list of key terms introduced in this chapter.
Q
Complete the case scenarios. These scenarios set up real-world situations involving the topics of this chapter and ask you to create a solution.
Q
Complete the suggested practices.
Q
Take a practice test.
Chapter Summary Q
Styles allow you to define consistent visual styles for your application by using a collection of Setters. They usually are defined as a Resource and referenced in XAML, though they can be set inline or dynamically. Styles can be inherited from other styles and applied to all instances of a particular type.
Q
Triggers respond to changes in the application environment. Property triggers and multi-triggers listen for changes in property values, and data triggers and multi-data-triggers listen for changes in bound values. When one of these triggers is activated, its Setters collection is applied. EventTriggers listen for a routed event and execute Actions in response to that event.
Q
Property values follow a strict order of precedence depending on how they are set.
Q
Animation objects drive automated property changes over time. There are three different types of Animation objects—linear animations, key frame–based animations, and path-based animations. Every animatable type has one or more Animation classes that can be used with it. Animations are organized by Storyboard objects, which are themselves controlled by Action objects that are activated in the Action collections of Trigger objects.
Q
Animations that use key frames provide waypoints that the Animation visits as it progresses. Key frames can be linear, spline-based, or discrete.
Q
You can create and apply Animation objects in code. When doing this, you do not need to define a Storyboard object, but rather you call the BeginAnimation method on the element with which you want to associate the Animation.
340
Chapter 7 Review
Key Terms Q
Action
Q
Animation
Q
Key Frame
Q
Setter
Q
Storyboard
Q
Style
Q
Trigger
Case Scenarios In the following case scenarios, you apply what you’ve learned about how to use controls to design user interfaces. You can find answers to these questions in the “Answers” section at the end of this book.
Case Scenario 1: Cup Fever You’ve had a little free time around the office, and you decided to write a simple but snazzy application to organize and display results from World Cup soccer matches. The technical details are all complete: You’ve located a Web service that feeds up-todate scores, and you’ve created a database that automatically applies updates from this service for match results and keeps track of upcoming matches. The database is exposed through a custom data object built on ObservableCollection lists. All that remains are the finishing touches. Specifically, when users choose an upcoming match from a drop-down box at the top of the window, you want the window’s color scheme to match the colors of the teams in the selected matchup.
Technical Requirements Q
The user interface is divided into two sections, each of which is built on a Grid container. Each section represents a team in the current or upcoming match. The user interface for each section must apply the appropriate team colors automatically when a new match is chosen.
Chapter 7 Review
341
Question Answer the following question for all your office mates, who are eagerly awaiting the application’s completion. Q
How can you implement these color changes to the user interface?
Case Scenario 2: A Far-Out User Interface Our friends with the questionable taste are back. They were so impressed with the work you did for them back in Chapter 4 that they’ve asked you to design a user interface that further pushes the envelope of good design sensibilities. Rather than having a static tie-dyed appearance, now they want the background to be a constantly changing multicolored experience. The idea of using a RadialGradientBrush to paint the background of the window is still acceptable, but they want the center of the gradient to change over time and they want the colors of the background to change.
Question Answer the following question for your manager: Q
How can we implement this appearance?
Suggested Practices Q
Create an Animation that moves elements across the user interface. Alternatively, use linear animations and key frame animations to explore a variety of different animation styles. Animate other properties of UI elements as well, such as the color, size, and content.
Q
Use Animations to create a slideshow application that reads all the image files in a given directory and displays each image for 10 seconds before automatically switching to the next one. Note that you have to create and apply the Animation in code.
Q
Modify the solution from Lesson 2 of Chapter 6, “Converting and Validating Data,” to create styles for the application that includes DataTriggers that automatically apply styles based on the CompanyName of the selected record.
Q
Modify the solution from the second lab in this chapter to reverse the Animation instead of stopping it when the mouse exits the control.
342
Chapter 7 Review
Take a Practice Test The practice tests on this book’s companion CD offer many options. For example, you can test yourself on just the content covered in this chapter, or you can test yourself on all the 70-502 certification exam content. You can set up the test so that it closely simulates the experience of taking a certification exam, or you can set it up in study mode so that you can look at the correct answers and explanations after you answer each question. MORE INFO
Practice tests
For details about all the practice test options available, see the section “How to Use the Practice Tests,” in this book’s Introduction.
Chapter 8
Customizing the User Interface Windows Presentation Foundation (WPF) is an exceedingly versatile programming foundation. The visual extensibility of elements through Styles, Triggers, and Animations provides an extremely rich programming environment. In this chapter, you learn how to extend that richness further by creating custom elements. You learn to incorporate preexisting Windows Forms controls into your WPF applications. You also learn to extend the visual appearance of standard WPF elements by providing new control templates that alter their visual appearance and behavior while retaining their inherent functionality. Finally, you learn to create custom WPF controls completely from scratch.
Exam objectives in this chapter: Q
Integrate Windows Forms controls into a WPF application.
Q
Change the appearance of a control by using templates.
Q
Create user and custom controls.
Lessons in this chapter: Q
Lesson 1: Integrating Windows Forms Controls . . . . . . . . . . . . . . . . . . . . . . . . 345
Q
Lesson 2: Using Control Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
Q
Lesson 3: Creating Custom and User Controls . . . . . . . . . . . . . . . . . . . . . . . . . 372
Before You Begin To complete the lessons in this chapter, you must have Q
A computer that meets or exceeds the minimum hardware requirements listed in the “About This Book” section at the beginning of the book
Q
Microsoft Visual Studio 2008 Professional Edition installed on your computer
Q
An understanding of Microsoft Visual Basic or C# syntax and familiarity with Microsoft .NET Framework version 3.5
Q
An understanding of Extensible Application Markup Language (XAML) 343
344
Chapter 8
Customizing the User Interface
Real World Matthew Stoecker I find that WPF provides most of the controls that I need to create my applications. But there are a few notable exceptions, particularly the file dialog boxes and some of the more specialized Windows Forms controls. Fortunately, it is easy to integrate that functionality into my applications, so I don’t have to spend hours reinventing Windows Forms controls or creating complex workarounds.
Lesson 1: Integrating Windows Forms Controls
345
Lesson 1: Integrating Windows Forms Controls The WPF suite of controls is very full, and together with WPF’s control customization abilities, you can create a very wide array of control functionality for your applications. Some types of functionality, however, are absent from the WPF elements and can be difficult to implement on your own. Fortunately, WPF provides a method for using Windows Forms controls in your application. In this lesson, you learn to use Windows Forms controls and dialog boxes in your WPF applications. After this lesson, you will be able to: Q
Describe how to use a Windows Forms control in a WPF application
Q
Integrate Windows Forms dialog boxes into WPF applications
Q
Use the MaskedTextBox control in a WPF application
Q
Use the PropertyGrid control in a WPF application
Estimated lesson time: 30 minutes
Using Windows Forms Controls While WPF provides a wide variety of useful controls and features, you might find that some familiar functionality that you used in Windows Forms programming is not available. Notably absent are controls such as MaskedTextBox and PropertyGrid, as well as simple dialog boxes. Fortunately, you still can use many Windows Forms controls in your WPF applications.
Using Dialog Boxes in WPF Applications Dialog boxes are one of the most notable things missing from the WPF menagerie of controls and elements. Because dialog boxes are separate user interfaces, however, they are relatively easy to incorporate into your WPF applications.
File Dialog Boxes The file dialog boxes, OpenFileDialog and SaveFileDialog, are components that you want to use frequently in your applications. They allow you to browse the file system and return the path to the selected file. The OpenFileDialog and SaveFileDialog classes are very similar and share most important members. Important properties of the file dialog boxes are shown in Table 8-1, and important methods are shown in Table 8-2.
346
Chapter 8
Customizing the User Interface
Table 8-1
Important Properties of the File Dialog Boxes
Property
Description
AddExtension
Gets or sets a value indicating whether the dialog box automatically adds an extension to a filename if the user omits the extension.
CheckFileExists
Gets or sets a value indicating whether the dialog box displays a warning if the user specifies a filename that does not exist.
CheckPathExists
Gets or sets a value indicating whether the dialog box displays a warning if the user specifies a path that does not exist.
CreatePrompt
Gets or sets a value indicating whether the dialog box prompts the user for permission to create a file if the user specifies a file that does not exist. Available only in SaveFileDialog.
FileName
Gets or sets a string containing the filename selected in the file dialog box.
FileNames
Gets the filenames of all selected files in the dialog box. Although this member exists for both the SaveFileDialog and the OpenFileDialog classes, it is relevant only to the OpenFileDialog class, as it is only possible to select more than one file in OpenFileDialog.
Filter
Gets or sets the current filename filter string, which determines the choices that appear in the Save As File Type or Files Of Type box in the dialog box.
InitialDirectory
Gets or sets the initial directory displayed by the file dialog box.
Multiselect
Gets or sets a value indicating whether the dialog box allows multiple files to be selected. Available only in OpenFileDialog.
OverwritePrompt
Gets or sets a value indicating whether the Save As dialog box displays a warning if the user specifies a filename that already exists. Available only in SaveFileDialog.
ValidateNames
Gets or sets a value indicating whether the dialog box accepts only valid Win32 filenames.
Lesson 1: Integrating Windows Forms Controls
Table 8-2
347
Important Methods of the File Dialog Boxes
Method
Description
OpenFile
Opens the selected file as a System.IO.Stream object. For OpenFileDialog objects, it opens a read-only stream. For SaveFileDialog objects, it saves a new copy of the indicated file and then opens it as a read-write stream. You need to be careful when using the SaveFileDialog.OpenFile method to keep from overwriting preexisting files of the same name.
ShowDialog
Shows the dialog box modally, thereby halting application execution until the dialog box has been closed. Returns a DialogResult.
To use a file dialog box in a WPF application
1. In Solution Explorer, right-click the project name and choose Add Reference. The Add Reference dialog box opens. 2. On the .NET tab, select System.Windows.Forms and then click OK. 3. In code, create a new instance of the desired file dialog box, as shown here: ' VB Dim aDialog As New System.Windows.Forms.OpenFileDialog() // C# System.Windows.Forms.OpenFileDialog aDialog = new System.Windows.Forms.OpenFileDialog();
4. Use the ShowDialog method to show the dialog box modally. After the dialog box is shown, you can retrieve the filename that was selected from the FileNames property. An example is shown here: ' VB Dim aResult As System.Windows.Forms.DialogResult aResult = aDialog.ShowDialog() If aResult = System.Windows.Forms.DialogResult.OK Then ' Shows the path to the selected file MessageBox.Show(aDialog.FileName) End If // C# System.Windows.Forms.DialogResult aResult; aResult = aDialog.ShowDialog(); if (aResult == System.Windows.Forms.DialogResult.OK) { // Shows the path to the selected file MessageBox.Show(aDialog.FileName); }
348
Chapter 8
Customizing the User Interface
It is not advisable to import the System.Windows.Forms namespace because this leads to naming conflicts with several WPF classes. NOTE
ColorDialog Dialog Box Also notably absent is the ColorDialog dialog box, which allows the user to select a color or to design a custom color. While you might think it would be as simple to use the ColorDialog dialog box as it is to use the file dialog boxes, it is in fact considerably more complex. WPF uses the System.Windows.Media.Color structure, whereas the ColorDialog dialog box returns an instance of System.Drawing.Color. Although these structures appear quite similar and indeed share many characteristics, there is no provided method for converting between the two. Furthermore, although System.Drawing.Color provides a ToArgb method that returns a hex integer that represents the color of that instance, the System.Windows.Media.Color.FromArgb method requires separate values for the A, R, G, and B channels, thus making a simple one-step conversion between the two impossible. However, if you are willing to write a little bit of code, you can create a method that parses the ARGB result returned by the System.Drawing.Color structure and creates a new System.Windows.Media structure. The following example (which requires adding a reference to the System.Drawing namespace to your project) demonstrates one method for converting between the two structures: ' VB Private Sub OpenColorDialog() Dim ColorDialog As New System.Windows.Forms.ColorDialog() Dim aResult As System.Windows.Forms.DialogResult aResult = ColorDialog.ShowDialog() If aResult = System.Windows.Forms.DialogResult.OK Then Dim myColor As Color myColor = ConvertColors(ColorDialog.Color) ' You can now use the System.Windows.Media.Color End If End Sub Private Function ConvertColors(ByVal aColor As System.Drawing.Color) _ As Color Dim bColor As System.Windows.Media.Color Dim i As Integer = aColor.ToArgb Dim a, r, g, b As Byte ' Parses the integer into its component bytes a = CType(i >> 24 And 255, Byte) r = CType(i >> 16 And 255, Byte) g = CType(i >> 8 And 255, Byte) b = CType(i And 255, Byte) bColor = Color.FromArgb(a, r, g, b) Return bColor End Function
Lesson 1: Integrating Windows Forms Controls
349
// C# private void OpenColorDialog() { System.Windows.Forms.ColorDialog myColorDialog = new System.Windows.Forms.ColorDialog(); System.Windows.Forms.DialogResult aResult; aResult = myColorDialog.ShowDialog(); if (aResult == System.Windows.Forms.DialogResult.OK) { Color myColor = ConvertColors(myColorDialog.Color); // You can now use the System.Windows.Media.Color } } private color ConvertColors(System.Drawing.Color aColor) { System.Windows.Media.Color bColor; int i = aColor.ToArgb(); byte a = (byte)((i >> 24) & 255); byte r = (byte)((i >> 16) & 255); byte g = (byte)((i >> 8) & 255); byte b = (byte)(i & 255); bColor = Color.FromArgb(a, r, g, b); return bColor; }
Exam Tip While it is unlikely that this specif ic method of converting colors will be asked for on the certif ication exam, you should keep in mind the kinds of problems caused by interoperability and be prepared to think creatively about how to solve them.
WindowsFormsHost While using dialog boxes in WPF applications is fairly straightforward, using controls is a bit more difficult. Fortunately, WPF provides an element specifically designed to ease this task, which is called WindowsFormsHost. WindowsFormsHost is a WPF element that is capable of hosting a single child element that is a Windows Forms control. The hosted Windows Forms control automatically sizes itself to the size of the WindowsFormsHost. You can use the WindowsFormsHost to create instances of Windows Forms controls declaratively, and you also can set properties on hosted Windows Forms declaratively.
Adding a Windows Forms Control to a WPF Application To use the WindowsFormsHost element in your WPF applications, first you must add to the XAML view a reference to the System.Windows.Forms.Integration namespace in the WindowsFormsIntegration assembly, as shown here. (This line has been formatted to fit on the printed page, but should be on a single line in your XAML.)
350
Chapter 8
Customizing the User Interface
xmlns:my="clr-namespace:System.Windows.Forms.Integration; assembly=WindowsFormsIntegration"
If you drag a WindowsFormsHost from the Toolbox to the designer, this reference is added automatically. You also must add a reference to the System.Windows.Forms namespace, as shown here: xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Then you can create an instance of the desired Windows Forms control as a child element of a WindowsFormsHost element, as shown here:
Setting Properties of Windows Forms Controls in a WPF application You can set properties on a hosted Windows Forms control declaratively in XAML as you would any WPF element, as shown in bold here:
Although you can set properties declaratively on a hosted Windows Forms control, some of those properties will not have any meaning. For example, properties dealing with layout, such as Anchor, Dock, Top, and Left, have no effect on the position of the Windows Forms control. This is because its container is the WindowsFormsHost, and the Windows Forms control occupies the entire interior of that element. To manage layout for a hosted Windows Forms control, you should set the layout properties of the WindowsFormsHost, as highlighted in bold here:
Setting Event Handlers on Windows Forms Controls in a WPF Application Similarly, you can set event handlers declaratively in XAML, as shown in bold in the following example:
Note that events raised by Windows Forms controls are regular .NET events, not routed events, and therefore they must be handled at the source.
Lesson 1: Integrating Windows Forms Controls
351
Obtaining a Reference to a Hosted Windows Forms Control in Code In most cases, using simple declarative syntax with hosted Windows Forms controls is not sufficient—you have to use code to manipulate hosted Windows Forms controls. Although you can set the Name property of a hosted Windows Forms control, that name does not give you a code reference to the control. Instead, you must obtain a reference by using the WindowsFormsHost.Child property and casting it to the correct type. The following code example demonstrates how to obtain a reference to a hosted Windows Forms Button control: ' VB Dim aButton As System.Windows.Forms.Button aButton = CType(windowsFormsHost1.Child , System.Windows.Forms.Button) // C# System.Windows.Forms.Button aButton; aButton = (System.Windows.Forms.Button)windowsFormsHost1.Child;
Using MaskedTextBox in WPF Applications A common scenario when hosting Windows Forms controls in a WPF application is the use of MaskedTextBox. MaskedTextBox is a modified TextBox control that allows you to use a preset pattern for accepting or rejecting user input. The Mask property allows you to specify required or optional characters or specify whether input characters are letters or numbers, and apply formatting for the display of strings. The MaskedTextProvider associated with MaskedTextBox provides the parsing engine that parses the Mask format. The code characters used by the default MaskedTextProvider property are shown in Table 8-3. Table 8-3
Elements of the Default MaskedTextProvider
Element
Description
0
Represents a required digit between 0 and 9.
9
Represents an optional digit between 0 and 9.
#
Represents an optional digit between 0 and 9, or a space. Plus (+) and minus (-) signs also are accepted.
L
Represents a required letter, either uppercase or lowercase (A-Z, a-z).
?
Represents an optional letter, uppercase or lowercase (A-Z, a-z).
&
Represents a required character.
C
Represents an optional character.
352
Chapter 8
Customizing the User Interface
Table 8-3
Elements of the Default MaskedTextProvider
Element
Description
A,a
Represents an optional alphanumeric character.
.
Represents a decimal character. The actual character used is the decimal character set by the control’s FormatProvider.
,
Represents a thousands separator. The actual character used is the thousands separator set by the control’s FormatProvider.
:
Represents a time separator. The actual character used is the time separator character set by the control’s FormatProvider.
/
Represents a date separator. The actual character used will be the date separator character set by the control’s FormatProvider.
$
Represents a currency symbol. The actual character used is the currency symbol character set by the control’s FormatProvider.
<
Converts all the subsequent characters to lowercase (shift down).
>
Converts all the subsequent characters to uppercase (shift up).
|
Disables a previous shift up or shift down.
\
Escapes a mask character, turning it into a literal character. The double backslash (\\) is the escape sequence for a backslash.
All other characters
All other characters appear as themselves in the MaskedTextBox and cannot be moved or deleted by the user.
Depending on the current user interface (UI) culture, different symbols might be used for decimal, thousands, time, and date separators and the currency symbol. You can design the mask for the MaskedTextBox by creating a string made of characters described in Table 8-3. Setting the Mask property of the MaskedEditBox restricts the allowable input to the format determined by the mask string. The following example demonstrates how to set the Mask property in XAML. The mask shown is appropriate for accepting Social Security numbers:
Some examples of mask strings are shown in Table 8-4.
Lesson 1: Integrating Windows Forms Controls
Table 8-4
353
Examples of Mask Strings
String
Input Text
Displayed Text
(999)-000-0000
1234567890
(123)-456-7890
00/00/0000
07141969
07/14/1969 when displayed in U.S. style. The date separator displayed is determined by the control’s FormatProvider.
$99,999.00
1234567
$12,345.67 when displayed in U.S. style. The currency symbol displayed is determined by the control’s FormatProvider.
LL>L|LLL
Lesson 2: Using Control Templates
365
You can use any other type of databinding expression in a template as well. For example, you could create a Binding object that sets the Path in the XAML template (shown here in bold) and then sets the DataContext for the Window in code, as shown here: ' VB Window1.DataContext = Me // C# Window1.DataContext = this;
When this code is applied, all the Button elements that are set to this template in Window1 have the ellipse painted by the same brush as the background of the window.
Applying Templates with a Style You can use a Style to apply templates automatically. By setting the TargetType property of the Style and using a Setter to set the Template property, the specified template is applied automatically to all elements of that type. The following example demonstrates an example of a Style that automatically applies a template to Button elements:
There is one important thing to note about setting a template with a Style. The Style must be defined in XAML after the template is defined.
Viewing the Source Code for an Existing Template Creating a template for a Button is fairly easy—there isn’t a whole lot of nuance to the layout and there aren’t too many different visual states to monitor. However, when designing templates for other controls, you might want to use the default template as
366
Chapter 8
Customizing the User Interface
a reference. You can view the default template for a WPF element easily, as described in the following procedure:
To view the source code for an existing template
1. Instantiate an example of the element whose template you want to view. The element must actually be created in the visual tree, so you can add it to a Window at design time or add it programmatically, as shown here: ' VB Dim aTextBox As New TextBox Grid1.Children.Add(aTextBox) // C# TextBox aTextBox = new TextBox(); Grid1.Children.Add(aTextBox);
2. Use the System.Windows.Markup.XamlWriter class to serialize the template. There are several ways to do this. The following example demonstrates how to save the template to an Extensible Markup Language (XML) file: ' VB Dim aStream As New System.IO.FileStream("C:\template.xml", _ System.IO.FileMode.Append) System.Windows.Markup.XamlWriter.Save(aTextBox.Template, aStream) // C# System.IO.FileStream aStream = new System.IO.FileStream("C:\\template.xml", System.IO.FileMode.Append); System.Windows.Markup.XamlWriter.Save(aTextBox.Template, aStream);
Using Predefined Part Names in a Template While WPF elements are designed to be lookless, this is not always completely the case. When inspecting the template of the TextBox in the previous section, you might have noticed the following line:
Specifically, the part of this line that deserves notice is “Name=“PART_ContentHost”. Despite the goal of lookless controls, some WPF elements interact with their code through named elements in their templates. By convention, all these elements that interact with the element code are named PART_, where is a description of the role of that part. In the TextBox example, the Part_ContentHost interacts with the element code to provide the editable surface of the TextBox. If you are providing a new template for any control with named parts, you should give the corresponding elements the same name to keep the functionality of the control consistent.
Lesson 2: Using Control Templates
367
Lab: Creating a Control Template In this lab, you create a new template for the Button control. You provide a custom appearance for the Button and implement the functionality to change that appearance when the Button is disabled, when the mouse moves over the Button, and when the Button is clicked.
Exercise: Creating a New Control Template 1. Create a new WPF application. 2. In XAML view, create a Window.Resources section just before the Grid section, as shown here:
3. Add the following ControlTemplate to the Window.Resources section. This template defines an Ellipse and a ContentPresenter that make up the visual appearance of the Button. The Ellipse is filled by a custom RadialGradientBrush that gives it a colorful appearance:
4. Add a ControlTemplate.Triggers section and add the following Trigger to highlight the Button when the mouse is over it:
368
Chapter 8
Customizing the User Interface
5. Add another Trigger to disable the Button when the IsEnabled property is False, as shown here:
6. Add an EventTrigger to the Triggers section that plays an Animation when the Button is clicked. The following example causes the Button to shrink to a point and re-expand:
7. In the Window.Resources section, after the end of the ControlTemplate, add the following Style to set the template for Button elements in this application automatically:
8. Add the following Button tags as children to the Grid in the XAML for this window: Button Button
Lesson 2: Using Control Templates
369
Do not set the Margin property because this interferes with the Animation you def ined in Step 6. NOTE
9. Set the IsEnabled property of the second Button to False, as shown here. (Note that the Button immediately disables.) Button
10. Press F5 to run your application. Note that your Button is highlighted when the mouse moves over it and that the Animation runs when the Button is clicked.
Lesson Summary Q
Control templates define the visual interface for a control but do not affect the inherent functionality of a control directly. By setting the Template property of a WPF element, you can provide a new visual interface for that control while leaving its core functionality intact.
Q
Control templates typically are defined as resources and are set on a target element by setting the element’s Template property to reference the appropriate resource. Alternatively, templates can be applied automatically to all elements of a given type by using a Style that has the TargetType property set to the appropriate element type.
Q
Control templates can contain Trigger objects as part of the template. Trigger objects typically are used to provide visual cues to the user when conditions change for the element.
Q
You can use the TemplateBinding markup to bind a property inside a template to a property of the templated parent. This markup does not function with Freezable objects in Triggers; in that case, you should use a regular Binding with the RelativeSource property set to TemplatedParent.
Q
Some templates contain elements that are acted on directly by the source code of the element. These elements are named PART_. Whenever creating a template for an element with named parts, you should keep the same name for corresponding parts of the template to avoid having to re-implement the functionality.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 2, “Using Control Templates.” The questions are also available on the companion CD if you prefer to review them in electronic form.
370
Chapter 8
NOTE
Customizing the User Interface
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following XAML snippets correctly binds the Background property of the Label in the template to the Background property of its templated parent? (Choose all that apply.) A.
B.
C.
D.
2. Which of the following XAML snippets correctly applies the control template to all instances of Label in a Window? A.
B.
Lesson 2: Using Control Templates
C.
D.
371
372
Chapter 8
Customizing the User Interface
Lesson 3: Creating Custom and User Controls Although the array of element possibilities that WPF exposes through control templates is vast, there still are times when the functionality you want to create in your applications is not matched by any preexisting WPF or Windows Forms control. In these cases, you can create custom controls that incorporate the functionality you need. In this lesson, you learn to create a dependency property and to create user and custom controls. After this lesson, you will be able to: Q
Choose among using a control template, a user control, or a custom control
Q
Implement a dependency property
Q
Create a new user control
Q
Create a new custom control
Q
Consume a custom or user control
Q
Render theme-based appearances for your custom controls
Estimated lesson time: 30 minutes
Control Creation in WPF With the enormous amount of customization available for WPF elements, the number of scenarios in which you have to build a control from scratch is fairly small. Nonetheless, occasionally you might want to create your own custom controls. Custom controls in WPF fall into two categories: user controls, which inherit the UserControl class and are made up of constituent controls bound together by a common functionality in a shared user interface; and custom controls, which inherit the Control or ContentControl class and define their own visual appearance and functionality. Because of the template-based mechanism of creating user interfaces, the line between user controls and custom controls is somewhat blurred. The important distinction from a developer’s standpoint is that user controls provide a designable surface at design time and custom controls do not. Whether creating a user control or a custom control, you likely want to provide new properties for your control. To take advantage of built-in databinding and change notification features in WPF, you should implement dependency properties.
Lesson 3: Creating Custom and User Controls
373
Choosing Among User Controls, Custom Controls, and Templates User controls, custom controls, and templates all allow you to create custom elements with custom appearances. Because each of these methods is so powerful, you might be confused as to what technique to use when creating a custom element for your application. The key to making the right decision isn’t based on the appearance that you want to create, but rather the functionality that you want to incorporate into your application. The standard WPF controls provide a great deal of built-in functionality. If the functionality of one of the preset controls, such as a progress bar or a slider, matches the functionality that you want to incorporate, then you should create a new template for that preexisting control to achieve your visual appearance goals. Creating a new template is the lightest-weight solution to creating a custom element, and you should consider that option first. If the functionality you want to incorporate into your application can be achieved through a combination of preexisting controls and code, you should consider creating a user control. User controls allow you to bind together multiple preexisting controls in a single interface and add code that determines how they behave. If no preexisting control or combination of controls can approach the functionality that you want to create, then you should create a custom control. Custom controls allow you to create a completely new template that defines the visual representation of the control and add completely custom code that determines the control’s functionality.
Implementing and Registering Dependency Properties Dependency properties are the standard form that properties in WPF take. They support change notification, animation, property value inheritance, and databinding, and they support multiple property value providers. Dependency properties can be implemented only on objects that derive from the DependencyObject class. All WPF elements derive from DependencyObject—if you want to implement dependency properties on a custom class, the class must inherit from DependencyObject. Dependency properties are implemented as normal .NET properties with some extra WPF infrastructure. The dependency property must be registered with the run time in a static constructor, and then it can be set using a standard .NET property wrapper. The following procedure describes how to implement and register a dependency property.
374
Chapter 8
Customizing the User Interface
To implement and register a dependency property
1. In a class that inherits from DependencyObject, declare a public, static, read-only variable of the type DependencyProperty. By convention, the name of this variable should be your desired name for the property with the suffix “Property” added to it. Look at the following example: ' VB Public Shared ReadOnly FlavorProperty As DependencyProperty // C# public static readonly DependencyProperty FlavorProperty;
2. Create a static constructor for the class that registers the dependency property. The DependencyProperty.Register method requires that you provide the name of the .NET property wrapper, the type of the property, the type that owns that property, and an instance of FrameworkPropertyMetadata, which can be used to add optional features to your dependency property. The following example shows a static constructor that registers the dependency property and assumes that your class is named PieClass: ' VB Shared Sub New() Dim md As New FrameworkPropertyMetadata() PieClass.FlavorProperty = DependencyProperty.Register("Flavor", _ GetType(String), GetType(PieClass), md) End Sub // C# static PieClass() { FrameworkPropertyMetadata md = new FrameworkPropertyMetadata(); PieClass.FlavorProperty = DependencyProperty.Register("Flavor", typeof(string), typeof(PieClass), md); }
3. Finally, create a .NET property wrapper to allow the dependency property to be accessed in code, as shown here: ' VB Public Property Flavor() As String Get Return CType(GetValue(PieClass.FlavorProperty), String) End Get Set SetValue(PieClass.FlavorProperty, value) End Set End Property
Lesson 3: Creating Custom and User Controls
375
// C# public string Flavor { get { return (string)GetValue(PieClass.FlavorProperty); } set { SetValue(PieClass.FlavorProperty, value); } }
It is important to note that when dependency properties are set by the run time, they are set directly through the GetValue and SetValue methods, not through the .NET Property wrapper. Thus, you should not put any additional code in the wrapper because it does not run unless the property is set directly in code. You can provide a static callback method that executes when the property value is changed by specifying a delegate in the FrameworkPropertyMetadata, as shown here: ' VB Shared Sub New() Dim md As New FrameworkPropertyMetadata(New _ PropertyChangedCallback(AddressOf FlavorPropertyChanged)) PieClass.FlavorProperty = DependencyProperty.Register("Flavor", _ GetType(String), GetType(PieClass), md) End Sub Private Shared Sub FlavorPropertyChanged(ByVal o As DependencyObject, _ ByVal e As DependencyPropertyChangedEventArgs) ' Implementation omitted End Sub // C# static PieClass() { FrameworkPropertyMetadata md = new FrameworkPropertyMetadata(new PropertyChangedCallback(FlavorPropertyChanged)); PieClass.FlavorProperty = DependencyProperty.Register("Flavor", typeof(string), typeof(PieClass), md); } private static void FlavorPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { // Implementation omitted }
376
Chapter 8
Customizing the User Interface
Creating User Controls You can create a user control when you want to combine the functionality of multiple preexisting controls with code that makes them work together in a specific way. When creating user controls, you typically are not attempting to create a lookless control, but rather a reusable amalgam of preconfigured controls. Creating a user control is easy. Visual Studio provides a designable surface for user control creation that enables drag-and-drop functionality from the Toolbox. Thus, you can design a user control in exactly the same way as you would design an application interface. Once the interface of the control is created, you can implement the functionality that your control requires. The following procedure describes how to create a user control.
To create a user control
1. In Visual Studio, create a new WPF User Control Library project using the template in the Windows project type. The designer opens to a new designable user control. 2. Create the user interface for your control. You can drag constituent controls from the Toolbox, or you can create them in XAML. 3. Add any functionality required by your control. If you need to implement any properties, you should implement dependency properties, as described earlier in this lesson. For events, you should implement routed events, as described in Chapter 2, “Events, Commands, and Settings.” 4. Press F5 to build your control. Consuming user controls is discussed in the section “Consuming User Controls and Custom Controls,” later in this lesson.
Creating Custom Controls Custom controls are different from user controls in that they are designed to be lookless. Visual Studio provides no designable surface for a custom control; rather, you must create the template for the custom control in the XAML designer. When you create a new custom control project in Visual Studio, it creates a Themes folder that contains a file named Generic.xaml. This file contains the default template for your custom control, and you can alter this file to create the template for your control.
To create a custom control
1. In Visual Studio, create a new WPF Custom Control Library project using the template in the Windows project type. The designer opens to the Code Window.
Lesson 3: Creating Custom and User Controls
377
2. In Solution Explorer, open the Themes folder and double-click Generic.xaml to open the default template for your custom control. 3. In XAML view, create the template for your control. 4. Add any functionality required by your control. If you need to implement any properties, you should implement dependency properties, as described earlier in this lesson. For events, you should implement routed events, as described in Chapter 2. 5. Press F5 to build your control. In the lab for this lesson, you create a custom control from scratch. Custom controls typically inherit the Control class, which provides all the infrastructure that a control needs but contains no control-specific implementation. In some cases, however, you want to inherit another class. For example, if you wanted to create a WPF implementation of the MaskedTextBox, you might start by inheriting the TextBox class. If you want to inherit a class other than Control, you must change the class that your control inherits in the code file manually.
Consuming User Controls and Custom Controls To test or otherwise consume a user control or a custom control, you must add it to another project. This involves creating a reference to the assembly and then instantiating the control.
To consume a user control or a custom control
1. In Solution Explorer, right-click the project and choose Add Reference. The Add Reference dialog box opens. 2. Click the Browse tab and browse to the .dll file that contains the control you want to use. Select it and click OK. 3. In XAML view, add an XAML reference to the newly added assembly, as shown in this example: xmlns:cc="clr-namespace:WpfCustomControlLibrary1; assembly=WpfCustomControlLibrary1"
4. In XAML, add your custom control, as shown in this example:
5. Press F5 to build your application. If you are testing a control, be sure that you reference the Dynamic Link Library (DLL) in the Debug folder. When you make changes to the control and rebuild, the change is detected by your test application and you are prompted to rebuild the application.
378
Chapter 8
Customizing the User Interface
Quick Check Q
What is the difference between a user control and a custom control?
Quick Check Answer Q
A user control is a composite control that is composed of preexisting WPF controls bound together by a common functionality. A custom control is a user-defined control that is composed of a new control template and a new associated functionality.
Rendering a Theme-Based Appearance The Windows Vista operating system allows the user to select themes for the desktop environment. When a theme is selected, applications that render a theme-based appearance automatically change to render a look and feel consistent with that theme. While in some cases you might want your custom controls to have a distinct appearance, rendering a theme-based appearance allows your controls to integrate seamlessly with other user interfaces. The keys to rendering a theme-based appearance are the SystemColors, SystemFonts, and SystemParameters classes. These classes provide user-accessible instances of the brushes, fonts, and miscellaneous parameters that are used by the current theme. When the user changes the theme, the System colors, fonts, and parameters are updated automatically to reflect the new theme. You can access these settings as resources. Because they are system objects, they are already declared and don’t need to be declared as resources in your Resources section. You can bind a property to one of these system objects, as shown in bold in this example:
Because System resources often are changed by the user, you should use the DynamicResource markup instead of the more familiar StaticResource markup. The difference between Static and Dynamic resources is explained in Chapter 9, “Resources, Documents, and Localization. ” Note that you refer to the resource key in this example. If you want to use this object in code, you would omit the key suffix, as shown here: ' VB Label1.Background = SystemColors.ControlBrush
Lesson 3: Creating Custom and User Controls
379
// C# label1.Background = SystemColors.ControlBrush;
Providing Theme-Specific Templates for Custom Controls In some cases, you might want to provide a completely different appearance for a control when it is rendered in one theme as opposed to another. You can create templates that are specific to individual themes and designate them for use with the ThemeInfoAttribute. The template defined in the Generic.xaml file is the template that is used for every theme that does not have a specific template defined. You create templates for specific themes by creating a XAML file named ..xaml, except for the Windows Classic theme, which is named simply Classic.xaml. Table 8-6 shows several examples of Windows themes and their corresponding XAML filenames. Table 8-6
Selected Windows Themes and Matching XAML Filenames
Windows Theme
XAML Filename
Windows Vista theme
Aero.NormalColor.xaml
Default Windows XP theme
Luna.NormalColor.xaml
Silver Windows XP theme
Luna.Metallic.xaml
Windows Classic theme
Classic.xaml
No matter what themes you provide specific support for, you must create a fallback template in the Generic.xaml file. In addition to creating alternate templates in theme-specific files, you must apply the ThemeInfoAttribute. This is an Assembly attribute, and it is applied in the AssemblyInfo.vb or AssemblyInfo.cs file. The ThemeInfoAttribute takes two arguments. The first argument designates where to look for theme-specific templates and resources, and the second argument indicates where to look for generic templates. Possible values for these arguments are as follows: Q
ResourceDictionaryLocation.None: This value indicates not to look for themespecific templates.
Q
ResourceDictionaryLocation.SourceAssembly: This value indicates to look in the source assembly.
380
Chapter 8
Q
Customizing the User Interface
ResourceDictionaryLocation.ExternalAssembly: This value indicates to look in an external assembly. This assembly must be named ..dll, where is the current assembly name and is the name of the appropriate theme.
The following example demonstrates the ThemeInfoAttribute: ' VB // C# [assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)]
Actually, you can use this technique to provide theme-specific resource dictionaries as well. (Resource dictionaries are discussed in Chapter 9.) The only adjustment that you have to make is that you must create a Themes folder if it does not exist, and you must create a Generic.xaml file that includes a resource set that is used for any themes that are unsupported.
Lab: Creating a Custom Control In this lab, you create a custom control that takes the form of a digital clock that is updated every second. You create a dependency property that represents the current time and implement the functionality to update this property every second. You then create the default template for your control and bind a TextBlock in that template to the Time property. Finally, you create a test project to test your new control.
Exercise: Creating a Custom Control 1. Create a new WPF Custom Control Library project using the template in the Windows project type. 2. In code view, create variables that represent a System.Timers.Timer class and a dependency property named TimeProperty. Also, declare a new delegate called SetterDelegate that takes no arguments, as shown here: ' VB Public Shared ReadOnly TimeProperty As DependencyProperty Private myTimer As New System.Timers.Timer Delegate Sub SetterDelegate() // C# public static readonly DependencyProperty TimeProperty; System.Timers.Timer myTimer = new System.Timers.Timer(); delegate void SetterDelegate();
Lesson 3: Creating Custom and User Controls
381
3. In the shared (static) constructor, add the following code to register a new dependency property. No arguments are necessary for the metadata for this property: ' VB Dim metadata As New FrameworkPropertyMetadata() TimeProperty = DependencyProperty.Register("Time", GetType(String), _ GetType(CustomControl1), metadata) // C# FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(); TimeProperty = DependencyProperty.Register("Time", typeof(string), typeof(CustomControl1), metadata);
4. Create an instance constructor that initializes the Timer and sets the DataContext property. In addition, you should add a handler for the Timer.Elapsed event (you will create the method that handles this event in a subsequent step). ' VB Public Sub New() AddHandler myTimer.Elapsed, AddressOf timer_elapsed myTimer.Interval = 1000 myTimer.Start() Me.DataContext = Me End Sub // C# public CustomControl1() { myTimer.Elapsed += timer_elapsed; myTimer.Interval = 1000; myTimer.Start(); this.DataContext = this; }
5. Create a method that sets the Time property to a string representing the current time in the long time format. This method is called by the Dispatcher in the Timer.Elapsed event handler. Note that you are setting the Time property directly through the SetValue method in WPF. Because this property is not meant to be set by other controls, you do not need to provide a .NET property wrapper for this dependency property: ' VB Private Sub TimeSetter() SetValue(TimeProperty, Now.ToLongTimeString) End Sub // C# void TimeSetter() { SetValue(TimeProperty, DateTime.Now.ToLongTimeString()); }
382
Chapter 8
Customizing the User Interface
6. Create the timer_elapsed method to handle the Timer.Elapsed event. Because Timer code is executed on a different thread than the user interface, this method should use the Dispatcher to invoke the TimeSetter method safely. An example is shown: ' VB Private Sub timer_elapsed(ByVal sender As Object, ByVal e As _ System.Timers.ElapsedEventArgs) Dispatcher.Invoke(Windows.Threading.DispatcherPriority.Normal, _ New SetterDelegate(AddressOf TimeSetter)) End Sub // C# void timer_elapsed(object sender, System.Timers.ElapsedEventArgs e) { Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new SetterDelegate(TimeSetter)); }
7. In Solution Explorer, expand the Themes folder and double-click Generic.xaml to open the Generic.xaml file in XAML view. Within the Border declaration, add the following XAML code to create the visual template for your control:
8. Press F6 to build your control. 9. From the File menu, choose Add and then choose New Project. The Add New Project dialog box opens. Choose WPF Application and click OK. 10. In Solution Explorer, right-click the new project and choose Add Reference to open the Add Reference dialog box. Choose the Projects tab and then select the custom control library you created in the previous steps. Click OK. 11. In the namespaces section of your XAML code for the test project, add the following line to import the custom control namespace into your project. Note that if your project and namespace are called something other than Lesson3, you have to modify this line accordingly: xmlns:MyNamespace="clr-namespace:Lesson3;assembly=Lesson3"
12. Add the following XAML within the Grid declaration in the XAML code for your test project:
An instance of your control is created. Because this control has the same run-time and design-time functionality, it is not necessary to run your test application to
Lesson 3: Creating Custom and User Controls
383
test the control. For other controls, however, you would build and run your test application to test the control features fully.
Lesson Summary Q
The choice among templates, custom controls, and user controls should be based on functionality. If an existing WPF control contains the functionality that you need, you should create a template to provide a new visual appearance for it. If multiple existing WPF controls can be bound together with code to create your needed functionality, you should create a user control. If you need completely new functionality, you should create a custom control.
Q
Dependency properties have built-in change notification and support animation, property value inheritance, and databinding. When creating new properties for user controls or custom controls, you should create dependency properties.
Q
User controls are created in a designer and consist of one or more WPF controls bound together in a single interface and incorporating custom code.
Q
Custom controls are WPF elements that have their visual appearance defined by a new template and incorporate custom functionality. They typically inherit from the Control class.
Q
You can provide theme-based rendering for your custom controls by using the SystemColors, SystemFonts, and SystemParameters classes. To provide completely different appearances for your control based on theme, you can create multiple templates and use the ThemeInfoAttribute to designate where to look for the alternate theme templates.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 3, “Creating Custom and User Controls.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
384
Chapter 8
Customizing the User Interface
1. You are designing a control that retrieves current stock prices and displays them in a ListBox that is updated regularly. Which of the following is the best strategy for creating your custom element? A. Use a preexisting Windows Forms control B. Use a preexisting WPF element with a custom template C. Create a user control D. Create a custom control 2. You are designing a control that downloads stock market quotes and displays them in a rolling “stock-ticker” fashion that moves constantly across the top of the window. Which of the following is the best strategy for creating your custom element? A. Use a preexisting Windows Forms control B. Use a preexisting WPF element with a custom template C. Create a user control D. Create a custom control 3. Which of the following is required to implement theme-specific templates for a custom control? (Choose all that apply.) A. Create separate templates for each Theme B. Create separate folders for each Theme C. Set the ThemeInfoAttribute D. Provide a generic template for themes that are not supported.
Chapter 8 Review
385
Chapter Review To practice and reinforce the skills you learned in this chapter further, you can do any or all of the following: Q
Review the chapter summary.
Q
Review the list of key terms introduced in this chapter.
Q
Complete the case scenarios. These scenarios set up real-world situations involving the topics of this chapter and ask you to create a solution.
Q
Complete the suggested practices.
Q
Take a practice test.
Chapter Summary Q
Windows Forms controls and dialog boxes can be imported into WPF applications, either as is or by using the WindowsFormsHost. Commonly used Windows Forms controls for which there is no direct WPF counterpart include MaskedTextBox and PropertyGrid.
Q
Control templates allow you to change the appearance of a WPF element completely while keeping the inherent functionality intact. Templates can use Trigger objects to respond to changes in the condition of the element or to give the user visual cues. Databinding is fully functional in templates and allows you to respect the properties of the template parent. Some preexisting WPF elements contain named parts in their template. The same names should be used for corresponding elements in any templates you create to maintain the intended functionality.
Q
Templates, user controls, and custom controls all allow you to create custom elements, based on the kind of functionality you need from your custom element. Dependency properties are at the core of WPF functionality and should be implemented on your custom elements. You can provide Theme-based rendering for your custom elements by creating multiple template versions.
Key Terms Q
Control Template
Q
Custom Control
386
Chapter 8 Review
Q
Dependency Property
Q
User Control
Case Scenarios In the following case scenarios, you apply what you’ve learned about how to use controls to design user interfaces. You can find answers to these questions in the “Answers” section at the end of this book.
Case Scenario 1: Full Support for Styles You’ve just been hired to create an application for one of your most important clients. There is nothing too technically difficult about this application—it basically just takes customer information as input and stores it in a database. The problem is that the design of the application requires the use of a MaskedTextBox-style control, but the client insists that the application be fully visually extensible like other WPF applications, including support for Styles and Templates.
Technical Requirements Q
The user interface must incorporate several text boxes that have full masking ability.
Q
Every control in the user interface must be visually extensible through Styles and Templates.
Question Answer the following question for all your office mates, who are eagerly awaiting the application’s completion: Q
What possible approaches can we use to solve these problems?
Case Scenario 2: The Pizza Progress Bar You’re designing an internal application for a well-known pizza company, and they’ve requested a few fun little details to personalize the application for their company. Specifically, they’d like a custom progress bar. Instead of showing a bar that gradually fills as the task nears completion, they’d like to show an image of a pizza that gradually disappears as the task nears completion.
Chapter 8 Review
387
Questions Answer the following questions for your manager: 1. What approach should we use to implement this control? 2. How can we implement the disappearing-pizza visual effect?
Suggested Practices Q
Implement the custom ProgressBar control described in the second case scenario.
Q
Re-create the custom control you created in the lab accompanying Lesson 3 as a user control.
Q
Create templates that create Button controls that appear as triangles, pentagons, or other complex geometric shapes.
Q
Practice creating Mask properties for the MaskedTextBox that accept a variety of standard formats, such as Lastname-Firstname, ZIP codes, phone numbers, and other input formats.
Q
Create a custom controller that displays an image and automatically extracts the image name from the metadata and displays it below the image.
Take a Practice Test The practice tests on this book’s companion CD offer many options. For example, you can test yourself on just the content covered in this chapter, or you can test yourself on all the 70-502 certification exam content. You can set up the test so that it closely simulates the experience of taking a certification exam, or you can set it up in study mode so that you can look at the correct answers and explanations after you answer each question. MORE INFO
Practice tests
For details about all the practice test options available, see the section “How to Use the Practice Tests,” in this book’s Introduction.
Chapter 9
Resources, Documents, and Localization A large part of any application is the way that the application presents data to the user. Whether through user interface (UI) strings, documents that provide information, or through other means, the user interface is all about communication with the user. In this chapter, you learn to use Windows Presentation Foundation (WPF) to maximize your communication effectiveness through resources, documents, and localization; to create resources and resource dictionaries that allow you to change the way your user interface is presented and reuse UI elements; to create and print documents; and to localize your applications for international audiences.
Exam objectives in this chapter: Q
Share logical resources throughout an application.
Q
Create and manipulate documents.
Q
Localize a WPF application.
Lessons in this chapter: Q
Lesson 1: Logical Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
Q
Lesson 2: Using Documents in WPF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
Q
Lesson 3: Localizing a WPF Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
Before You Begin To complete the lessons in this chapter, you must have Q
A computer that meets or exceeds the minimum hardware requirements listed in the “About This Book” section at the beginning of the book
Q
Microsoft Visual Studio 2008 Professional Edition installed on your computer
Q
An understanding of Microsoft Visual Basic or C# syntax and familiarity with Microsoft .NET Framework version 3.5
Q
An understanding of Extensible Application Markup Language (XAML) 389
390
Chapter 9
Resources, Documents, and Localization
Real World Matthew Stoecker One thing I really appreciate about WPF applications versus Windows Forms applications is the incredible support for documents and printing. In Windows Forms, printing can feel like a nightmare—it seems to require graduate degrees in math and computer science just to get a document to page correctly. In WPF, printing is easy. The simple, logical approach to document modeling and printing makes the creation of visually interesting and printable applications easy for me, and my users benefit.
Lesson 1: Logical Resources
391
Lesson 1: Logical Resources In previous chapters of this book, you got a fair amount of exposure to resources. Resources allow you to define objects for use in your application and allow you to share objects among elements. In this lesson, you deepen your understanding of logical resources and learn to create resource dictionaries and to access resources in code. You also learn the difference between static and dynamic resources and when to use each. After this lesson, you will be able to: Q
Create a logical resource
Q
Create an application resource
Q
Access a resource in XAML
Q
Explain the difference between a static resource and a dynamic resource
Q
Create a resource dictionary
Q
Merge resource dictionaries
Q
Decide where to store a resource
Q
Access a resource object in code
Estimated lesson time: 30 minutes
Using Logical Resources Logical resources allow you to define objects in XAML that are not part of the visual tree but are available for use by WPF elements in your user interface. Elements in your user interface can access the resource as needed. An example of an object that you might define as a resource is a Brush used to provide a common color scheme for the application. By defining objects that are used by several elements in a Resources section, you gain a few advantages over defining the object each time you use it. First, you gain reusability because you define your object only once rather than multiple times. You also gain flexibility—by separating the objects used by your user interface from the user interface itself, you can refactor parts of the user interface without having to redesign it completely. For example, you might use different collections of resources for different cultures in localization or for different application conditions.
392
Chapter 9
Resources, Documents, and Localization
Logical Resources Any type of object can be defined as a resource. Every WPF element defines a Resources collection, which can be used to define objects that are available to that element and the elements in its visual tree. Although it is most common to define resources in the Resources collection of the Window, you can define a resource in any element’s Resources collection and access it so long as the accessing element is part of the defining element’s visual tree.
Declaring a Logical Resource You declare a logical resource by adding it to a Resources collection, as seen here:
If you don’t intend a resource to be available to the entire Window, you can define it in the Resources collection of an element in the Window, as shown in this example:
The usefulness of this is somewhat limited, and the most common scenario is to define resources in the Window.Resources collection. One point to remember is that when using static resources, you must define the resource in the XAML code before you refer to it. Static and dynamic resources are explained later in this lesson. Every object declared as a Resource must set the x:Key property. This is the name that will be used by other WPF elements to access the resource. There is one exception to this rule: Style objects that set the TargetType property do not need to set the x:Key property explicitly because it is set implicitly behind the scenes. In the previous two examples, the key is set to myBrush. The x:Key property does not have to be unique in the application, but it must be unique in the Resources collection where it is defined. Thus, you could define one resource in the Grid.Resources collection with a key of myBrush and another in the Window.Resources collection with the same key. Objects within the visual tree of the Grid that reference a
Lesson 1: Logical Resources
393
resource with the key myBrush reference the object defined in the Grid.Resources collection, and objects that are not in the visual tree of the Grid but are within the visual tree of the Window reference the object defined in the Window.Resources collection.
Application Resources In addition to defining resources at the level of the element or Window, you can define resources that are accessible by all objects in a particular application. You can create an application resource by opening the App.xaml file (for C# projects) or the Application.xaml file (for Visual Basic projects) and adding the resource to the Application .Resources collection, as shown in bold here:
Accessing a Resource in XAML You can access a resource in XAML by using the following syntax: {StaticResource myBrush}
In this example, the markup declares that a static resource with the key myBrush is accessed. Because this resource is a Brush object, you can plug that markup into any place that expects a Brush object. This example demonstrates how to use a resource in the context of a WPF element:
When a resource is referenced in XAML, the Resources collection of the declaring object first is searched for a resource with a matching key. If one is not found, the Resources collection of that element’s parent is searched, and so on, up to the Window that hosts the element and to the application Resources collection.
Static and Dynamic Resources In addition to the syntax described previously, you can reference a resource with the following syntax: {DynamicResource myBrush}
394
Chapter 9
Resources, Documents, and Localization
The difference between the DynamicResource and StaticResource syntax lies in how the resources are retrieved by the referencing elements. Resources referenced by the StaticResource syntax are retrieved once by the referencing element and used for the lifetime of the resource. Resources referenced with the DynamicResource syntax, on the other hand, are acquired every time the referenced object is used. It might seem intuitive to think that if you use StaticResource syntax, the referencing object does not reflect changes to the underlying resource, but this is not necessarily the case. WPF objects that implement dependency properties automatically incorporate change notification, and changes made to the properties of the resource are picked up by any objects using that resource. Take the following example:
This example renders the Grid in the Window with a Blue background. If the color property of the SolidColorBrush defined in the Window.Resources collection were changed in code to Red, for instance, the background of the Grid would render as Red because change notification would notify all objects using that resource that the property had changed. The difference between static and dynamic resources comes when the underlying object changes. If the Brush defined in the Windows.Resources collection were accessed in code and set to a different object instance, the Grid in the previous example would not detect this change. However, if the Grid used the following markup, the change of the object would be detected and the Grid would render the background with the new Brush:
Accessing resources in code is discussed in the section “Retrieving Resources in Code,” later in this chapter. The downside of using dynamic resources is that they tend to decrease application performance. Because the resources are retrieved every time they are used, dynamic resources can reduce the efficiency of an application. The best practice is to use static resources unless there is a specific reason for using a dynamic resource. Examples of
Lesson 1: Logical Resources
395
when you would want to use a dynamic resource include when you use the SystemBrushes, SystemFonts, and SystemParameters classes as resources (see Lesson 2 of Chapter 8, “Customizing the User Interface,” for more information about these classes), or any other time when you expect the underlying object of the resource to change.
Creating a Resource Dictionary A resource dictionary is a collection of resources that reside in a separate XAML file and can be imported into your application. They can be useful for organizing your resources in a single place or for sharing resources between multiple projects in a single solution. The following procedure describes how to create a new resource dictionary in your application:
To create a resource dictionary
1. From the Project menu, choose Add Resource Dictionary. The Add New Item dialog box opens. Choose the name for the resource dictionary and click Add. The new resource dictionary is opened in XAML view. 2. Add resources to the new resource dictionary in XAML view. You can add resources to the file in XAML view, as shown in bold here:
Merging Resource Dictionaries For objects in your application to access resources in a resource dictionary, you must merge the resource dictionary file with a Resources collection that is accessible in your application, such as the Windows.Resources or Application.Resources collection. You merge resource dictionaries by adding a reference to your resource dictionary file in the ResourceDictionary.MergedDictionaries collection. The following example demonstrates how to merge the resources in a Windows.Resources collection with the resources in resource dictionary files named Dictionary1.xaml and Dictionary2.xaml:
396
Chapter 9
Resources, Documents, and Localization
Note that if you define additional resources in your Resources collection, they must be defined within the bounds of the ResourceDictionary tags.
Choosing Where to Store a Resource You have seen several options regarding where resources should be stored. The factors that should be weighed when deciding where to store a resource include ease of accessibility by referencing elements, readability and maintainability of the code, and reusability. For resources to be accessed by all elements in an application, you should store resources in the Application.Resources collection. The Window.Resources collection makes resources available only to elements in that Window, but that is typically sufficient for most purposes. If you need to share individual resources over multiple projects in a solution, your best choice is to store your resources in a resource dictionary that can be shared among different projects. Readability is important for enabling maintenance of your code by other developers. The best choice for readability is to store resources in the Window.Resources collection because this allows developers to read your code in a single file rather than having to refer to other code files. If making your resources reusable is important, then the ideal method for storing them is to use a resource dictionary. This allows you to reuse resources among different projects and to extract those resources easily for use in other solutions as well.
Retrieving Resources in Code You can access resources in code. The FindResource method allows you to obtain a reference to a resource by using the Key value. To use the FindResource method, you must call it from an element reference that has access to that resource. The following code example demonstrates how to obtain a reference to a resource with a Key value of myBrush through a Button element that has access to that resource: ' VB Dim aBrush As SolidColorBrush aBrush = CType(Button1.FindResource("myBrush"), SolidColorBrush) // C# SolidColorBrush aBrush; aBrush = (SolidColorBrush)Button1.FindResource("myBrush");
The FindResource method throws an exception if the named resource cannot be found. To avoid possible exceptions, you can use the TryFindResource method instead.
Lesson 1: Logical Resources
397
You also can access resources directly through the Resources collection on the element that contains it. The caveat here is that you must know in which collection the resource is defined and use the correct Resources collection. The following example demonstrates how to access a resource with the Key value of myBrush through the Resources collection of the Window: ' VB Dim aBrush As SolidColorBrush aBrush = CType(Me.Resources("myBrush"), SolidColorBrush) // C# SolidColorBrush aBrush; aBrush = (SolidColorBrush)this.Resources["myBrush"];
Note that when used in code, resources are read-write. Thus, you actually can change the object to which a resource refers. This example demonstrates how you can create a new object in code and set an existing resource to it: ' VB Dim aBrush As New SolidColorBrush(Colors.Red) Me.Resources("myBrush") = aBrush // C# SolidColorBrush aBrush = new SolidColorBrush(Colors.Red); this.Resources["myBrush"] = aBrush;
If the object that a resource refers to is changed in code, then objects that use that resource behave differently depending on how the resource is referenced. Those resources referenced with the DynamicResource markup use the new object when the resource is changed in code. Objects that reference resources with the StaticResource markup continue to use the object they initially retrieved from the Resources collection and are unaware of the change.
Lab: Practice with Resources In this lab, you create two resource dictionaries and merge them with the resources in your window.
Exercise: Creating Resource Dictionaries 1. Open the partial solution for this lab. 2. From the Project menu, choose Add Resource Dictionary. Name the file GridResources.xaml and click Add. 3. Add another resource dictionary and name it ButtonResources.xaml.
398
Chapter 9
Resources, Documents, and Localization
4. In Solution Explorer, double-click GridResources.xaml to open the GridResources resource dictionary. Add the following LinearGradientBrush object to the GridResources.xaml file:
5. Double-click ButtonResources.xaml to open the ButtonResources resource dictionary. Add the following resources to this file:
These resources include Brushes for the Background, Foreground, and Border, as well as a Style that automatically applies these Brushes to Button elements. 6. Double-click Window1 to open the designer for the Window. Above the definition for the Grid element, add the following Resources section to the XAML code for the Window:
7. Modify the Grid definition to reference the resource that defines the Brush to be used for the Background of the Grid, as shown here:
8. Press F5 to build and run your application. Note that the Brush objects defined in the resource dictionaries are applied to your Window.
Lesson 1: Logical Resources
399
Lesson Summary Q
Logical resources are objects that are defined in XAML and can be used by elements in your application.
Q
You can define a resource in several locations—in the Resources collection for the Element, in the Resources collection for the Window, in the Resources collection for the Application, or in a resource dictionary. Where you define a resource depends largely on reusability, maintainability of code, and how available the resource object needs to be to the rest of the application.
Q
Static resources retrieve an object from a Resources collection once, whereas dynamic resources retrieve the object each time it is accessed. While changes made to an object are detected by static resources, a change within the actual underlying object is not.
Q
Resource dictionaries are separate XAML files that define resources. Resource dictionaries can be merged with an existing Resources collection for an Element.
Q
Resources can be retrieved in code, either by accessing the Resources collection directly or by using the FindResource or TryFindResource method.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 1, “Logical Resources.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. You have created a series of customized Brush objects that will be used to create a common color scheme for every window in each of several applications in your company. The Brush objects have been implemented as resources. What is the best place to define these resources? A. In the Resources collection of each control that needs them B. In the Resources collection of each Window that needs them C. In the Application.Resources collection D. In a separate resource dictionary
400
Chapter 9
Resources, Documents, and Localization
2. Look at the following XAML: Button
What happens to the colors of the Button when the following code is executed? ' VB Dim aBrush As New SolidColorBrush(Colors.Green) Me.Resources("ForegroundBrush") = aBrush Dim bBrush As SolidColorBrush bBrush = CType(Me.Resources("BackgroundBrush"), SolidColorBrush) bBrush.Color = Colors.Black // C# SolidColorBrush aBrush = new SolidColorBrush(Colors.Green); this.Resources["ForegroundBrush"] = aBrush; SolidColorBrush bBrush; bBrush = (SolidColorBrush)this.Resources["BackgroundBrush"]; bBrush.Color = Colors.Black;
A. Nothing happens. B. The background turns black. C. The foreground turns green. D. Both B and C.
Lesson 2: Using Documents in WPF
401
Lesson 2: Using Documents in WPF Document creation and printing in a Windows Forms application is difficult, tedious, and very complicated. It is, therefore, a very welcome change that WPF introduces a document and printing model that is logical, easy to use, and consistent. In this lesson, you learn to view and print read-only XML Paper Standard (XPS) documents, as well as to create, manipulate, and print flow documents. You also learn to print nondocument visuals. After this lesson, you will be able to: Q
Create a flow document
Q
Use the different flow document elements
Q
Scale text in a flow document
Q
Use flow document containers
Q
View an XPS document
Q
Print a flow document
Q
Print an XPS document
Q
Use the PrintDialog class
Q
Print visual elements
Estimated lesson time: 30 minutes
Flow Documents Flow documents are read-write documents that can be created and displayed in a WPF application. They consist primarily of text, along with figures and other included elements that are automatically laid out in a flow environment. Flow documents consist of two different types of elements: block elements, which define block sections of text; and inline elements, which provide inline formatting or effects for text. These elements are used to provide block and inline formatting for the actual text of the document. Flow documents also must be created in a container. Flow document containers provide automatic layout for a flow document and provide support for zooming and printing.
402
Chapter 9
Resources, Documents, and Localization
Creating Flow Documents WPF provides three flow document containers. You can create a flow document inside any of the flow document containers. The following example shows a flow document without any content in a FlowDocumentScrollViewer:
The FlowDocumentScrollViewer provides basic layout and scrolling support for flow documents. We examine the flow document containers in detail later in this lesson. All content in a flow document must be wrapped within at least one block element. Inline elements can occur within a block element. The following code shows a simple flow document with a single block element, a Paragraph element: A simple flow document
At run time, this is rendered as shown in Figure 9-1.
Figure 9-1 A simple flow document
Lesson 2: Using Documents in WPF
403
When more text is added to a flow document, it is wrapped automatically. In the FlowDocumentScrollViewer, support for scrolling is also included, as seen in Figure 9-2.
Figure 9-2 A flow document with more content
The text used in the figure, as well as some of the following examples, is from the poem Kubla Khan, by Samuel Taylor Coleridge.
Formatting Flow Documents All inline elements and block elements expose a variety of properties that can be used to format the text they wrap. Important properties shared by both inline and block elements are shown in Table 9-1. Table 9-1
Important Properties Shared by Block and Inline Elements
Property
Description
Background
Gets or sets the Brush used to paint the background of the text.
Foreground
Gets or sets the Brush used to paint the foreground of the text.
FontFamily
Gets or sets the font family in which the text is displayed.
FontSize
Gets or sets the font size of the text.
FontStretch
Gets or sets the font stretch of the text, which determines how wide the particular font is rendered.
FontStyle
Gets or sets the font style of the text, such as normal or italic.
404
Chapter 9
Resources, Documents, and Localization
Table 9-1
Important Properties Shared by Block and Inline Elements
Property
Description
FontWeight
Gets or sets the font weight of the text, such as regular or bold.
Style
Gets or sets the Style object that should be used to format this element.
ToolTip
Sets a ToolTip that will appear over the text when the mouse hovers over this element.
Changes made to any of these properties are applied to all text that is enclosed by the element. In addition, there are formatting properties that are exposed only by block elements. These are shown in Table 9-2. Table 9-2
Important Properties of Block Elements
Property
Description
BorderBrush
Gets or sets the Brush that is used to paint the border around a block element.
BorderThickness
Gets or sets the thickness of the border around a block element.
LineHeight
Sets the spacing between the lines of a block element. The value is given in device-independent pixels.
LineStackingStrategy
Determines how lines are spaced when text includes multiple font sizes. There are two potential values for this property: MaxHeight indicates that the line should be as high as the tallest font in the line. BlockLineHeight makes the lines the height of the font of the block element, regardless of other fonts used in the line.
Margin
Sets the spacing between the block element and its container or adjacent elements.
Padding
Sets the spacing between the edges of the block element and any nested content.
TextAlignment
Sets the horizontal alignment of nested text. This property can be Left, Right, Center, or Justify (the default).
Lesson 2: Using Documents in WPF
405
Block Elements As you have seen earlier in this lesson, block elements wrap text inside a flow document. In this section, we examine each of the different block elements. Paragraph
You have seen the Paragraph element earlier in this chapter. This is the basic block element that defines a block of text that is offset from others. The following example demonstrates the use of Paragraph elements in a flow document: In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man Down to a sunless sea. So twice five miles of fertile ground With walls and towers were girdled round: And there were gardens bright with sinuous rills, Where blossomed many an incense-bearing tree; And here were forests ancient as the hills, Enfolding sunny spots of greenery. (The rest omitted)
This example renders as shown in Figure 9-3.
Figure 9-3 Use of Paragraph elements in a flow document
406
Chapter 9
Resources, Documents, and Localization
List
The List element allows you to define a list of items that are offset from the rest of the text in the flow document. The List element contains a collection of ListItem elements, each of which must itself contain another block element, such as a Paragraph. The following example demonstrates the usage of the List element: Poems by Samuel Taylor Coleridge Kubla Khan The Rime of the Ancient Mariner Christabel
Figure 9-4 shows how this example appears at run time.
Figure 9-4 Use of the List element in a flow document
The List element exposes a property called MarkerStyle, which allows you to set the marker that is used to offset list members. Possible values for the MarkerStyle property are shown in Table 9-3.
Lesson 2: Using Documents in WPF
Table 9-3
407
Possible Values for the MarkerStyle Property
Value
Marker
Box
A solid square box is displayed.
Circle
An unfilled circle is displayed.
Decimal
A number that increments automatically is displayed. The numbering starts at 1 by default, but you can choose the starting number by setting the StartingIndex property.
Disc
A filled circle is displayed.
LowerLatin
A lowercase letter that increments automatically is displayed. The lettering starts at a by default, but you can choose the starting letter by setting the StartingIndex property.
LowerRoman
A lowercase Roman numeral that increments automatically is displayed. The numbering starts at i by default, but you can choose the starting numeral by setting the StartingIndex property.
None
No marker is displayed.
UpperLatin
An uppercase letter that increments automatically is displayed. The lettering starts at A by default, but you can choose the starting letter by setting the StartingIndex property.
UpperRoman
An uppercase Roman numeral that increments automatically is displayed. The numbering starts at I by default, but you can choose the starting numeral by setting the StartingIndex property.
Table The Table element allows you to create and populate a series of cells that are laid out in rows and columns. The Table element is analogous to the Hypertext Markup Language (HTML) table. To create a table, you must create a Table element that has a child TableRowGroup. The TableRowGroup in turn contains a collection of TableRow elements, each of which contains a collection of individual TableCell elements, which contain the actual content for the table cell. Each TableCell element must itself contain a block element, such as a Paragraph, that wraps the textual content. The following example demonstrates a Table block element, and the result is shown in Figure 9-5: Number of lines in Coleridge Poems
408
Chapter 9
Resources, Documents, and Localization
Poem Number of Lines Kubla Khan 54 The Rime of the Ancient Mariner 625 Christabel (1816 version) 665
Figure 9-5 Use of the Table element in a flow document
Lesson 2: Using Documents in WPF
409
Section
The Section element does not have any built-in formatting or layout of its own but instead is used to wrap other block elements to provide a common styling. The following example demonstrates the use of the Section element:
In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man Down to a sunless sea.
This example applies the italic font style to the first two paragraphs, as can be seen in Figure 9-6.
Figure 9-6 Use of the Section element in a flow document
BlockUIContainer The BlockUIContainer element allows you to incorporate a UI element into a flow document. While it is limited to a single child element, you can use elements such as Grid or ListBox that themselves can have child elements. This allows you to incorporate application functionality into a flow document. The following example demonstrates the use of the BlockUIContainer element: In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man Click here for information on rafting the sacred river Alph
410
Chapter 9
Resources, Documents, and Localization
Down to a sunless sea.
The result can be seen in Figure 9-7.
Figure 9-7 Use of the BlockUIContainer element in a flow document
Inline Elements Inline elements wrap text inside block elements. They are used primarily for inline formatting, although they also can be used to create hyperlinks, create offset figures, and incorporate inline UI elements. In this section, we briefly examine each kind of inline element. Run
A Run element contains regular text. If no inline element is created explicitly, a Run element is created implicitly. See the following code:
Down to a sunless sea.
The result is identical to this code: Down to a sunless sea.
Bold, Italic, and Underline The Bold, Italic, and Underline inline elements apply formatting to the text that they enclose. Predictably, the Bold element applies a bold font
Lesson 2: Using Documents in WPF
411
style, Italic applies an italic font style, and Underline underlines the enclosed text. The following example demonstrates the use of the Underline element: Down to a sunless sea.
Hyperlink
The Hyperlink element represents a clickable link. In Page-based applications, the Hyperlink can be used to navigate to a different page; and in Window-based applications, the Hyperlink can be used to initiate actions via Commands or event handlers. The following example demonstrates the use of the Hyperlink element:
Down to a sunless sea.
LineBreak The LineBreak element breaks the document flow and starts a new line. Unlike other inline elements, the LineBreak element does not support direct content. It typically is used at the end of a line. It can be useful for presenting text that is in metered verse, as seen in this example: In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man Down to a sunless sea. So twice five miles of fertile ground With walls and towers were girdled round: And there were gardens bright with sinuous rills, Where blossomed many an incense-bearing tree; And here were forests ancient as the hills, Enfolding sunny spots of greenery. (The rest omitted)
The result of this formatting is seen in Figure 9-8.
412
Chapter 9
Resources, Documents, and Localization
Figure 9-8 Use of the LineBreak element in a flow document
Span The Span element generally is used to wrap multiple inline elements so that they have common formatting, much the way the Section element is used to wrap multiple block elements. The following example demonstrates the Span element: In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man
Floater
The Floater element allows you to create a section of the document that is set off from the rest of the text. The Floater is so named because it is placed in the flow document at the position that works best and is in the approximate location specified in the flow document, but it does not allow for precise positioning. Content in a Floater is enclosed in its own box, the size of which is determined by the Floater.Width property, and the height of the content. Floater elements are unusual among inline elements in that they must enclose a block element as a child element. The inline element can be any of the block elements, such as Paragraph, which provides an aside or a highlighted quotation, a Table or List that provides a figure relevant to the text, or a BlockUIElement to add offset UI functionality. Figure 9-9 demonstrates a Floater in a flow document.
Lesson 2: Using Documents in WPF
413
Figure 9-9 Use of the Floater element in a flow document
The code to create the Floater in this example is shown in bold here: In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Though Coleridge referred to this poem as a fragment, many scholars believe it to be the intended complete work. Through caverns measureless to man Down to a sunless sea. So twice five miles of fertile ground With walls and towers were girdled round: And there were gardens bright with sinuous rills, Where blossomed many an incense-bearing tree: And here were forests ancient as the hills, Enfolding sunny spots of greenery.
You can use an enclosed BlockUIElement to host an Image element to provide images with the Floater element. The following Floater tag replaces the Floater tag in the previous example to yield the document shown in Figure 9-10:
414
Chapter 9
Resources, Documents, and Localization
Figure 9-10
Use of the Floater element to host an image in a flow document
Figure
A Figure element is similar to a Floater, but it includes additional properties that allows for more precise positioning. These properties are described in Table 9-4.
Table 9-4
Properties of the Figure Element
Property
Description
Height
Sets the height of the Figure.
HorizontalAnchor
Determines how the Figure is oriented horizontally. It allows you to specify ContentRight, ContentCenter, ContentLeft, PageLeft, PageCenter, PageRight, ColumnLeft, ColumnCenter, or ColumnRight.
HorizontalOffset
Specifies an offset by which to move the Figure horizontally. If this property moves a Figure away from the edge of the container, flow content flows into the space created.
VerticalAnchor
Determines how the Figure is oriented vertically. It allows you to specify ContentTop, ContentCenter, ContentBottom, PageTop, PageCenter, PageBottom, or ParagraphTop.
VerticalOffset
Specifies an offset by which to move the Figure vertically. If this property moves a Figure away from the edge of the container, flow content flows into the space created.
Lesson 2: Using Documents in WPF
Table 9-4
415
Properties of the Figure Element
Property
Description
Width
Specifies the width of the Figure.
WrapDirection
Indicates whether text is able to wrap on both sides of a Figure, one specific side of a Figure, or neither side of a Figure. Possible values are None, Left, Right, or Both.
Unlike the Floater element, the Figure element does not expose the HorizontalAlignment property—instead, that functionality is exposed through the HorizontalAnchor property. Many of the properties of the Figure element are not supported in the FlowDocumentScrollContainer and instead require a container that supports paging, such as FlowDocumentReader. InlineUIContainer
The InlineUIContainer element is used to insert inline UI elements. Elements inserted with InlineUIContainer appear within the text rather than being offset from the text as are elements in a BlockUIContainer. The following example demonstrates the InlineUIContainer:
Down to a sunless sea. Sunless Sea Vacations from Fabrikam
Preserving White Space White space is collapsed by default in XAML, and flow documents are no exception. This applies to multiple spaces, tabs, and line breaks, all of which are converted to a single space. While this is typically the desired behavior, there are times when you will want to retain white space in a flow document. To retain white space, you should apply the xml:space="preserve" attribute to the flow document element that contains the white space you want to preserve, as shown here: This preserved
white
space
is
416
Chapter 9
Resources, Documents, and Localization
Flow Document Containers WPF includes three viewers for flow documents. You saw the FlowDocumentScrollViewer in one of the previous examples. This container lays out flow content according to the width of the viewer but does not support pages or columns. A reader reading a document in a FlowDocumentScrollViewer must scroll to view content that is not currently displayed. The FlowDocumentPageViewer automatically divides a flow document into pages. The size of the page is determined by the size of the FlowDocumentPageViewer; the visible content in the container amounts to a single page. The FlowDocumentPageViewer includes a user interface for navigating pages, as well as a slider that controls the zoom factor of the text. Figure 9-11 shows a document in a FlowDocumentPageViewer.
Figure 9-11
A document in a FlowDocumentPageViewer
The FlowDocumentReader provides the most options to the reader. It allows the reader to switch between page view, scroll view, or columned page view. In addition, the FlowDocumentReader incorporates a search bar that allows the reader to search within the text. Figure 9-12 shows a document in a FlowDocumentReader.
Figure 9-12
A document in a FlowDocumentReader
Lesson 2: Using Documents in WPF
417
Scaling Text in Flow Documents All three document containers support zooming, which allows the user to scale text while viewing. The FlowDocumentReader and FlowDocumentPageViewer automatically show the zoom slider, but the FlowDocumentScrollViewer must have the IsToolBarVisible property explicitly set to True, as shown here:
The zoom slider controls the value of the Zoom property, which in turn determines how the text is scaled. A Zoom value of 100 represents normal-sized content. Higher or lower values increase or decrease the size proportionately. For example, a Zoom value of 300 would enlarge the content by a factor of 3, whereas a Zoom value of 25 would shrink content by a factor of 4. You can set the maximum and minimum Zoom values by setting the MaxZoom and MinZoom properties respectively. You can also set the Zoom property itself to provide a preset Zoom value, as shown in the following example:
Quick Check Q
What are the advantages and disadvantages of the various flow document containers?
Quick Check Answer Q
FlowDocumentScrollViewer is a fairly lightweight document viewer that provides a scrolling experience for flow documents. It provides the simplest user experience and requires the least application resources. FlowDocumentPageViewer requires slightly more application resources than FlowDocumentScrollViewer does, but it provides automatic page layout for the flow document. FlowDocumentReader requires the most application resources of the three flow document containers, but it is also the most functional, allowing the user to switch between a scroll-based and a page-based experience and incorporating search functionality. All three document containers incorporate the functionality for printing the document.
418
Chapter 9
Resources, Documents, and Localization
XPS Documents XPS documents are fixed, read-only documents that are created using the XML Paper Standard (XPS)—a standard based on Extensible Markup Language (XML) that describes typeset, print-ready documents. Few manipulations can be performed on an XPS document other than loading and viewing. While it is possible to create an XPS document programmatically, doing so is a complex process that is beyond the scope of this lesson and this exam. This section focuses on viewing preexisting XPS documents.
Viewing XPS Documents Like flow documents, XPS documents require a specialized viewer. XPS documents use the DocumentViewer class. DocumentViewer supports paging, searching, and zooming XPS documents. You create a DocumentViewer in XAML, as shown here:
You might think that you could specify a document for the DocumentViewer by setting the Document property in XAML, but unfortunately that is not the case. You must load the XPS document in code and set the Document property there. The XpsDocument class is in the System.Windows.Xps.Packaging namespace in the ReachFramework assembly, so before executing this code, you first must add a reference to ReachFramework.dll through the Add Reference dialog box. The following example demonstrates how to load an XPS document into the document viewer: ' VB Dim aDoc As System.Windows.Xps.Packaging.XpsDocument aDoc = New System.Windows.Xps.Packaging.XpsDocument("C:\Chapter9.xps", _ System.IO.FileAccess.Read) Viewer1.Document = aDoc.GetFixedDocumentSequence() // C# System.Windows.Xps.Packaging.XpsDocument aDoc; aDoc = new System.Windows.Xps.Packaging.XpsDocument("C:\\Chapter9.xps", System.IO.FileAccess.Read); Viewer1.Document = aDoc.GetFixedDocumentSequence();
Printing Unlike printing in Windows Forms, printing in WPF is easy. For most documents, printing support is already in place and no additional code needs to be written. For other printing types, printing is facilitated through the methods of the PrintDialog class.
Lesson 2: Using Documents in WPF
419
Printing Documents All four types of document views that you have seen in this lesson (DocumentViewer, FlowDocumentScrollViewer, FlowDocumentPageViewer, and FlowDocumentReader) support printing of their contained documents. The Print method automatically opens the Print dialog box in Microsoft Windows and sends the document to the printer. The following example demonstrates how to print a document contained in any of these viewers: ' VB ' In this example, aViewer could be an instance of any of the document ' viewers covered in this lesson. aViewer.Print // C# // In this example, aViewer could be an instance of any of the document // viewers covered in this lesson. aViewer.Print();
You also can print a document through the ApplicationCommands.Print command. Invoking this command with a document viewer focused automatically opens the Print dialog box, from which the user can choose to send the document to the printer. You can invoke this command from the keyboard by pressing Ctrl+P.
The PrintDialog Class The PrintDialog class provides support for printing documents and visual elements. The PrintDialog class encapsulates the Print dialog box (shown in Figure 9-13), which allows the user to choose a printer and options pertaining to the printer. The PrintDialog class also exposes two methods to facilitate printing: PrintVisual and PrintDocument.
Figure 9-13
The Print dialog box
420
Chapter 9
Resources, Documents, and Localization
The general method for printing with the PrintDialog class is to declare an instance of it and then call the ShowDialog method. This method returns a bool? (in C#) or Boolean? (in Visual Basic), and you should test that the method returned True (indicating that the user clicked OK rather than Cancel) before calling the appropriate method. This scheme will be demonstrated in the next two sections.
Printing Visual Elements The PrintDialog.PrintVisual method can be used to print any element that inherits from the Visual class, which essentially includes all WPF elements. When you print a WPF element, all child elements are printed as well. The PrintVisual method takes two arguments: the Visual element to print and a string description of the item being printed. The following example demonstrates the PrintVisual method: ' VB Dim pd As New PrintDialog() If pd.ShowDialog = True Then pd.PrintVisual(Me,"The Window") End If // C# PrintDialog pd = new PrintDialog(); if (pd.ShowDialog() == true) { pd.PrintVisual(this,"The Window"); }
Printing Flow Documents with PrintDialog Under most circumstances, the easiest way to print a flow document is simply to call the Print method of the viewer that contains the document, or invoke the Print command. You can, however, use the PrintDocument method to manually print a flow document that exists in memory. Rather than requiring an instance of a flow document, the PrintDocument method requires a DocumentPaginator object to print. Although this approach is somewhat roundabout, you can obtain an instance of DocumentPaginator that represents your flow document by first casting the flow document as an IDocumentPaginatorSource and then accessing its DocumentPaginator property. This example demonstrates how to print a flow document instance named myDocument: ' VB Dim pd As New PrintDialog() If pd.ShowDialog = True Then Dim aPag As DocumentPaginator aPag = CType(myDocument, IDocumentPaginatorSource).DocumentPaginator
Lesson 2: Using Documents in WPF
421
pd.PrintDocument(aPag, "My Document") End If // C# PrintDialog pd = new PrintDialog(); if (pd.ShowDialog() == true) { DocumentPaginator aPag; aPag = ((IDocumentPaginatorSource)myDocument).DocumentPaginator; pd.PrintDocument(aPag, "My Document"); }
Lab: Creating a Simple Flow Document In this lab, you create a simple flow document by formatting a brief text document and then view your document in the FlowDocumentReader.
Exercise: Creating a Simple Flow Document 1. Open the partial solution for this lab. 2. In the Grid element definition, in XAML view, add a FlowDocumentReader element, with a child FlowDocument element that itself has a child Paragraph element, as shown here:
3. In Solution Explorer, double-click Rime.txt to open the text document. Copy the text from the text document into the Paragraph tag. Note that although there is white space in the text document, it is not preserved in the Window when pasted into XAML. 4. Add a closing Paragraph tag and another opening Paragraph tag immediately after the first occurrence of the word “Mariner” to close the first Paragraph and start another one, as shown here: Mariner
5. Set the FontSize and FontWeight of the first Paragraph element to 20 and Bold, respectively, as shown here:
422
Chapter 9
Resources, Documents, and Localization
6. Search for the sentence “The great illustrator Gustave Dore engraved a set of illustrations for an edition of this poem.”, and after it, add a Floater element with a width of 150, as shown here:
7. Add a BlockUIContainer element to the Floater that contains an Image element that loads the image Dore.jpg. Also, add a Paragraph element with the FontSize set to 10 that contains a short description of the image. An example is shown here: A representative example of Dore's engraving from the Rime of the Ancient Mariner.
8. After the Floater, add a new Paragraph closing and opening tag, as shown here:
9. Search for the text “including the following” and add a similar pair of closing and opening tags after that text. 10. Set the TextAlignment property of the opening Paragraph element that you just added to Center, as shown here:
11. Add a LineBreak element after each line of the verse, as shown in this example: Water, water, everywhere, And all the boards did shrink; Water, water, everywhere, Nor any drop to drink.
12. Press F5 to build and run your application. Resize your application and change the viewer settings. Note that the layout of the document automatically adjusts to the new conditions. 13. Print your document by pressing CTRL+P.
Lesson Summary Q
Flow documents are documents in XAML that are laid out automatically in the available space.
Q
Flow documents are made of block elements and inline elements. Block elements contain inline elements, which themselves contain text and other content. Both
Lesson 2: Using Documents in WPF
423
block and inline elements share a variety of properties that can be used to format the contained content. Q
There are three built-in flow document containers. They are FlowDocumentScrollViewer, FlowDocumentPageViewer, and FlowDocumentReader. The FlowDocument ScrollViewer provides a scrolling experience for documents, the FlowDocument PageViewer automatically paginates the document, and the FlowDocumentReader allows the viewer to switch between multiple views. Each container provides support for scaling contained content.
Q
XPS documents are fixed, read-only documents that can be viewed with the DocumentViewer element.
Q
All the document viewer classes provide support for printing their contained documents. Support is tied directly to the ApplicationCommands.Print command, and thus the contained document can be printed by invoking the command through its associated keystroke gesture (Ctrl+P).
Q
The PrintDialog class provides support for printing both documents and other content. You can use the PrintVisual method of the PrintDialog class to print any element that inherits the Visual class, and you can use the PrintDocument method to print documents.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 2, “Using Documents in WPF.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following FlowDocument snippets, when placed within a FlowDocumentReader, yields the document seen on the following page in Figure 9-14? A. He paused. "I should think about that", he said. "Yes, you should", she replied.
424
Chapter 9
Resources, Documents, and Localization
B. He paused. "I should think about that", he said. "Yes, you should", she replied.
C. He paused. "I should think about that", he said. "Yes, you should", she replied.
D. He paused. "I should think about that", he said. "Yes, you should", she replied.
Figure 9-14
Sample flow document
2. Which of the following prints a flow document named fd1 hosted in a FlowDocumentReader named fdr1? (Choose all that apply.)
Lesson 2: Using Documents in WPF
A. Pressing Ctrl+P when the document reader is focused B. ' VB ApplicationCommands.Print.Execute(Nothing, fd1) // C# ApplicationCommands.Print.Execute(null, fd1);
C. ' VB fdr1.Print() // C# fdr1.Print();
D. ' VB Dim aDialog As New PrintDialog() aDialog.PrintVisual(fdr1, "my document") // C# PrintDialog aDialog = new PrintDialog(); aDialog.PrintVisual(fdr1, "my document");
425
426
Chapter 9
Resources, Documents, and Localization
Lesson 3: Localizing a WPF Application In an international business environment, you must consider multiple cultures and languages when writing applications. Your applications must be usable and understandable in every locale in which you do business. Localization is the process of making your application internationally consumable. In this lesson, you learn to prepare your applications for localization, to mark localizable elements, to load resources selectively by locale, and to use culture settings in validators and converters. After this lesson, you will be able to: Q
Mark localizable elements
Q
Describe the localization process
Q
Load resources by locale
Q
Use culture settings in validators and converters
Estimated lesson time: 30 minutes
Localization The culture of the user interface of an application is determined by the System.Threading .Thread.CurrentThread.CurrentUICulture property, and unless explicitly set, this property inherits the value of the system culture. Culture codes typically consist of four letters separated by a hyphen. For example, the code for English in the United States is en-US. The first two letters of the code indicate the language—English, in this instance. The second two letters indicate the locale—the United States, in this instance. In some cases where more than one alphabet might be used for a particular language, the culture code is prepended with another two-letter code that indicates the alphabet. For example, Cy-uz-UZ indicates the alphabet as Cyrllic, Uzbek as the language, and Uzbekistan as the location. There are also cases where the culture code indicates only the character set used, such as zh-CH, which indicates Chinese language using the traditional character set. If localizing only for a language, you can use just the first two letters of the culture code. For example, en would represent English without specifying a locale. You can change the UI culture of a thread manually by setting the CurrentUICulture property to a new instance of System.Globalization.Info, as shown in the following example: ' VB System.Threading.Thread.CurrentThread.CurrentUICulture = _ New System.Globalization.CultureInfo("fr-CA")
Lesson 3: Localizing a WPF Application
427
// C# System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("fr-CA");
This code must be executed before the UI is rendered in order to have any effect on the loading of culture-specific resources. The first two letters of the culture code in this example indicate French as the language and Canada as the locale, so this example sets the language of the user interface to French as spoken in Canada. Localization in WPF is enabled through the use of satellite assemblies. Localizable elements of your application are segregated into resource assemblies that are loaded automatically depending on the current UI culture. When a localized application is started, the application first looks for resource assemblies targeted to the specific culture and region (fr-CA in the previous example). If those assemblies are not found, it looks for assemblies targeted to the language only (fr in the previous example). If neither is found, then the application looks for a neutral resource set. If this is not found either, an exception is raised. You should localize your application for every language in which you expect it to be used. You can avoid localization-based exceptions by setting the NeutralResourcesLanguage attribute. This attribute designates the resource set that is to be used if a specific set of resources cannot be found. The following example demonstrates how to use the NeutralResourcesLanguage attribute: ' VB // C# [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
Localizing an Application Localization in WPF is a multi-step process. The following procedure is a high-level protocol for localizing a WPF application. Each of the steps is discussed in greater detail later in this lesson.
To localize an application
1. Add a UICulture attribute to the project file and build the application to generate culture-specific subdirectories. 2. Mark localizable properties with the Uid attribute to identify them uniquely. You must perform this step for each XAML file in your application.
428
Chapter 9
Resources, Documents, and Localization
Localizable properties include more than just text strings—they might include colors, layout properties, or any other UI property that has cultural signif icance. NOTE
3. Extract the localizable content from your application using a specialized tool (as discussed later in this chapter). 4. Translate the localizable content. 5. Create subdirectories to hold satellite assemblies for the new cultures. 6. Generate satellite assemblies using a specialized tool.
Adding the UICulture Attribute to the Project File By default, a WPF application is not culture-aware. You can make your application culture-aware by adding the UICulture attribute to the project file and building the application. The UICulture attribute indicates the default culture for the application (usually en-US for applications created and run in the United States). After adding this attribute, building the application generates a subdirectory for the culture in the application directory with localizable content in a satellite assembly.
To add the UICulture attribute to the project file
1. Open the project file for your project (.csproj for C# applications and .vbproj for Visual Basic applications) with Notepad or a similar text editor. 2. Locate the first tag. Within that tag, add the following set of XML tags: en-US
If you are creating your application in a location other than the United States or are using a language other than English, adjust the culture code in this tag accordingly. 3. Save the project file and build your application.
Marking Localizable Elements The first step in actually localizing your application is to mark elements that are localizable. Naturally, this includes all strings that are displayed in the user interface, but many other properties are localizable as well. For example, languages that use different alphabets might require the FontWidth property of visual elements to be localized, and languages that are read from right to left (rather than from left to right, as English is)
Lesson 3: Localizing a WPF Application
429
require the FlowDirection property of visual elements to be localized. Images typically are localized and thus ImageSource properties have to be localized to point to the appropriate images. Different languages require the localization of font or other UI element sizes to account for differences in string lengths. Even color combinations can be culturally sensitive and require you to localize the Foreground and Background brushes. Deciding what to localize in an application often is the most difficult part of the entire process, but it is also the most important and should be given a great deal of thought. The point to keep in mind is that localization involves much more than simple translation—it is a complex process that requires sufficient research and planning. You can mark elements for localization by adding the Uid attribute to the element in XAML. This is an attribute that uniquely identifies an element for the purpose of localization. You can add the Uid attribute, as shown here in bold: Button
Alternatively, you can use the Msbuild.exe tool to mark every element in your application with the Uid attribute by using the updateuid flag and pointing it to your project file, as shown here: msbuild /t:updateuid myApplication.vbproj
This tool should be run from the command prompt in Visual Studio, which is available in the Visual Studio Tools subdirectory of your Visual Studio folder on the Start menu. When localizable resources are extracted from your application, every localizable property of every element marked with the Uid attribute is extracted. Note that you must mark every element in your application that is in an XAML file and that you want to localize. This includes resources and resources in resource dictionaries.
Extracting Localizable Content Extraction of localizable content from your application requires a specialized tool. You can download a command-line tool named LocBaml that can extract localizable content, and there are also third-party solutions that can be used. To acquire LocBaml, navigate to http://msdn.microsoft.com/en-us/library/ms771568.aspx and download the source files from the link in the LocBaml Tool Sample topic.
430
Chapter 9
Resources, Documents, and Localization
The LocBaml tool is not a compiled application. It must be compiled by the user before it is used, and then you must run it as a command-line application from the directory that contains your complied application and use the /parse switch to provide the path to the resources dynamic link library (DLL). An example is shown here: locbaml /parse en-US\myApplication.resources.dll
LocBaml outputs a .csv file that contains all localizable properties from all the elements that have been marked with the Uid attribute.
Translating Localizable Content Content typically is not translated by the developer. Rather, localization specialists are employed to provide translated strings and values for other translatable properties. The .csv file that is generated by LocBaml provides a row of data pertaining to each localizable property that was extracted from the application. Each row contains the following information: Q
The name of the localizable resource
Q
The Uid of the element and the name of the localizable property
Q
The localization category, such as Title or Text
Q
Whether the property is readable (that is, whether it is visible as text in the user interface)
Q
Whether the property value can be modified by the translator (this is always true unless you indicate otherwise)
Q
Any additional comments that you provide for the translator
Q
The value of the property
The final entry in each row, the value of the property, is the property that must be translated by the translator. Once translation is complete, the .csv file is returned to you with the translated values in the final column.
Creating Subdirectories Before satellite assemblies can be created, you must create a subdirectory named for the appropriate culture code to house them. This subdirectory should be created in the directory where your compiled application exists, and it should be named for the culture code for which you are creating satellite assemblies. For example, if you were
Lesson 3: Localizing a WPF Application
431
creating satellite assemblies for French as spoken in Canada, you would name your directory fr-CA.
Generating Satellite Assemblies Once the resources have been translated and the subdirectories have been created, you are ready to generate your satellite assemblies, which hold culture-specific resources for a localized application. If you are using LocBaml, you can generate satellite assemblies by running LocBaml again from the directory in which your compiled application resides and using the /generate switch to generate a satellite assembly. The following example demonstrates a command-line use of LocBaml to generate a satellite assembly: locbaml /generate en-US\myApplication.resources.dll /trans:myApplication.resources.FrenchCan.csv /cul:fr-CA /out:fr-CA
Let’s break down what this command does. The /generate switch tells LocBaml to generate a satellite assembly based on the indicated assembly, which in this example is en-US\myApplication.resources.dll. The /trans switch specifies the .csv file that is used to generate the satellite assembly (myApplication.resources.FrenchCan.csv in this example). The /cul switch associates the indicated culture with the satellite assembly, and the /out switch specifies the name of the folder, which must match the specified culture exactly.
Loading Resources by Locale Once satellite assemblies have been created, your application automatically loads the appropriate resources for the culture. As described previously, you can change the current UI culture by setting the CurrentThread.CurrentUICulture property to a new instance of System.Globalization.CultureInfo, or you can change culture settings through the system. If the culture changes while an application is running, you must restart the application to load culture-specific resources. If you use code to change the UI culture, you must set the UICulture to a new instance of CultureInfo before any of the user interface is rendered. Typically, the best place to do this is in the Application.Startup event handler. Exam Tip Localization is a complex process that typically involves localization specialists in addition to the developer. Focus on learning the aspects of localization that involve the developer directly, such as preparing the application for localization and marking localizable elements. Processes that probably will be performed by a different person, such as extracting content and translation, are likely to be emphasized less on the exam.
432
Chapter 9
Resources, Documents, and Localization
Using Culture Settings in Validators and Converters While localizing UI elements is an invaluable part of localization, you also must format data appropriately for the current culture setting. In some cases, this happens automatically: For example, the String.Format method uses the correct decimal and time separators based on the current UI culture. But in instances where you provide formatting for data presented in your user interface or provide validation, your code must take the current culture into account. The Convert and ConvertBack methods of the IValueConverter interface and the Validate method of the ValidationRule class provide a parameter that indicates the culture. In the case of IValueConverter, the parameter is named culture, and in the Validate method the parameter is called cultureInfo. In both cases, the parameter represents an instance of System.Globalization.CultureInfo. Whenever you create a validation rule or converter in a localized application, you always should test the culture value and provide cultureappropriate formatting for your data. The following shows an example: ' VB _ Public Class DateBrushConverter Implements IValueConverter ' Note: the Translator class is assumed to be a class that contains a ' dictionary used to translate the provided strings. Dim myTranslator As New Translator Public Function Convert(ByVal value As Object, ByVal targetType As _ System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.Convert Dim astring As String = CType(value, String) Select Case culture.ToString Case "fr-FR" Return myTranslator.EnglishToFrench(astring) Case "de-DE" Return myTranslator.EnglishToGerman(astring) Case Else Return astring End Select End Function Public Function ConvertBack(ByVal value As Object, ByVal targetType _ As System.Type, ByVal parameter As Object, ByVal culture As _ System.Globalization.CultureInfo) As Object Implements _ System.Windows.Data.IValueConverter.ConvertBack Dim astring As String = CType(value, String) Select Case culture.ToString Case "fr-FR" Return myTranslator.FrenchToEnglish(astring) Case "de-DE"
Lesson 3: Localizing a WPF Application
433
Return myTranslator.GermanToEnglish(astring) Case Else Return astring End Select End Function End Class // C# [ValueConversion(typeof(string), typeof(string))] public class LanguageConverter : IValueConverter { // Note: the Translator class is assumed to be a class that contains a // dictionary used to translate the provided strings. Translator myTranslator = new Translator(); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string aString = (string)value; switch(culture.ToString()) { case "fr-FR": return myTranslator.EnglishToFrench(aString); case "de-DE": return myTranslator.EnglishToGerman(aString); default: return aString; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string aString = (string)value; switch(culture.ToString()) { case "fr-FR": return myTranslator.FrenchToEnglish(aString); case "de-DE": return myTranslator.GermanToEnglish(aString); default: return aString; } } }
Lab: Localizing an Application In this lab, you localize a simple application. You prepare your application for localization, mark localizable elements, extract items for localization, translate them, create a satellite assembly, and test your localization scheme.
434
Chapter 9
Resources, Documents, and Localization
Exercise: Localizing an Application 1. Open the partial solution for this exercise (which is the same as the completed solution from the lab accompanying Lesson 1, along with a precompiled copy of LocBaml in the /bin/Debug folder). 2. In Windows, open Notepad.exe by clicking the Start menu and selecting All Programs, Accessories, and then Notepad. 3. In Notepad, open the .vbproj or .csproj Project file for your application. Note that you have to change the filter in the Open File dialog box to View All Files. 4. In the first PropertyGroup element, add the following child element: en-US
5. In Notepad, save your file and exit. Switch back to Visual Studio and click Reload when prompted. 6. In Solution Explorer, click the Show All Files button (the second from the left on the toolbar), expand the Properties folder (in C#) or the My Project folder (in Visual Basic), and double-click the AssemblyInfo file to open your AssemblyInfo code file. Uncomment the line with the NeutralLanguageResources attribute. 7. In XAML view for the Window, mark the Button with the x:Uid attribute, as shown in bold here: Button
8. In Solution Explorer, double-click GridResources.xaml to open XAML view for this file. Mark each GradientStop object with the x:Uid attribute, as shown here:
9. In Visual Studio, press F6 to build your application. 10. Open a command prompt by clicking Start and selecting All Programs, Accessories, and then Command Prompt. 11. Use the DOS cd command to change directories to your \bin\Debug directory, as shown in this example. Note that you must substitute the actual path to your Debug directory: cd C:\Documents\TK9\Chapter 9\CS\Complete\Lesson3\Lesson3\bin\Debug
Lesson 3: Localizing a WPF Application
435
12. In the command window, execute the following LocBaml command to extract your localizable resources: locbaml /parse en-US\Lesson3.resources.dll
13. In Windows Explorer, navigate to the \bin\Debug folder for your solution. Rightclick Lesson3.resources.csv, choose Open With, and then choose Notepad.exe to open the .csv file. 14. In Notepad, change the entries for Button, AliceBlue, Blue, and Black to read Bouton, Blue, White, and Red, respectively. Save the file as Lesson3.Resources.frFR.csv. 15. In Windows Explorer, in the Debug folder, create a new subfolder named fr-FR. 16. In the command prompt window, execute the following LocBaml command (the command has been formatted to fit on the printed page): locbaml /generate en-US\Lesson3.resources.dll /trans:Lesson3.resources.fr-FR.csv /cul:fr-FR /out:fr-FR
17. In Visual Studio, double-click App.xaml (in C#) or Application.xaml (in Visual Basic) to open the Application file. Create a new event handler for the Application.Startup event, as shown here:
18. In Solution Explorer, right-click App.xaml (in C#) or Application.xaml (in Visual Basic) and choose View Code. In the code window, add the following line of code to the Application_Startup event handler to set the UI Culture to fr-FR: ' VB System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("fr-FR") // C# System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("fr-FR");
19. Press F5 to build and run your application. The user interface now displays a French string and displays the national colors of France.
436
Chapter 9
Resources, Documents, and Localization
Lesson Summary Q
Localization is the process of preparing your application for international audiences. Localization in WPF is done through satellite assemblies, which contain culture-specific resources that are loaded based on the UI culture of the application.
Q
Localization in WPF requires that localizable elements be marked with the x:Uid attribute, which uniquely identifies localizable elements in your application.
Q
LocBaml is a command-line application that is available from Microsoft as a downloadable, compilable sample. LocBaml can be used to extract localizable resources from your application and to build satellite assemblies with localized resources.
Q
Methods in IValueConverter and ValidationRule provide a reference to the CultureInfo object to be used in the operation. Whenever culture-specific formatting or validation is required, your code should check the culture to provide the appropriate functionality.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 3, “Localizing a WPF Application.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following must be done before using LocBaml to extract localizable resources from an application? (Choose all that apply.) A. Add the element to the project file. B. Mark localizable elements with the x:Uid attribute. C. Create a subfolder that represents the culture to which you are localizing. D. Create a subfolder that represents the neutral culture. 2. Which of the following command-line commands correctly compiles the resources contained in a file named FrenchCan.csv into satellite assemblies for the culture fr-CA for an application named myApplication?
Lesson 3: Localizing a WPF Application
437
A. locbaml /generate en-US\myApplication.resources.dll /trans:FrenchCan.csv /cul: fr-FR /out:fr-CA
B. locbaml /generate en-US\myApplication.resources.dll /trans:FrenchCan.csv /out: fr-CA
C. locbaml /generate en-US\myApplication.resources.dll /trans:FrenchCan.csv /cul: fr-CA /out:fr-CA
D. locbaml /generate en-US\myApplication.resources.dll /trans:FrenchCan.csv /out: fr-FR
438
Chapter 9 Review
Chapter Review To practice and reinforce the skills you learned in this chapter further, you can do any or all of the following: Q
Review the chapter summary.
Q
Review the list of key terms introduced in this chapter.
Q
Complete the case scenarios. These scenarios set up real-world situations involving the topics of this chapter and ask you to create a solution.
Q
Complete the suggested practices.
Q
Take a practice test.
Chapter Summary Q
Logical resources are objects that are defined in XAML and can be used by multiple elements in your application. They can be defined in a variety of different locations in your application, each with varying levels of accessibility. Resource dictionaries allow you to package resources in independent files that can be shared across projects and solutions.
Q
WPF has a considerably simplified document and printing model as compared to Windows Forms. Flow documents are documents in XAML that are made up from XAML elements and automatically laid out in the available space, and XPS documents are fixed, read-only documents. Printing support for both kinds of documents is built into their respective viewers. The PrintDialog class provides support for printing documents as well as for printing visual elements.
Q
Localization is the process of preparing your application for international audiences. Localization in WPF is done through satellite assemblies, which contain culturespecific resources that are loaded based on the UI culture of the application, and requires an accessory application such as the LocBaml tool or a third-party solution.
Key Terms Do you know what these key terms mean? You can check your answers by looking up the terms in the glossary at the end of the book. Q
Flow Document
Q
Localization
Chapter 9 Review
Q
Logical Resource
Q
Resource
Q
Resource Dictionary
Q
Satellite Assembly
Q
XPS Document
439
Case Scenario In the following case scenario, you apply what you’ve learned about how to use controls to design user interfaces. You can find answers to this question in the “Answers” section at the end of this book.
Case Scenario: Help for the Beta We’re releasing a series of beta releases of our latest product to an international audience. We want to be able to provide documentation for each beta release but don’t want the additional overhead of using a compiled Help system. Help documentation is expected to change with each beta release, so we need to be able to incorporate it into the application fairly easily. Help should be visible in its own window that is a part of the application. We have translators on staff, so we can provide documentation in multiple languages.
Technical Requirements Q
Help must be available for the beta. It must look professional, be printable, and include illustrations.
Q
We must be able to update Help between beta versions easily.
Q
We must be able to deploy versions of Help in multiple languages.
Q
Help must be visible in a separate window.
Question Answer the following question for your manager: Q
What kind of solution can you propose that meets these requirements?
440
Chapter 9 Review
Suggested Practices Q
Practice localization by localizing any of the sample applications you created in previous chapters.
Q
Convert a passage from your favorite book or poem to a flow document. Include formatting and visual aids as appropriate.
Q
Practice creating resources by creating resource dictionaries that contain a variety of different Brush and Style objects. Incorporate these resource dictionaries into existing applications or build new applications around them.
Take a Practice Test The practice tests on this book’s companion CD offer many options. For example, you can test yourself on just the content covered in this chapter, or you can test yourself on all the 70-502 certification exam content. You can set up the test so that it closely simulates the experience of taking a certification exam, or you can set it up in study mode so that you can look at the correct answers and explanations after you answer each question. MORE INFO
Practice Test
For details about all the practice test options available, see the section “How to Use the Practice Tests,” in this book’s Introduction.
Chapter 10
Deployment Deployment is the process of getting your completed application to its intended audience. This includes not only putting the application in its completed state, but also distributing it to its intended users and installing it. Microsoft Visual Studio 2008 provides several methods for deploying your application; Microsoft Windows Installer allows you to create a detailed and complex deployment project for desktop installation; and Microsoft ClickOnce technology provides an agile and flexible environment for deployment either to a desktop or to a Web or file share. In this chapter, you learn how to create deployment projects for your Windows Presentation Foundation (WPF) applications, to create Windows Installer deployment projects, and to deploy your application using ClickOnce. In addition, you learn to manage updates and security using ClickOnce technology.
Exam objectives in this chapter: Q
Deploy for standalone access.
Q
Deploy to a partial trust environment.
Q
Deploy an XBAP application.
Q
Manage upgrades.
Q
Configure the security settings of an application deployment.
Lessons in this chapter: Q
Lesson 1: Creating a Setup Project with Windows Installer. . . . . . . . . . . . . . . 443
Q
Lesson 2: Deploying Your Application with ClickOnce . . . . . . . . . . . . . . . . . . 451
Before You Begin To complete the lessons in this chapter, you must have Q
A computer that meets or exceeds the minimum hardware requirements listed in the “About This Book” section at the beginning of the book
Q
Microsoft Visual Studio 2008 Professional Edition installed on your computer
441
442
Chapter 10
Deployment
Q
An understanding of Microsoft Visual Basic or C# syntax and familiarity with Microsoft .NET Framework version 3.5
Q
An understanding of Extensible Application Markup Language (XAML)
Real World Matthew Stoecker Once the application is completed, you’ve got to get it to its audience. The deployment technologies incorporated in Visual Studio allow a wide range of options for creating my deployments. Windows Installer has been around for a while and is familiar technology to many people, but the new ClickOnce technology is terrific. ClickOnce makes deployment quick and easy and allows me to distribute changes to a deployed application easily if necessary. Unless there is a compelling reason to use Windows Installer, I use ClickOnce for all my deployments now.
Lesson 1: Creating a Setup Project with Windows Installer
443
Lesson 1: Creating a Setup Project with Windows Installer Applications need to be deployed if they are to be used by their intended audience. In some cases, deployment can be as simple as copying the .exe file for the application to its final location; in other cases, it can be very complicated, with multiple configuration steps. Visual Studio provides two primary deployment technologies: Windows Installer Setup projects and ClickOnce technology. In this lesson, you learn you use Windows Installer to deploy WPF applications. After this lesson, you will be able to: Q
Choose between using a ClickOnce deployment and a Windows Installer deployment for your project
Q
Create a Setup project
Estimated lesson time: 30 minutes
Deploying a WPF Application WPF applications can use either Windows Installer or ClickOnce as their deployment technology. Windows Installer allows you to create Setup projects that provide a very detailed and complex deployment experience for stand-alone access on a desktop computer. ClickOnce is a lighter-weight deployment technology, but at the same time it is more agile than Windows Installer and provides functionality to deploy your application to a Web or file share as well as providing for updating applications easily after they have been deployed.
Choosing Between Windows Installer and ClickOnce The choice between Windows Installer and ClickOnce depends primarily on what kind of functionality is required for the installation. Windows Installer provides a great deal of control over the installation process. A Setup project allows you to edit the file system by installing files to defined folders, to edit the registry, to define custom actions to be performed during installation, to set conditions for launching the Setup project, and to provide a custom user interface during installation. ClickOnce applications, however, provide a great deal of flexibility in the installation process. With a ClickOnce deployment, you can deploy the application to a Web or file server for ultimate download by the application’s audience or deploy the application to a CD-ROM. When deploying to an online share, you can use ClickOnce to configure an application for automatically distributing updates.
444
Chapter 10
Deployment
Table 10-1 highlights the relative advantages and disadvantages of each deployment technology. Table 10-1 Comparing Windows Installer and ClickOnce
Feature
Windows Installer
ClickOnce
Edit the file system
;
Edit the registry
;
Create associations between file types and the application
;
;
Edit the installation user interface
;
Create custom actions
;
Create start conditions
;
Install for multiple users of a workstation
;
Specify installation folder
;
Install shared components to the global assembly cache
;
Deploy for partial-trust environments
;
Configure application for automatic updates
;
Deploy so the application runs from an online location
;
Deploying with Windows Installer Windows Installer provides a full-featured deployment environment. You can use the editors provided in Visual Studio to edit the file system and registry, to create custom actions and file associations, and to implement launch conditions and custom installation user interfaces. Windows Installer requires that applications be installed under full-trust security. However, all WPF Windows and Navigation applications must run under full trust regardless of the deployment technology used.
Lesson 1: Creating a Setup Project with Windows Installer
445
When choosing Windows Installer to deploy your WPF applications, you typically deploy a Windows or Navigation application. While it is technically possible to deploy an XBAP using Windows Installer, to do so would require that the XBAP be deployed in a full-trust environment. Running under full trust defeats the purpose of an XBAP, which is to provide an application that can be executed safely in partial-trust environments.
Deploying a Stand-alone Application Deploying a stand-alone application with Windows Installer involves creating and building a Setup project. A Setup project is a Visual Studio project that packages your application files into an .msi file, incorporating a setup wizard for the application. The .msi file can be distributed by disk or by uploading to a Web or file share. When opened, the .msi file starts the application setup wizard and proceeds to install the application.
Creating the Setup Project The general process for creating a Setup project for deployment is as follows: You add a Setup project to your solution and then use the File System Editor to add your project output and any ancillary files that the application requires. Then you can use any of the other editors (as described in the section “Other Setup Project Editors,” later in this chapter) to add functionality to your Setup project and build the solution to create the redistributable .msi file. After that, the .msi file can be distributed on disk or by file download.
To add a Setup project to your solution
1. From the File menu, choose Add and then New Project to open the Add New Project dialog box. 2. In the Project Types pane, expand Other Project Types and then select Setup And Deployment. 3. In the Templates pane, click Setup Project and then click OK. The new Setup project is added to your solution and opens to the File System Editor.
Adding Files to the Setup Project with the File System Editor The File System Editor represents the file system on the target computer. You can add output files to various directories, create new directories on the target computer, or create and add shortcuts on the target computer. Figure 10-1 shows the File System Editor.
446
Chapter 10
Figure 10-1
Deployment
The File System Editor
The File System Editor is split into two panes. The left pane represents the directory structure of the target computer. Each folder in the left pane represents a folder on the target computer that exists or that will be created by the setup application. The right pane displays the content of the directory that is selected in the left pane. Initially, the File System Editor consists of three folders: Application Folder, which represents the folder where the application is installed; User’s Desktop; and User’s Program Menu. You can change the folder for a particular file by selecting the file in the right pane and dragging it to the appropriate folder. You can change the default folder for Application Folder by setting the DefaultLocation property in the Properties window. You can add folders to the File System Editor by right-clicking the left pane and choosing Add Special Folder. The shortcut menu pictured in Figure 10-2 appears. Using this menu, you can add a special folder to the File System Editor or create your own custom folder. If you choose a custom folder, this folder is created in the target computer’s file system upon installation.
Figure 10-2
The Add Special Folder shortcut menu
Lesson 1: Creating a Setup Project with Windows Installer
447
To add output from a project to a deployment project
1. Right-click Application Folder in the left pane of the File System Editor, choose Add, and then choose Project Output. The Add Project Output Group dialog box (shown in Figure 10-3) opens.
Figure 10-3
The Add Project Output Group dialog box
2. Choose the project output that you want to add to your Setup project. All .exe and .dll files created by the project are contained in Primary Output. You also can add project files such as localized resources, content files, or documentation files to your Setup project. Less frequently, you might add debug symbols, source files, or Extensible Markup Language (XML) serialization assemblies. Once you have selected output to be added to the folder, click OK.
To create a shortcut and add it on the target computer
1. In the right pane of the File System Editor, right-click the file for which you want to create a shortcut and choose Create Shortcut. A shortcut to the file is created and added to the pane. 2. Drag the shortcut from the right pane to the appropriate folder in the left pane.
To associate an icon with an application
1. In Solution Explorer, right-click the project and select Properties to open the Properties page. 2. Click the Application tab. 3. Click the button next to the Icon combo box and browse to the desired icon. Select the desired icon and click OK.
448
Chapter 10
Deployment
To associate an icon with an application shortcut
1. In the left pane of the File System Editor, right-click a folder (usually Application Folder), choose Add, and then select File. The Add Files dialog box opens. 2. Browse to the .ico file that you want to associate with the application and click Open to add it to your Setup project. 3. Create a shortcut to your application as described in the previous procedure. 4. In the File System Editor, select the shortcut. 5. In the Properties window, select the Icon property, click the down arrow in the Property Value box, and then choose (Browse . . .). In the Icon dialog box, click Browse and browse to the icon (at the location in which you saved it in Step 1) that you want to associate with your application. 6. Select the icon and then click OK.
Other Setup Project Editors In addition to the File System Editor, Visual Studio provides five other editors that can be used to configure the contents and behavior of the Setup project. These additional editors are described briefly here: Q
Registry Editor
This editor allows you to write entries to the registry upon instal-
lation. Q
File Types Editor
This editor allows you to set associations between applications
and file types. Q
User Interface Editor
Q
Custom Actions Editor
This editor allows you to define custom actions to be performed during installation, commitment of the installation, and rollback of the installation, as well as during uninstallation.
Q
Launch Conditions Editor
This editor allows you to edit the user interface seen during installation for both regular installation and administrative installation.
This editor allows you to set conditions for launching the installation of your Setup project.
Lab: Creating a Setup Project In this lab, you create a simple Setup project.
Lesson 1: Creating a Setup Project with Windows Installer
449
Exercise: Creating a Setup Project 1. Open the completed solution for Chapter 1, Lesson 1, Exercise 1. Note that both labs in this chapter use this completed lab as a starting point. 2. From the File menu, choose Add and then choose New Project. The Add New Project dialog box opens. 3. In the right pane of the Add New Project dialog box, expand Other Project Types, choose Setup And Deployment, and, in the left pane, select Setup Project. Keep the default name of Setup1 and click OK. A Setup project named Setup1 is added to your solution. 4. In the left pane of the File System Editor, right-click Application Folder, select Add, and then choose Project Output. The Add Project Output Group dialog box appears. 5. In the Add Project Output Group dialog box, select Primary Output and click OK. 6. In the left pane of the File System Editor, select Application Folder. Right-click an empty area in the right pane of the File System Editor and select Create New Shortcut. In the Select Item In Project dialog box, double-click Application Folder and select Primary Output From Lesson 1 (Active). Click OK. 7. Drag your newly created shortcut to the User’s Desktop folder. 8. From the Build menu, choose Configuration Manager. The Configuration Manager dialog box opens. 9. In the Configuration Manager dialog box, select the Build check box in the Setup1 row. Click Close. 10. Press F6 to build your solution. 11. In Windows Explorer, navigate to the Exercise 1\Setup1\Debug folder within your project. Double-click Setup1.msi to start the installation wizard. 12. Complete the installation wizard. The application is installed on your machine and a shortcut is added to your desktop. 13. In Windows, in the Control Panel, select Programs And Features. Right-click Setup1 and select Uninstall to uninstall your application. Alternatively, you can select Setup1 and then click Uninstall.
450
Chapter 10
Deployment
Lesson Summary Q
Windows Installer allows you to create detailed deployments for stand-alone applications. Visual Studio Setup projects provide a variety of editors that you can use when designing your deployment to configure the target machine during the installation of the application.
Q
The File System Editor allows you to edit both the files that are included by your Setup project and the file system on the target machine. You can add folders, create shortcuts, and associate icons with applications.
Q
All installations done with Windows Installer must be performed under fulltrust security conditions. Thus, this process is unsuitable for installing XBAPs, which typically are designed for partial-trust conditions.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 1, “Creating a Setup Project with Windows Installer.” The questions are also available on the companion CD if you prefer to review them in electronic form. NOTE
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. Which of the following is NOT a characteristic of deploying with Setup projects? A. Allows the creation of file associations B. Allows deployment to partial-trust environments C. Allows the creation of new folders on the target machine D. Allows the writing of registry values 2. Which of the following is required to associate an icon with an application shortcut at setup? (Choose all that apply.) A. The icon must be added to your Setup project in the File System Editor. B. The icon must be added to the project as an embedded resource. C. The icon must be set to the Icon property of a shortcut to the application in the File System Editor. D. The icon must be added to the project as a resource.
Lesson 2: Deploying Your Application with ClickOnce
451
Lesson 2: Deploying Your Application with ClickOnce Although not as complex as Windows Installer, ClickOnce provides you with a wide range of control over the installation of your application. You can configure applications for stand-alone deployment or require the user to run the application from a server. This lesson describes the various options presented by ClickOnce deployment. After this lesson, you will be able to: Q
Deploy a WPF application with ClickOnce for stand-alone access.
Q
Deploy an XBAP with ClickOnce.
Q
Configure a deployed application for a partial-trust environment.
Estimated lesson time: 30 minutes
Deploying with ClickOnce ClickOnce is a deployment technology that allows you to create self-updating applications that can be installed from a variety of media and that require minimal user interaction. Any WPF application can be published as a ClickOnce application, including Windows applications, Navigation applications, and XBAPs. You can use ClickOnce to create applications that are deployed from a Web site, a file share, or a CD-ROM. You can configure ClickOnce applications to be run only while the user is online or while the user is off-line as well. ClickOnce applications are isolated from the rest of the system. Because they are completely self-contained, they share no components with any other applications installed on the computer and run no risk of breaking other applications’ installations. Windows and Navigation applications require full trust to be installed on a local computer via ClickOnce, but XBAPs and applications that run online execute in the Internet security zone by default (or in the local Intranet security zone if they are run from a file share on the local intranet). Exam Tip
While WPF applications can be deployed with either Setup projects or ClickOnce, ClickOnce usually is considered the preferred technology for WPF deployment. Have a good general knowledge of Setup projects for the exam, but focus on learning the intricacies of ClickOnce.
452
Chapter 10
Deployment
Deploying an Application Using ClickOnce You can deploy an application directly in Visual Studio from the Publish tab of the project properties dialog box. To open the Publish tab, right-click the project in Solution Explorer, choose Properties, and then select the Publish tab. This displays the Publish properties page, as shown in Figure 10-4.
Figure 10-4
The Publish properties page
You can publish your application using ClickOnce by setting the Publish properties in this page and then clicking Publish Now. The process of configuring Publish properties is described in the following sections. The information contained in these sections applies to both stand-alone applications and XBAPs unless noted.
Selecting the Publishing Location You can specify the publishing location in the Publishing Folder Location combo box in the Publish properties page. The Publishing Folder Location can be a file path, a network share, a local Microsoft Internet Information Services (IIS) folder, a Hypertext Transfer Protocol (HTTP) address, or a File Transfer Protocol (FTP) address. This is generally the address to which users go to install the application. Clicking the button to the right of the Publishing Folder Location combo box opens the Open Web Site dialog box, shown in Figure 10-5. The Open Web Site dialog box allows you to select the type of site to deploy your application to (file address, HTTP
Lesson 2: Deploying Your Application with ClickOnce
453
address, FTP address, or local IIS installation) and to browse to and configure each location option.
Figure 10-5
The Open Web Site dialog box
Publishing to a Temporary Location You can specify a separate location for publishing your application and the installation Uniform Resource Locator (URL). You might want to do this if you want to deploy your application to a staging server first and then have an administrator copy the files from the staging server to the final location from which users download the application. You can specify an installation URL that represents a temporary location or staging server in the Installation Folder URL combo box immediately beneath the Publishing Folder Location combo box. You also can click the button to the right of the Installation URL combo box to open the Open Web Site dialog box, which allows you to browse to and configure the final location. Note that if you designate an installation URL, you have to copy the files manually from the staging server to the final installation server to complete the deployment.
Online and Off-line Deployment You can configure a ClickOnce application to be available only when the user is online or when the user is online or off-line. By selecting The Application Is Available Online Only in the Install Mode And Settings group of the Publish properties page,
454
Chapter 10
Deployment
you require the application to be run directly from the location specified in the Publishing Folder Location combo box. This ensures that the user always runs the most recent available version of the application. When an application is available in Online Only mode, the application still runs on the client machine, as opposed to the server. However, it is not installed to the client machine and must be retrieved from the server each time it is run. NOTE
You can select The Application Is Available Offline As Well to make the application available both online and off-line. In this case, the application is copied to the local computer and added to the Start menu and the Add/Remove Programs box in the Control Panel in Windows.
Specifying Application Information You can specify information about the application by clicking the Options button in the Publish properties page. This opens the Publish Options dialog box, shown in Figure 10-6.
Figure 10-6
The Publish Options dialog box
Lesson 2: Deploying Your Application with ClickOnce
455
In the Publish Options dialog box, you can set the Publisher Name, the Product Name, the Support URL, and the name for the Deployment Web Page that is generated when the application is published. Of particular interest for stand-alone applications are the Publisher Name and the Product Name settings. When an application is installed for off-line use, an entry is made in the All Programs folder in the Start menu. The entry appears under the Product Name, which in turn appears under the Publisher Name, so these values must be set in the Publish Options dialog box.
Configuring ClickOnce Update Options ClickOnce allows you to configure applications to check for updates automatically. By clicking Updates in the Publish properties page, you can open the Application Updates dialog box, shown in Figure 10-7.
Figure 10-7
The Application Updates dialog box
Configuring Update Settings with Visual Studio To enable the application to check for updates, select the check box labeled The Application Should Check For Updates. Doing so enables the other options in the dialog box.
456
Chapter 10
Deployment
You can specify when the application checks for updates by selecting either After The Application Starts or Before The Application Starts. If you select Before The Application Starts, the application checks for new updates every time the application starts. This ensures that the user always runs the most recent version of the application, but it also slows the application’s performance at startup. If you select After The Application Starts, you can specify that the application check for updates every time it is run, or at a designated time interval by choosing the appropriate option under Specify How Frequently The Application Should Check For Updates. You also can specify a minimum required version for the application, and you can specify a different location for updates if your updates are hosted in a location other than the install location.
Loading Updates Programmatically Rather than scheduling updates, you might want to give the user the option to check for updates. While no way to do this is built into the properties page, you can add functionality to your application to check for and install updates manually. For example, you might provide a menu option entitled Check For Updates. You can check for updates in code by using the ApplicationDeployment class. This class represents a ClickOnce deployment. To use this class in code, you first must add a reference to System.Deployment. Once this reference has been added, you can retrieve an instance that represents the current deployment from the static CurrentDeployment property, as shown here: 'VB Dim aDep As System.Deployment.Application.ApplicationDeployment aDep = _ System.Deployment.Application.ApplicationDeployment.CurrentDeployment // C# System.Deployment.Application.ApplicationDeployment aDep; aDep = System.Deployment.Application.ApplicationDeployment.CurrentDeployment;
Once you have obtained a reference to the current deployment, you can check for updates using the CheckForUpdate method, and if updates are available, you can update the application using the Update method, as seen here: 'VB If aDep.CheckForUpdate Then aDep.Update() End If
Lesson 2: Deploying Your Application with ClickOnce
457
// C# if (aDep.CheckForUpdate()) { aDep.Update(); }
The CheckForUpdate and Update methods also have asynchronous counterparts, called CheckForUpdateAsync and UpdateAsync respectively, which can be used to perform updates asynchronously. When CheckForUpdateAsync returns, it raises the CheckForUpdateCompleted event. By handling this event, you can query the CheckForUpdateCompletedEventArgs argument in the event handler to determine if an update is available. If an update is available, you can call UpdateAsync to download and install the update asynchronously. If the application is running, updates are applied after the application ends, so the updates are not seen until the application is restarted. These techniques are demonstrated in the following example: ' VB Dim aDep As System.Deployment.Application.ApplicationDeployment Public Sub New() ' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. aDep = _ System.Deployment.Application.ApplicationDeployment.CurrentDeployment AddHandler aDep.CheckForUpdateCompleted, AddressOf UpdateCheckCompleted End Sub Public Sub UpdateApp() aDep.CheckForUpdateAsync() End Sub Private Sub UpdateCheckCompleted(ByVal sender As Object, ByVal e As _ System.Deployment.Application.CheckForUpdateCompletedEventArgs) If e.UpdateAvailable Then aDep.UpdateAsync() End If End Sub // C# System.Deployment.Application.ApplicationDeployment aDep; public myApplication { InitializeComponent(); aDep = System.Deployment.Application.ApplicationDeployment.CurrentDeployment; aDep.CheckForUpdateCompleted += UpdateCheckCompleted; } public void UpdateApp() { aDep.CheckForUpdateAsync(); }
458
Chapter 10
Deployment
private void UpdateCheckCompleted(object sender, System.Deployment.Application.CheckForUpdateCompletedEventArgs e) { if (e.UpdateAvailable) { aDep.UpdateAsync(); } }
Migrating Settings and User Data Application settings and default user settings are stored in the application’s .config file. These typically are updated with each new application update. However, user settings that have been modified by the user are stored in a local file that is separate from the .config file. Thus, no additional effort is required to migrate user settings when an application is updated. Similarly, you should take steps not to overwrite any custom files that store user data.
Deploying an XBAP with ClickOnce With ClickOnce, XBAPs can be deployed in much the same way as a Windows or Navigation application. All the ClickOnce publishing properties also apply to XBAPs, but there are a few XBAP-specific concerns as well. XBAPs run in the browser and typically are run from a Web site. Thus, they almost always are run under partial trust. You must design your XBAP applications for execution in a partial-trust environment and test them in the same trust environment in which you plan for users to execute them.
Considerations for Deploying in Partial-Trust Environments When designing an application for execution in a partial-trust environment, you should avoid coding features that require access to protected system resources. For example, you should not attempt to access the file system or registry in an XBAP. You can use built-in tools to indicate the permissions required by your application. Configuring Code Access Security Requirements
You can configure code access security requirements on the Security properties page, which you can access by rightclicking your project, choosing Properties, and then choosing the Security tab. The Security properties page is shown in Figure 10-8.
Lesson 2: Deploying Your Application with ClickOnce
Figure 10-8
459
The Security properties page
You can configure the permissions requested by your application in this page by first selecting the Enable ClickOnce Security Settings check box and then selecting the radio button labeled This Is A Partial Trust Application. The ClickOnce Security Permissions group beneath this radio button is used to configure the permissions requested by your application. You can set your requested permissions to the defaults allowed by the Internet or local intranet zones by selecting the zone in the drop-down box labeled Zone Your Application Will Be Installed From. You also can create custom security requests by selecting (Custom) in the drop-down box and then individually setting each permission to Include or Exclude in the Setting column. If the requested permissions violate the local security policy of the executing computer, the application will not run. Table 10-2 lists general operations that are not safe in partial-trust environments.
460
Chapter 10
Deployment
Table 10-2 Features That Are Unsafe for Use in Partial-Trust Environments
Feature Area
Features
General
Window (Application-Defined Windows and Dialog Boxes) SaveFileDialog File System Registry Access Drag And Drop XAML Serialization (via XamlWriter.Save) UIAutomation Clients Source Window Access (HwndHost) Full Speech Support Windows Forms Interoperability
Web Integration
Web Services (using Windows Communication Foundation) Scripting Document Object Model
Visuals
Bitmap Effects
Editing
Rich Text Format Clipboard Full XAML support
When writing applications that might be deployed in partial-trust environments, you should try to avoid using the features listed in Table 10-2 as much as possible. For applications that might be run in both partial trust and full trust, you can use Code Access Security (CAS) to check at run time whether a specific operation is permitted, and if not, to recover gracefully from that attempt rather than cause the application to crash. The following example demonstrates creating a Demand for FileIOPermission to determine whether it is safe to write to a file. If the application does not have permission to write to the specified file, an exception is thrown: ' VB Try Dim perm As New System.Security.Permissions.FileIOPermission( _ System.Security.Permissions.FileIOPermissionAccess.AllAccess, _ "C:\myFile")
Lesson 2: Deploying Your Application with ClickOnce
461
perm.Demand() ' proceed with writing the file Catch ex As Exception ' recover from being unable to write the file End Try // C# try { System.Security.Permissions.FileIOPermission perm = new System.Security.Permissions.FileIOPermission( System.Security.Permissions.FileIOPermissionAccess.AllAccess, "C:\\myFile"); perm.Demand(); // proceed with writing the file } catch { // recover from being unable to write the file }
Choosing a Local Persistence Mechanism
Because XBAPs generally do not have access to the local file system, you must use a different mechanism to save user information. User-specific data can be written to the isolated storage file store, a specialized technology that allows isolated access to the file system under partial-trust conditions. Isolated storage is described in Chapter 1, “WPF Application Fundamentals.” You can also read and write to user settings in a partial-trust environment. Settings are described in Chapter 2, “Events, Commands, and Settings.”
Configuring the Application Manifest The Application Manifest is a file created by Visual Studio during ClickOnce deployment that describes the files and dependencies required by an application, the identity and trust information of an assembly, the entry point for the application, and any associations between file types and the application. All these aspects, except for file associations, are configurable via the Visual Studio user interface, as has been described earlier in this chapter. To create a file association, you must edit the Application Manifest file manually.
Adding a File Association A file association is described in the Application Manifest by the element. The attributes of this element are listed in Table 10-3. All four attributes are required.
462
Chapter 10
Deployment
Table 10-3 Attributes of the Element
Attribute
Description
extension
The file extension to be associated with the application.
description
A description of the file type for use by the shell.
progid
A name uniquely identifying the file type.
defaultIcon
Specifies the icon to use for files with this extension. The .icon file must be described in the Application Manifest, and thus must have been added to the application with the Build Action property set to Content.
You can add a element to your Application Manifest by adding it to the App.Manifest file in Visual Studio. This file is added to your project when you select Enable ClickOnce Security Settings on the Security tab of your project’s property pages. The content in the App.Manifest file is folded into the Application Manifest at publish time. The following procedure describes how to create a file association for an application.
To create a file association
1. Select or create an icon for your file type. 2. From the Project menu, choose Add Existing Item. The Add Existing Item dialog box opens. Set the filter to All Files and navigate to your icon, select it, and click Add. 3. With the icon selected in Solution Explorer, in the Properties window, set the Build Action property to Content and the Copy To Output Directory property to Copy Always. 4. In Solution Explorer, right-click your project and select Properties. Select the Security tab. 5. On the Security properties page, select the Enable ClickOnce Security Settings check box. The App.manifest file is added to your solution. 6. In Visual Basic only, in Solution Explorer, select View All Files and expand My Project. 7. Double-click App.manifest to open the App.manifest file.
Lesson 2: Deploying Your Application with ClickOnce
463
8. Just before the final closing tag, add a element that includes values for all four required attributes. An example is shown here:
Note that you can create file associations only for applications that are installed as stand-alone applications, and you can create no more than eight file associations per application.
Associating a Certificate with the Application Certificates are digital signatures issued by certificate authorities (CAs) that guarantee the identity of the application’s publisher. You can use Visual Studio to associate a certificate with your application. To associate a certificate with your application, right-click your project in Solution Explorer, choose Properties, and then choose the Signing tab to open the Signing properties page, as shown in Figure 10-9.
Figure 10-9
The Signing properties page
To enable certificate signing for your application, select the Sign The ClickOnce Manifests check box. This allows you to choose a certificate, either from your local
464
Chapter 10
Deployment
certificate store or from a particular file, by pressing the appropriate button and browsing to the correct store or file. You also can create a test certificate by clicking Create Test Certificate and providing a password for certificate creation. You can view the details of the certificate by clicking More Details.
Lab: Publishing Your Application with ClickOnce In this lab, you publish your application using ClickOnce.
Exercise: Publishing Your Application 1. Open the completed solution for Chapter 1, Lesson 1, Exercise 1 from the companion media. (Do not continue with the version you modified in the previous lab.) 2. In Solution Explorer, right-click your project and choose Properties. 3. Select the Publish tab. 4. In the Publishing Folder Location drop-down box, select a folder in which to publish your application. The completed solution on the CD-ROM that comes with this book uses the default entry (Publish\), which is a subfolder in the project folder. If you have access to a Web or FTP server, you might want to try publishing to a server location instead. 5. Click Options to open the Publish Options dialog box. Add a suitable Publisher Name and Product Name and click OK. 6. Click Publish Now to publish the application to the selected location. If you are publishing to a file, Visual Studio opens the publish folder in Windows Explorer for you when the process is complete. 7. In Windows Explorer (for the file system) or Windows Internet Explorer (for HTTP or FTP sites), browse to the publish location for your application if the publish location is not already open. Double-click Setup to launch the application installation. The Application Install dialog box opens. 8. In the Application Install dialog box, click Install. The installation process installs and starts the application. Close the application. 9. In Windows, click Start, select All Programs, and look for the Publisher Name you added. Then confirm that the Product Name is present in the Start menu hierarchy. 10. Open Control Panel and then open Programs And Features. Right-click Lesson 2 (or the name you designated for your product) and choose Uninstall/Change to
Lesson 2: Deploying Your Application with ClickOnce
465
open the Maintenance dialog box. Alternatively, you can select Lesson 2 and then click Uninstall. 11. Select Remove The Application From This Computer and click OK to uninstall the application.
Lesson Summary Q
ClickOnce is a versatile and flexible deployment technology. It allows you to publish an application to a disk location, a file share, an FTP site, or an HTTP address. ClickOnce application installations are segregated from the rest of the system and do not use shared components. Thus, installing a ClickOnce application runs no risk of breaking other application installations.
Q
You can install a ClickOnce application so that it is available online or off-line. If an application is available online only, it runs from its server rather than from a location on the local machine. If an application is available online only, a shortcut to it is not added to the Start menu.
Q
ClickOnce applications can be configured to be self-updating. You can designate an application to check for updates every time it starts or on a set schedule, such as every week. You can also implement code to check for updates manually.
Q
Stand-alone ClickOnce applications must be installed to a local computer under full-trust conditions. Applications that are run online are executed in the Internet security zone (if run from a Web site) or the local intranet security zone (if run from a file server on the local intranet).
Q
XBAPs are designed to run in partial-trust conditions, and as such they should not attempt to access system resources that are not allowed by the default Internet security zone settings. Because they cannot use the file system, XBAPs should persist user data via isolated storage or user settings.
Q
You can create file associations for ClickOnce applications by editing the App.manifest file. Visual Studio also provides tools for adding certificates to your application.
Lesson Review You can use the following questions to test your knowledge of the information in Lesson 2, “Deploying Your Application with ClickOnce.” The questions are also available on the companion CD if you prefer to review them in electronic form.
466
Chapter 10
NOTE
Deployment
Answers
Answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the book.
1. For an XBAP that is installed with ClickOnce to run from an Internet Web page, what is the default security mode under which the XBAP executes? A. Full trust B. Intranet zone C. Internet zone D. Custom zone 2. You are converting a WPF application that formerly executed under full trust to execute under partial trust. This application previously stored user data in a small local file, but now it cannot because it lacks FileIO permissions. What is the appropriate way to store user data in a partial-trust environment? (Choose all that apply.) A. Use isolated storage to store user data. B. Create a Web service that saves data back to the deployment server. C. Save user data as user settings. D. Call FileIOPermission.Demand to gain temporary access to the file system. 3. Which of the following code snippets manually checks for updates and installs updates if available? For examples C and D, assume that the UpdateApp method has been called after execution of the constructor. (Choose all that apply.) A. ' VB Dim aDep As System.Deployment.Application.ApplicationDeployment aDep = _ System.Deployment.Application.ApplicationDeployment.CurrentDeployment If aDep.CheckForUpdate Then aDep.Update() End If // C# System.Deployment.Application.ApplicationDeployment aDep; aDep = System.Deployment.Application.ApplicationDeployment.CurrentDeployment; if (aDep.CheckForUpdate()) { aDep.Update(); }
Lesson 2: Deploying Your Application with ClickOnce
B. ' VB Dim aDep As System.Deployment.Application.ApplicationDeployment aDep = _ System.Deployment.Application.ApplicationDeployment.CurrentDeployment If aDep.CheckForUpdateAsync Then aDep.Update() End If // C# System.Deployment.Application.ApplicationDeployment aDep; aDep = System.Deployment.Application.ApplicationDeployment.CurrentDeployment; if (aDep.CheckForUpdateAsync()) { aDep.Update(); }
C. ' VB Dim aDep As System.Deployment.Application.ApplicationDeployment Public Sub New() ' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. aDep = _ System.Deployment.Application.ApplicationDeployment.CurrentDeployment AddHandler aDep.CheckForUpdateCompleted, AddressOf UpdateCheckCompleted End Sub Public Sub UpdateApp() aDep.CheckForUpdate() End Sub Private Sub UpdateCheckCompleted(ByVal sender As Object, ByVal e As _ System.Deployment.Application.CheckForUpdateCompletedEventArgs) If e.UpdateAvailable Then aDep.UpdateAsync() End If End Sub // C# System.Deployment.Application.ApplicationDeployment aDep; public myApplication { InitializeComponent(); aDep = System.Deployment.Application.ApplicationDeployment.CurrentDeployment; aDep.CheckForUpdateCompleted += UpdateCheckCompleted; } public void UpdateApp() { aDep.CheckForUpdate(); }
467
468
Chapter 10
Deployment
private void UpdateCheckCompleted(object sender, System.Deployment.Application.CheckForUpdateCompletedEventArgs e) { if (e.UpdateAvailable) { aDep.UpdateAsync(); } }
D. ' VB Dim aDep As System.Deployment.Application.ApplicationDeployment Public Sub New() ' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. aDep = _ System.Deployment.Application.ApplicationDeployment.CurrentDeployment AddHandler aDep.CheckForUpdateCompleted, AddressOf UpdateCheckCompleted End Sub Public Sub UpdateApp() aDep.CheckForUpdateAsync() End Sub Private Sub UpdateCheckCompleted(ByVal sender As Object, ByVal e As _ System.Deployment.Application.CheckForUpdateCompletedEventArgs) If e.UpdateAvailable Then aDep.UpdateAsync() End If End Sub // C# System.Deployment.Application.ApplicationDeployment aDep; public myApplication { InitializeComponent(); aDep = System.Deployment.Application.ApplicationDeployment.CurrentDeployment; aDep.CheckForUpdateCompleted += UpdateCheckCompleted; } public void UpdateApp() { aDep.CheckForUpdateAsync(); } private void UpdateCheckCompleted(object sender, System.Deployment.Application.CheckForUpdateCompletedEventArgs e) { if (e.UpdateAvailable) { aDep.UpdateAsync(); } }
Chapter 10 Review
469
Chapter Review To practice and reinforce the skills you learned in this chapter further, you can do any or all of the following: Q
Review the chapter summary.
Q
Review the list of key terms introduced in this chapter.
Q
Complete the case scenarios. These scenarios set up real-world situations involving the topics of this chapter and ask you to create a solution.
Q
Complete the suggested practices.
Q
Take a practice test.
Chapter Summary Q
Windows Installer Setup Projects provide a detailed and intensive installation environment that allows the user to configure many aspects of the target machine. Setup projects are executed under full trust.
Q
ClickOnce is a lighter-weight and versatile installation technology that allows you to publish your application to a variety of deployment site types. Through ClickOnce, you can manage security settings, configure automatic updates, and install an application for either online or off-line use.
Key Terms Do you know what these key terms mean? You can check your answers by looking up the terms in the glossary at the end of the book. Q
Application Manifest
Q
Certificate
Q
ClickOnce
Q
File System Editor
Q
Setup Project
470
Chapter 10 Review
Case Scenario In the following case scenario, you apply what you’ve learned about how to deploy WPF applications. You can find answers to these questions in the “Answers” section at the end of this book.
Case Scenario: Buggy Beta We are so behind schedule! Our customers have been eagerly awaiting the release of our Widget 2.0, the most anticipated release of the year. Unfortunately, development has been fraught with issues, and we are hopelessly behind. While we will never make our final release date, we need to get a beta out as fast as possible to appease our customers while we still have some. We finally have a reasonably functional beta, even though lots of bugs remain. Everyone in the company is working around the clock to fix these issues, and we can expect to have frequent updates.
Technical Requirements Q
We must release the buggy beta now.
Q
We must provide frequent updates that incorporate bug fixes as quickly as possible.
Question Q
What deployment strategy can we use to address these issues?
Suggested Practices Q
Practice creating Setup projects for other solutions from this training kit.
Q
Practice creating ClickOnce installations for other solutions from this training kit. Practice installing to a file share, a local IIS server, a file address, and a Web site if possible.
Q
Create an application that allows the user to click a button to check for updates by incorporating code that uses the ApplicationDeployment class.
Q
Use ClickOnce to create a file association between your application and a particular file type.
Chapter 10 Review
471
Take a Practice Test The practice tests on this book’s companion CD offer many options. For example, you can test yourself on just the content covered in this chapter, or you can test yourself on all the 70-502 certification exam content. You can set up the test so that it closely simulates the experience of taking a certification exam, or you can set it up in study mode so that you can look at the correct answers and explanations after you answer each question. MORE INFO
Practice tests
For details about all the practice test options available, see the section “How to Use the Practice Tests,” in this book’s Introduction.
Answers Chapter 1: Lesson Review Answers Lesson 1 1. Correct Answers: B and D A. Incorrect. Accessing the registry is not allowed by Internet zone permissions. B. Correct. Isolated storage allows XBAPs to read and write small files safely. C. Incorrect. Accessing the registry is not allowed by Internet zone permissions. D. Correct. XBAPs can display graphics and animation. 2. Correct Answer: D A. Incorrect. You must call Show from an instance of the class, not the class itself. B. Incorrect. The Show method is an instance method, and must be called from an instance of the class, not the class itself; also, the Show method does not take an argument. C. Incorrect. The Show method does not take an argument. D. Correct. You must instantiate a new Window1 object and then call Show from that instance.
Lesson 2 1. Correct Answers: A, B, and C A. Correct. XBAPs are hosted in Internet Explorer. B. Correct. Navigation applications are hosted in NavigationWindows. C. Correct. A frame control can host a page. D. Incorrect. A PageFunction cannot host a page directly unless a frame is placed in it first. 2. Correct Answer: C A. Incorrect. The Page or PageFunction for the custom journal entry must implement IProvideCustomContent. B. Incorrect. You must call NavigationService.AddBackEntry to create the custom back entry. 473
474
Answers
C. Correct. You do not need an instance of JournalEntry to create a custom back entry. D. Incorrect. You must create a class that inherits from CustomContentState to store the state of the page. 3. Correct Answer: B A. Incorrect. FragmentNavigation occurs after LoadCompleted and NavigationProgress occurs after Navigated. B. Correct. This is the correct order in which navigation events fire. C. Incorrect. NavigationProgress occurs after Navigated. D. Incorrect. FragmentNavigation occurs after LoadCompleted.
Lesson 3 1. Correct Answers: A and B A. Correct. RunWorkerAsync raises the DoWork event, which must be handled to run code on the background thread. B. Correct. The DoWork event handler contains the code that will be run on the background thread. C. Incorrect. Handling the ProgressChanged event is not required. D. Incorrect. Setting WorkerSupportsCancellation to True is not required. 2. Correct Answers: A and C A. Correct. The Dispatcher.BeginInvoke method will execute a method asynchronously and safely on the main thread. B. Incorrect. Direct access of the user interface from a background thread is not allowed. C. Correct. Using the built-in mechanism of BackgroundWorker for reporting is the preferred way to report progress that can be expressed numerically. D. Incorrect. Direct access of the user interface from a background thread is not allowed.
Case Scenario: Designing a Demonstration Program 1. An XBAP is ideal for this application. With an XBAP, we can host the application on our server and customers can download it and run it immediately in their browser without having to install it. Because it automatically runs under Internet zone permissions, we don’t have to worry about our application conflicting with
Answers
475
local security policy, and our customers will be more likely to access an application that they know they can trust. 2. An XBAP also provides an ideal flow model. Our users can open the initial page, navigate to the animation, view other information about our product’s new features at their leisure, and then when they are ready to buy, they can navigate to the shopping cart that is built into the application.
Chapter 2: Lesson Review Answers Lesson 1 1. Correct Answer: B A. Incorrect. No Button1_Click method is defined as a handler for the button1 click event in this example. B. Correct. Click is a bubbling event, so stackPanel1_Click will execute first. C. Incorrect. Click is a bubbling event, so stackPanel1_Click will execute first. D. Incorrect. Click is a bubbling event, so stackPanel1_Click will execute first. 2. Correct Answer: B A. Incorrect. MouseDown is a bubbling event, and thus is raised after corresponding tunneling events, which are prepended with “Preview,” are raised. B. Correct. PreviewMouseDown is a tunneling event, and is thus raised by controls from the top down in the visual tree. C. Incorrect. Because the StackPanel is contained in the Grid, PreviewMouseDown will be raised by the grid first. D. Incorrect. Click is a bubbling event, and thus occurs after all preview events are raised. 3. Correct Answer: A A. Correct. Because the Activated event is raised the first time the window is shown and every other time it is activated, this is the correct event to handle. B. Incorrect. This event will execute code only when the application begins. C. Incorrect. Because the Activated event is raised the first time the window is shown and every other time it is activated, you do not need to handle the Startup event as well. D. Incorrect. This event would execute code when the application is started and every time the window loses focus.
476
Answers
Lesson 2 1. Correct Answers: A, B, D, and F A. Correct. You must create a new instance of CommandBinding for each command you register. B. Correct. The CommandBinding.Command property specifies what command is to be bound. C. Incorrect. A command is not required to have any input gestures registered. D. Correct. If the CommandBinding.Executed event is not handled, no code will be executed when the command is invoked. E. Incorrect. Although handling the CanExecute event enables you to determine when the command is unavailable, it is not required to register a command. F. Correct. You must add CommandBinding to one of the CommandBindings collections for it to be registered. 2. Correct Answer: C A. Incorrect. CanExecute is a method, not a property, and cannot be set to a value. B. Incorrect. The Execute method requires a target element where the run time begins looking for a CommandBinding. C. Correct. You call the command’s Execute method to execute the command, specifying the parameter and the owning control. D. Incorrect. There is no CanExecute method on the Window class.
Lesson 3 1. Correct Answer: C A. Incorrect. Individual settings are exposed as properties on the Settings object, not as members of a collection. B. Incorrect. Individual settings are exposed as properties on the Settings object, not as members of a collection. In addition, you must call the Settings.Save method to persist changes to the settings. C. Correct. You first set the named setting and then call the Save method. D. Incorrect. You must call the Settings.Save method to persist changes to the settings.
Answers
477
2. Correct Answer: D A. Incorrect. Settings are strongly typed and therefore do not need to be cast. B. Incorrect. Settings are strongly typed and therefore have no conversion methods. C. Incorrect. Assigning a strongly typed setting to an object will require that the object be cast as a System.Window.Media.Color type. D. Correct. Because settings are strongly typed, you can assign a setting directly to a variable of the correct type.
Case Scenario 1: Validating User Input The key to implementing these requirements is routed events. Since all the TextBox controls in this form will be contained in a Grid or other layout control, you can implement the validation rules by handling the PreviewKeyDown event. Because PreviewKeyDown is a tunneling event, it will be raised by the layout control before other events are raised by the TextBox controls, and you have an opportunity to cancel invalid keystrokes before they are entered into the user interface. The global validation rules can be applied directly, and then individual validation rules can be applied based on the e.Source property, which will indicate in what control the event originated.
Case Scenario 2: Humongous Insurance User Interface Commands allow you to implement much of this functionality. By using commands, you can assign a command to the Command property of each menu item. When that command is unavailable, as determined by the handler for its binding’s CanExecute event, the corresponding menu item will be disabled. Shortcut keys and mouse gestures can be added to the InputGestures collection of the command, and these will be inactivated as well when the command is unavailable. To implement auto-fill functionality in your TextBox controls, you can handle the TextBox.KeyDown event. In this event, you can test if appropriate keystrokes have been entered and then complete the entry automatically as necessary.
Chapter 3: Lesson Review Answers Lesson 1 1. Correct Answer: B A. Incorrect. A content control can host a single nested control. B. Correct. A content control can host a single nested control.
478
Answers
C. Incorrect. While a content control can host a list control, which itself can host an unlimited number of nested members, the content control can itself host only a single control. D. Incorrect. All content controls can host only a single nested control. 2. Correct Answers: A and C A. Correct. You must set the Target property of the Label control to the control that is the target of the mnemonic key. B. Incorrect. The Label control and the Target property can be in different containers. C. Correct. You specify the mnemonic key by preceding it with an underscore symbol. D. Incorrect. There is no MnemonicKey property. 3. Correct Answer: B A. Incorrect. You must use the Grid.Column and Grid.Row attached properties to designate the cell for controls. B. Correct. This example shows the correct use of the attached Grid.Column and Grid.Row properties. C. Incorrect. No Grid.Column or Grid.Row values are set for this button. D. Incorrect. You must use the Grid.Column and Grid.Row attached properties to designate the cell for controls.
Lesson 2 1. Correct Answer: D A. Incorrect. Context menus cannot be part of a visual tree. B. Incorrect. This structure would require that the context menu be a visual child of a higher-level element, and context menus cannot be part of a visual tree. C. Incorrect. Context menus must be an instance of the ContextMenu class, not the Menu class. D. Correct. The context menu must be defined as a property of the control it is associated with.
Answers
479
2. Correct Answer: C A. Incorrect. Item controls can contain a list of items, the number of which has no defined limit. B. Incorrect. Item controls can contain a list of items, the number of which has no defined limit. C. Correct. Item controls can contain a list of items, the number of which has no defined limit. D. Incorrect. All item controls can contain a list of items, the number of which has no defined limit. 3. Correct Answer: B A. Incorrect. Though this example does demonstrate replacing the ItemPanel with a VirtualizingStackPanel, virtualization could be turned off by internal processes because VirtualizingStackPanel is not set to True. B. Correct. This example uses VirtualizingStackPanel to implement virtualization. C. Incorrect. TreeView does not virtualize by default. D. Incorrect. TreeView does not virtualize by default.
Lesson 3 1. Correct Answer: C A. Incorrect. While you can use a Grid control to create an evenly spaced control layout, UniformGrid requires much less work to implement this kind of layout. B. Incorrect. While you can use a Canvas control to create an evenly spaced control layout, UniformGrid requires much less work to implement this kind of layout. C. Correct. The UniformGrid control automatically lays out controls in an equidistant manner, irrespective of control Margin properties. D. Incorrect. WrapPanel lays out controls in a horizontal wrapping layout, and does not guarantee equidistance. 2. Correct Answers: A and C A. Correct. With the Canvas.Right and Canvas.Bottom properties set to 0 and the Margin property set to 20, the Button control maintains 20 units between the Button edges and the right and bottom Canvas edges.
480
Answers
B. Incorrect. Because the Canvas.Right and Canvas.Bottom properties are not set, the Button location will default to the upper left corner. C. Correct. Using absolute positioning with the Canvas.Top, Canvas.Left, Canvas.Right, and Canvas.Bottom attached properties is the preferred way to handle control position when using the Canvas control. D. Incorrect. Because the margin is set to 20 and the Canvas.Right and Canvas.Bottom properties are also set to 20, the control edges actually are 40 units from the edges of the Canvas control.
Case Scenario 1: Streaming Stock Quotes Because the Stock Quote control needs linear space but not two-dimensional space, a StatusBar control is a natural choice to display this information. Controls used to configure the application could be hosted in a ToolBar control. A couple of different layout controls could be used to create the layout. A grid is an obvious choice—by creating multiple grid rows, you can control where and how each control is positioned. A possibly better choice would be DockPanel. By setting DockPanel.LastChildFill to True and adding the chart control last, you can dock the ToolBar and StatusBar controls to the edges, fill the remaining space with the chart, and even allow the application layout to be reconfigured to dock the ToolBar to different edges at run time.
Case Scenario 2: The Stock Watcher The main interface should be built on a grid. The list can be hosted in one grid column, which can be made resizable by the user with the GridSplitter control. The obvious choice for the main user interface is the UniformGrid control, which automatically controls layout as multiple copies of the stock interface are added.
Chapter 4: Lesson Review Answers Lesson 1 1. Correct Answer: B A. Incorrect. To keep the coordinates of the rendered control the same, you must set the RenderTransformOrigin property to .5, .5. B. Correct. You must set the ScaleY property to –1 and the RenderTransformOrigin property to .5, .5 to achieve the specified effect.
Answers
481
C. Incorrect. You must set the ScaleY property to –1, not the ScaleX property. In addition, to keep the coordinates of the rendered control the same, you must set the RenderTransformOrigin property to .5, .5. D. Incorrect. You must set the ScaleY property to –1, not the ScaleX property. 2. Correct Answer: D A. Incorrect. When the StartPoint and EndPoint properties are not set, they default to (0,0) and (1,1), respectively, which creates a diagonal gradient. B. Incorrect. This example creates a horizontal gradient. C. Incorrect. This example creates a horizontal gradient. In addition, it fades from yellow to red, not red to yellow. D. Correct. With a StartPoint of (1,0) and an EndPoint of (1,1) the gradient is vertical.
Lesson 2 1. Correct Answer: C A. Incorrect. Because MediaElement elements do not throw exceptions, a Try… Catch block never detects them. B. Incorrect. Although MediaElement elements do not throw exceptions, ignoring a failure of media to load would create a confusing and frustrating user experience at best. C. Correct. Even through MediaElement does not throw exceptions, you can access the original exception that is thrown from the media failure through the event arguments. Then you can notify the user or take any other appropriate action. D. Incorrect. The MediaOpened event is raised only after media has been opened, thus it will be raised only when there is no problem. 2. Correct Answer: D A. Incorrect. SoundPlayer can be a good choice, but if your application needs to control volume or balance, these elements are inaccessible. B. Incorrect. MediaPlayer can be a good choice, but if you need to play sound as an action or want a lightweight sound element, SoundPlayer is a better option. C. Incorrect. Although MediaElement plays sound files, MediaPlayer or SoundPlayer is a better choice because neither one has a visual representation.
482
Answers
D. Correct. Depending on application requirements, either SoundPlayer or MediaPlayer can be a good choice.
Lesson 3 1. Correct Answer: A A. Correct. This is a correctly formatted pack URI. B. Incorrect. In this URI, component and MyFolder are transposed from their correct locations. C. Incorrect. The assembly name must come before the semicolon. D. Incorrect. In this URI, the folder name is not specified. 2. Correct Answer: B A. Incorrect. The EmbeddedResource setting embeds the image using a different resource handling technology, making it more difficult to access. B. Correct. The Resource setting makes this image accessible to the Image control through pack URI syntax. C. Incorrect. With the None setting, the file is neither embedded nor copied into the output directory. D. Incorrect. The Content setting does not embed the file; rather, it copies it as a loose file. 3. Correct Answer: D A. Incorrect. MediaElement cannot access resources of any kind. B. Incorrect. MediaElement cannot access resources of any kind. C. Incorrect. With the None setting, the file is neither embedded nor copied into the output directory. D. Correct. The Content setting allows MediaElement to access the files. You also should set the Copy To Output Directory property to Copy Always.
Lesson 4 1. Correct Answer: C A. Incorrect. This snippet shows default Image behavior. B. Incorrect. This snippet might result in unfilled space in the image area, depending on the image’s size and shape.
Answers
483
C. Correct. This snippet gives the correct behavior. D. Incorrect. This snippet allows the image to be shrunk. 2. Correct Answer: D A. Incorrect. You must wrap a graphic in a GeometryDrawing object and then in a DrawingImage object to convert it to an image. B. Incorrect. You must wrap a graphic in a GeometryDrawing object and then in a DrawingImage object to convert it to an image. C. Incorrect. You must wrap a graphic in a GeometryDrawing object and then in a DrawingImage object to convert it to an image. D. Correct. You must wrap a graphic in a GeometryDrawing object and then in a DrawingImage object to convert it to an image.
Case Scenario 1: The Company with Questionable Taste Obviously, the stark black-and-white of the background is not a problem. That is simply a matter of setting the correct properties. The tie-dyed user input is somewhat more challenging, but that should not be a problem either. To create this effect, you should develop a RadialGradientBrush that achieves the right mix of colors and set the Foreground property of all user input controls to this brush. The video in the shape of a dancing bear can be achieved by adding a MediaElement to the application and setting the Clip property to the Path object supplied by the art department. Because the client wants continuous footage, you should handle the MediaElement.MediaEnded event and supply code to restart the media file when the end is reached.
Case Scenario 2: The Image Reception Desk We can use BitmapDecoder to extract metadata about the images. From there, it is a simple matter to catalog the images in our database with the relevant metadata. For the slideshow application, the appropriate resource strategy is not to embed them, but rather to make them available as content files. Because the images are to be updated every week but the application will be widely distributed, embedding images as resources would require that the application be recompiled each time the images changed. By not embedding them, the application can load them directly from the address on the file server.
484
Answers
Chapter 5: Lesson Review Answers Lesson 1 1. Correct Answers: A and D A. Correct. The ElementName property establishes the source object and the Path property establishes the source property for that object. B. Incorrect. While setting the DataContext property establishes the source object, you still must define the path to the source property for that object by setting the Path property (or by other means, as discussed in Lesson 2 of this chapter). C. Incorrect. While setting the Source property establishes the source object, you still must define the path to the source property for that object by setting the Path property (or by other means, as discussed in Lesson 2 of this chapter). D. Correct. The RelativeSource property identifies the source object and the path property indicates the path to the source property. 2. Correct Answer: B A. Incorrect. A binding mode of OneTime does not update the user interface with any changes made to the database while the application is running. B. Correct. This mode allows the user to view the data but not change it. C. Incorrect. This mode allows the user to update the data source. D. Incorrect. When the need is explicit, it is best to declare the binding mode explicitly rather than relying on defaults. 3. Correct Answer: D A. Incorrect. Because this application requires a specialized update procedure, using the default setting is not the best practice. B. Incorrect. This setting would update the source whenever it changed, rendering it unacceptable for the scenario described. C. Incorrect. This setting updates the data source whenever the control loses focus, which is unacceptable for the scenario described. D. Correct. This setting updates the source only when explicitly called.
Answers
485
Lesson 2 1. Correct Answer: C A. Incorrect. The DataContext property is set incorrectly to the data set, not to the data table. B. Incorrect. The Path property does not recognize the data set named mySet. C. Correct. The DataContext is set correctly to the Customers table and the Display MemberPath property is set to the correct property. D. Incorrect. The DisplayMemberPath property is set incorrectly. 2. Correct Answer: C A. Incorrect. The Path property must be set to the CustomersOrders/OrdersDetails relation. B. Incorrect. The Path property must be set to the CustomersOrders/OrdersDetails relation. C. Correct. The Path is set correctly to the CustomersOrders/OrdersDetails relation. D. Incorrect. The Path property incorrectly includes the Customers table. Because the DataContext is set to the Customers table already, the Path property incorrectly looks for a Customers table within the Customers table.
Lesson 3 1. Correct Answer: D A. Incorrect. The tags must be enclosed in tags to set the data template to the ItemTemplate property. B. Incorrect. The data template must be set to the ItemTemplate property, not the ItemsSource property. C. Incorrect. The data template must be enclosed in tags. D. Correct. The data template is correctly set. 2. Correct Answer: A A. Correct. You set the Filter object to a Predicate that specifies a filtering method that returns a Boolean value. B. Incorrect. The specified method must return a Boolean value.
486
Answers
C. Incorrect. You must set the Filter property, not the CustomFilter property. D. Incorrect. You must set the Filter property, not the CustomFilter property. In addition, the specified method must return a Boolean value.
Case Scenario 1: Getting Information from the Field Because the application needs to read XML flies in a queue, harnessing the power of XmlDataProvider seems like a logical choice. By specifying the file name, we can bind our controls to the XML files and view the data that way. Because the original XML files cannot be altered, we should use a binding mode of OneWay to keep any changes from being propagated back to the XML file. Once the data has been edited, it can be added to the central database manually using ADO.NET data access.
Case Scenario 2: Viewing Customer Data The first step is to separate the records that meet the criteria from the records that do not. To achieve this, we should create a complex filtering subroutine that examines each record and determines whether it is a member of any of the groups we are looking for. We should then create a custom grouping by implementing IValueConverter and group the records based on criteria group.
Chapter 6: Lesson Review Answers Lesson 1 1. Correct Answer: A A. Correct. The Content property is bound to the Date path, and the Background property also is bound to the Date path while setting the Converter property of that binding to the predefined static resource myConverter. B. Incorrect. Although the properties are bound correctly, you must designate the converter that returns a Brush object for the Background property. C. Incorrect. Although it is possible to define a resource in an element’s resource collection, you still must designate the converter that returns a Brush object for the Background property. In addition, the question assumes that the converter already has been defined as a resource in the Windows .Resources collection. D. Incorrect. Because the converter is a resource, you must refer to it using the {StaticResource} syntax.
Answers
487
2. Correct Answer: C A. Incorrect. To bind to a multi-value converter, you must use a MultiBinding object. B. Incorrect. To bind to a multi-value converter, you must use a MultiBinding object. C. Correct. This example correctly uses a MultiBinding object and the arguments are bound in the correct order. D. Incorrect. Although this example correctly uses a MutliBinding object, the arguments are bound in the incorrect order. Thus, the first member of the object array will be an integer, instead of a string as the converter expects.
Lesson 2 1. Correct Answers: A and D A. Correct. Exceptions never are surfaced to the user in databinding. B. Incorrect. The Validation.HasError property is set to True. C. Incorrect. A new ValidationError object is added to the Validation.Errors collection. D. Correct. Because the Binding.NotifyOnValidationError property is not set to True, the Validation.Error event is not raised. 2. Correct Answer: C A. Incorrect. The PropertyChanged event must be raised to implement data change notification. B. Incorrect. The PropertyChanged event is raised before the property actually changes, which can lead to unpredictable results. C. Correct. The PropertyChanged event should be raised after the property is changed. D. Incorrect. Because the property does not change in the getter, it is incorrect to raise the PropertyChanged event there. 3. Correct Answer: B A. Incorrect. While it is possible to implement a data object by inheriting from an ObservableCollection of Object types, this would require a lot of casting code and would make coding the application more prone to error. It is better to use an ObservableCollection of Employee objects.
488
Answers
B. Correct. Using an ObservableCollection of Employee objects is the best choice. C. Incorrect. While it is possible to implement a data object by declaring a member collection, this would require creating wrappers to access the collection’s members. The better choice is to inherit ObservableCollection. D. Incorrect. While it is possible to implement a data object by declaring a member collection, this would require creating wrappers to access the collection’s members. The better choice is to inherit ObservableCollection.
Case Scenario 1: The Currency Trading Review Console We can provide the currency conversion data by using a value converter. By creating an instance of the Web service in our value converter, we can access up-to-date exchange rates and convert the transaction from dollars to yen, pounds, euros, won, bhat, or any other currency we desire. Because we are not changing the culture of the thread, however, we are unable to use built-in string formatting to get culture-specific currency formatting. Thus, we have to implement our own currency formatting code. We can alert the users to trades that exceed the individual trader’s limits by creating a multi-value converter. This converter would evaluate the trader’s trade limit and the total value of the trade. If the value exceeded the limit, we could return a SolidColorBrush element of an alert color, such as red, and bind the background of our display control to that color, thus easily alerting the manager of the issue.
Case Scenario 2: Currency Trading Console We can implement this requirement through a validation rule. The validation rule would evaluate the identity of the individual trader logged into the console and the total value of the attempted trade. If the value of the trade exceeded the trader’s limit, a validation error would be raised and the trade would not be submitted to the database.
Chapter 7: Lesson Review Answers Lesson 1 1. Correct Answer: D A. Incorrect. Button1 does not reference or explicitly set a Style; thus, the Background is not affected by the Style that is defined in Window.Resources. B. Incorrect. Button1 does not reference or explicitly set a Style; thus, the Background is not affected by the Style that is defined in Window.Resources.
Answers
489
C. Incorrect. Button1 does not reference or explicitly set a Style; thus, the Background is not affected by the Style that is defined in Window.Resources. D. Correct. Button1 does not reference or explicitly set a Style; thus, the Background is not affected by the Style that is defined in Window.Resources. Therefore, the background remains the default. 2. Correct Answer: C A. Incorrect. A multi-trigger requires that all conditions be met before activating its Setters. B. Incorrect. A multi-trigger requires that all conditions be met before activating its Setters. C. Correct. A multi-trigger is active when all conditions defined in it are met. D. Incorrect. A multi-trigger requires that all conditions be met before activating its Setters. E. Incorrect. A multi-trigger is active when all conditions are met. 3. Correct Answer: C A. Incorrect. An explicitly set property always takes precedence over a property set by Setters or Triggers. Because the Content property is set explicitly, it displays Button. B. Incorrect. An explicitly set property always takes precedence over a property set by Setters or Triggers. Because the Content property is set explicitly, it displays Button. C. Correct. An explicitly set property always takes precedence over a property set by Setters or Triggers. Because the Content property is set explicitly, it displays Button. D. Incorrect. An explicitly set property always takes precedence over a property set by Setters or Triggers. Because the Content property is set explicitly, it displays Button.
Lesson 2 1. Correct Answer: D A. Incorrect. Because the RepeatBehavior property is set to a time span, it repeats for the duration of that time span. Because the span is 1 minute and the duration of the Animation is 15 seconds, it repeats three additional times after the first iteration.
490
Answers
B. Incorrect. Because the RepeatBehavior property is set to a time span, it repeats for the duration of that time span. Because the span is 1 minute and the duration of the Animation is 15 seconds, it repeats three additional times after the first iteration. C. Incorrect. Because the RepeatBehavior property is set to a time span, it repeats for the duration of that time span. Because the span is 1 minute and the duration of the Animation is 15 seconds, it repeats three additional times after the first iteration. D. Correct. Because the RepeatBehavior property is set to a time span, it repeats for the duration of that time span. Because the span is 1 minute and the duration of the Animation is 15 seconds, it repeats three additional times after the first iteration. 2. Correct Answer: D A. Incorrect. Because the default value for FillBehavior is HoldEnd, the final value of the Animation holds after this Animation has completed. Because both the To property and the By property are set, the value of the By property is ignored. B. Incorrect. Because the default value for FillBehavior is HoldEnd, the final value of the Animation holds after this Animation has completed. Because both the To property and the By property are set, the value of the By property is ignored. C. Incorrect. Because the default value for FillBehavior is HoldEnd, the final value of the Animation holds after this Animation has completed. Because both the To property and the By property are set, the value of the By property is ignored. D. Correct. Because the default value for FillBehavior is HoldEnd, the final value of the Animation holds after this Animation has completed. Because both the To property and the By property are set, the value of the By property is ignored.
Case Scenario 1: Cup Fever There are several possible approaches to this problem, but the one that seems to make the most sense is to create a Style for the elements in your user interface that defines the appropriate color scheme. The Style should incorporate Setters to set the color scheme of the object, and the value of the Setter objects should be bound to the value of the drop-down box. Using a custom converter, you can return the appropriate Brush objects for the Style based on the content in the drop-down box.
Answers
491
Case Scenario 2: A Far-Out User Interface We can implement this visual effect by using a few different Animations. First, we can animate the GradientOrigin property of the RadialGradientBrush so that the center of the radial gradient appears to move around the window. In addition, we can animate the Color properties of the individual GradientStop objects to create a continuously changing color effect. Finally, we can set the RepeatBehavior property of each animation to Forever, so the animations continue for the life of the program.
Chapter 8: Lesson Review Answers Lesson 1 1. Correct Answers: B and D A. Incorrect. The background of the WindowsFormsHost is never visible, and setting this property has no effect on the user interface. B. Correct. You can set properties for a hosted control in XAML just as you would for a WPF element. C. Incorrect. Even though it is named in XAML, the child of a WindowsFormsHost cannot be accessed directly in code. D. Correct. The correct way to gain a reference to a child of a WindowsFormsHost is to cast the Child to the appropriate type. 2. Correct Answer: C A. Incorrect. This mask requires all 10 digits. B. Incorrect. This mask makes all 10 digits optional. C. Correct. This mask correctly sets the first 3 digits to optional while requiring the other 7 digits. D. Incorrect. This mask requires the first 3 digits and sets the rest of the digits to optional.
Lesson 2 1. Correct Answers: C and D A. Incorrect. You must use either TemplateBinding or Binding to bind a property. B. Incorrect. When using Binding, you must set the Path property to the appropriate property name and somehow specify the source object.
492
Answers
C. Correct. TemplateBinding allows you to bind directly to a property on the TemplatedParent. D. Correct. You can specify the TemplatedParent as the RelativeSource and set the Path property to the appropriate value. 2. Correct Answer: A A. Correct. The Style element sets the target type to Label and includes a Setter that sets the Template property to the referenced template. The Style is defined after the Template in XAML so it can be parsed. B. Incorrect. A Style that sets the Template property, but which either has that Template defined inline or before the Style, is defined in the XAML code. C. Incorrect. The Style contains no Setter to set the template property. D. Incorrect. Templates are not applied automatically to elements without the aid of a Style.
Lesson 3 1. Correct Answer: C A. Incorrect. No preexisting Windows Forms controls have this functionality. B. Incorrect. Because no current WPF elements incorporate this functionality, a new template would not achieve the desired effect. C. Correct. Because the visual interface can be achieved with existing WPF elements, the best choice is to create a user control that binds together the preexisting WPF elements with custom functionality. D. Incorrect. Although you could achieve your goals with a custom control, creating a completely new template is unnecessary, and thus creating a user control is a better choice. 2. Correct Answer: D A. Incorrect. No preexisting Windows Forms controls have this functionality. B. Incorrect. Because no current WPF controls incorporate this functionality, a new template would not achieve the desired effect. C. Incorrect. Because the visual interface cannot be achieved with existing WPF elements, a user control is not the best choice. D. Correct. Because this custom element requires both custom visual effects and custom functionality, you should create a custom control.
Answers
493
3. Correct Answers: A, C, and D A. Correct. Each theme needs to have a separate template in an appropriately named file. B. Incorrect. All templates should exist in the Themes folder or in the appropriate external folder. C. Correct. The ThemeInfoAttribute must be set to point to the correct folders. D. Correct. You always must have a fallback template that is used if the chosen Theme is not supported.
Case Scenario 1: Full Support for Styles The most common approach in this situation would be simply to use the Windows Forms MaskedTextBox control hosted in a WindowsFormsHost. Unfortunately, because of the requirement that all elements be visually extensible, this is not an option in this case because Windows Forms controls cannot have Styles or Templates applied to them. The only other possible solution is to spend the developer hours implementing a WPF-based MaskedTextBox. By creating a custom control that inherits TextBox. we already have the presentation logic implemented and we can focus on developing the mask engine and incorporating it into the new control.
Case Scenario 2: The Pizza Progress Bar Because the control that the client is requesting is essentially a ProgressBar with an alternative appearance, the best approach to take is to build a new control template. We can create the disappearing-pizza effect by starting with an image of a pizza in the background and then, as the Value property goes from the minimum to the maximum, gradually overlaying a partially filled ellipse. We can implement this functionality by creating a custom converter that converts the current value of the progress bar to a fraction of 360 degrees and by painting the appropriate filled arc over the pizza image, making it seem to disappear.
Chapter 9: Lesson Review Answers Lesson 1 1. Correct Answer: D A. Incorrect. Because these Brush objects need to be used in many different elements, defining them at the individual element level would require a great deal of redundancy.
494
Answers
B. Incorrect. Because these Brush objects need to be used in each window, defining them at the window level would be redundant. C. Incorrect. Although defining these resources at the application level would enable use across the entire application, it would not facilitate reuse across multiple applications. D. Correct. By defining your resources in a resource dictionary, you can share the file between applications as well as import the resources contained therein at the window or application level as needed. 2. Correct Answer: D A. Incorrect. Because the Foreground is bound to the resource as a dynamic resource, it detects when the resource changes and the Foreground turns green. Also, because the Background is bound as a static resource and a property on the Brush that paints the Background is changed, the resource detects that change through change notification and the Background turns black. B. Incorrect. Because the Foreground is bound to the resource as a dynamic resource, it detects when the resource changes and the Foreground turns green in addition to the Background turning black. C. Incorrect. Because the Background is bound as a static resource and a property on the Brush that paints the Background is changed, the resource detects that change through change notification and the Background turns black in addition to the Foreground turning green. D. Correct. Because the Foreground is bound to the resource as a dynamic resource, it detects when the resource changes and the Foreground turns green. Also, because the Background is bound as a static resource and a property on the Brush that paints the Background is changed, the resource detects that change through change notification and the Background turns black.
Lesson 2 1. Correct Answer: B A. Incorrect. Although the LineBreak element inserts the line break, the spaces in the first line would be collapsed because white space is not preserved. B. Correct. With xml:space="preserve" set, white space, which includes extra spaces and line breaks, is preserved.
Answers
495
C. Incorrect. Because white space is preserved already due to the xml:space= "preserve" attribute, adding the LineBreak element inserts an additional line break. D. Incorrect. White space is not preserved by default, so the additional spaces and line break would be collapsed in this example. 2. Correct Answers: A, B, and C A. Correct. Ctrl+P is wired directly to the ApplicationCommands.Print command, which prints the document. B. Correct. The ApplicationCommands.Print command invokes printing for the document. C. Correct. The FlowDocumentReader, as well as other flow document containers, exposes built-in functionality for printing the contained documents. D. Incorrect. The PrintVisual command prints the visual appearance of the element, which includes the toolbar at the bottom of the window. To use the PrintDialog class to print a document, you should call the PrintDocument method.
Lesson 3 1. Correct Answers: A and B A. Correct. You must add the UICulture element to the project file before you can extract localizable resources. B. Correct. You must mark localizable elements with the x:Uid attribute before they can be extracted. C. Incorrect. Although you must create a folder that represents the culture to which you are localizing, you do not need to create it before extracting localizable resources. D. Incorrect. If the culture that you are localizing from is the neutral culture, this folder is created automatically. In any case, you are not required to designate a neutral culture nor create a subfolder that represents it. 2. Correct Answer: C A. Incorrect. The output folder must have the same name as the culture for which resources are being compiled. B. Incorrect. This example does not specify the culture.
496
Answers
C. Correct. This example correctly specifies the output folder and the culture. D. Incorrect. This example does not specify the culture and sets the output folder to fr-FR, not fr-CA.
Case Scenario: Help for the Beta We can create an easily updatable Help system for our betas by using WPF flow documents. While they probably are not as full-featured as a compiled Help solution, they have the key aspects we need, including the ability to be updated quickly. We can create a Help window as a window of the application that displays as a flow document. We can incorporate the Help flow documents as a resource directory in the application. When we update beta versions, we can update the resource file and thereby always ensure that the correct Help is loaded. Because resource directories are localizable, we can use the WPF localization features to extract the translatable parts of the Help files and create satellite assemblies so that culture-appropriate help is loaded. By using one of the standard flow document containers, such as FlowDocumentReader, to display the Help file, we ensure a professional looking Help experience with built-in support for printing.
Chapter 10: Lesson Review Answers Lesson 1 1. Correct Answer: B A. Incorrect. The File Association Editor allows you to create file associations in Setup projects. B. Correct. Setup projects must be deployed to full-trust environments. C. Incorrect. The File System Editor allows you to create new folders on the target machine. D. Incorrect. The Registry Editor allows you to write values to the registry. 2. Correct Answers: A and C A. Correct. To associate an icon with your application shortcut, you first must add it to your Setup project. B. Incorrect. You do not need to add your icon as an embedded resource. C. Correct. The Icon property of the appropriate shortcut must be set to the desired icon in the File System Editor. D. Incorrect. You do not need to add your icon as a resource.
Answers
497
Lesson 2 1. Correct Answer: C A. Incorrect. Applications and XBAPs that run from a Web page execute under the Internet security zone by default. B. Incorrect. Applications and XBAPs that run from a Web page execute under the Internet security zone by default. C. Correct. Applications and XBAPs that run from a Web page execute under the Internet security zone by default. D. Correct. Applications and XBAPs that run from a Web page execute under the Internet security zone by default. 2. Correct Answers: A and C A. Correct. Isolated storage provides a safe way to persist data to the file system in a partial-trust environment. B. Incorrect. Web service access typically is curtailed in a partial-trust environment, so this strategy cannot be relied on to give acceptable results. C. Correct. Because user settings are persisted safely in a partial-trust environment, this strategy can be used if the data to be persisted is of an appropriate size and format for settings. D. Incorrect. A permission demand is used to check for permission, not to gain permission temporarily. 3. Correct Answers: A and D A. Correct. This example demonstrates correct usage of the synchronous methods CheckForUpdate and Update. B. Incorrect. CheckForUpdateAsync does not return a value but rather uses a callback. Thus, in this example, the call to aDep.CheckForUpdateAsync produces an error because it cannot be evaluated as a value. C. Incorrect. Because the CheckForUpdate application is a synchronous method, it returns a Boolean value that must be evaluated. In this example, CheckForUpdate is called and returned, but the expression never is evaluated and no subsequent code path leads to the installation of updates. D. Correct. This example correctly demonstrates the use of the asynchronous methods CheckForUpdateAsync and Update Async.
498
Answers
Case Scenario: Buggy Beta Using ClickOnce technology, we can provide the version of the application that is available now through a Web site that our clients can access. By setting the application to check for updates every time it is run, we can ensure that our clients receive the most up-to-date version every time they run the application.
Glossary Access Key
A key defined by a Button that clicks the button when pressed in combination with the ALT key.
Action
An object that controls the execution of a Storyboard object.
Ancestor
An object in the visual tree at a position above the current object.
Animation
An object that describes a change in value over time. The value change can be gradual, or it can be discrete, as in the case of some Animations that use key frames. An Animation typically is used as part of a Storyboard to change the value of a specified property temporarily over a period of time.
Application Manifest
In ClickOnce applications, the file that describes the files and dependences required by a particular application.
Application Setting
A Setting that is scoped for the entire application and is read-only
at run time. BackgroundWorker
A component that handles multithreading.
Binary Resource A resource that is compiled into an application but is distinct from
the executable code. Binary resources are different from logical resources which are defined in code files. Binding
The object in WPF that binds the value of a target property to the value of a source property.
Brush
An object that paints part of the user interface. Brushes can be solid or can represent a variety of visual effects.
Bubbling Event A Routed Event that is raised first by the originating element and
then each higher member of the visual tree until it is finally raised by the topmost element. Certificate
A file issued by a certificate authority that guarantees the identity of an application’s publisher.
ClickOnce
An agile development technology that allows you to publish your application to a variety of publishing site types.
Command A high-level application task that can be associated with input gestures or
controls and command handlers. 499
500
Glossary
Command Handler A method that is associated with a Command and executed when
the Command is invoked. Content Control Control Template
A control that can contain a single nested WPF element. The XAML code that defines the visual appearance of a WPF control.
Converter
An object that is used to convert an object of one type into another object, usually of a different type.
Custom Control
A user-defined, stand-alone WPF control whose appearance is specified by a user-defined control template.
DataContext A property that, when set on an element, sets the data source object for
all objects in that element’s visual tree. Dependency Property
A WPF property that incorporates built-in functionality for WPF data binding, property value inheritance, animation, and change notification.
Direct Event
A Routed Event that is raised only by the originating element.
Dispatcher
A static class that enables cross-thread method invocation.
Event Handler
A method that is associated with a particular event and is executed when that event is raised.
File System Editor
The primary editor for Setup projects, which allows you to configure the file system on the target computer.
Flow Document
A document defined in XAML and automatically laid out in a flow
container. Freezable
Freezable objects are read-write until the Freeze method is called, after which they become read-only.
Gesture
An input action such as a keystroke combination or mouse action that can be set to invoke a Command.
Item Control A WPF control that can contain a list of nested WPF elements. Journal In a Page-based application such as a Navigation application or an XBAP, the
Journal is the record of sites that have been visited. Key Frame
An object that represents a discrete value as a point in an Animation.
Layout Panel
A WPF control that can contain multiple nested WPF controls and provides logic for the layout of those controls.
Glossary
Localization
501
The process of preparing an application for use in multiple cultures.
Logical Resource An object defined in a Resources collection that is available for use by
elements in your application. Mnemonic Key A key defined in a Label control that shifts the focus to an associated
control when pressed with the ALT key. Multi-value Converter An object that converts multiple objects into a single object. Navigation Application A WPF application that is designed to use page navigation
and uses Page objects as the primary unit of the user interface. NavigationService
The class that exposes a variety of navigation-related functionality
for a Page. ObjectDataProvider
A class that allows you to bind a WPF element or property to a method called on an object.
ObservableCollection A generic collection type that implements built-in change noti-
fication for the collection. Pack URI The URI syntax that is used to access binary resources. PageFunction Resource
A class that acts like a Page, but can return a value.
In WPF, usually refers to a logical resource. See also binary resource.
Resource Dictionary
A separate XAML file that defines a resource collection.
Routed Event
A WPF event that can be raised by objects other than the element that it originates in.
Satellite Assembly
An assembly that holds culture-specific resources for a localized
application. Setter Setting
An object that can set a specified property to a specified value. A value that is stored externally to the application and can be read at run time.
Setup Project
A project created by Windows Installer that is designed to install another application on a local computer.
Shape
A drawing primitive that represents a geometric shape.
Storyboard
An object that contains a collection of Animation objects and applies them to target properties.
502
Glossary
Style An object that contains collections of Setter and Trigger objects and can be
applied to an element to create a visual appearance. Transformation
A mathematical process applied to a visual element that changes the way it is rendered. Also known as transform.
Trigger
An object that contains a collection of Setter objects, which are applied when the Trigger condition is met.
Tunneling Event
A Routed Event that is raised first by the topmost member of the Visual tree, and then by each successive member until it is finally raised by the originating element.
User Control A user-defined WPF control that is composed of multiple preexisting
WPF controls bound together by a common functionality. User Setting A Setting that is scoped for each user and is read-write at run time. Windows Application
A WPF application that uses Windows as the primary unit of the user interface.
XAML
XML-based Application Markup Language—the language in which the user interface for WPF applications is defined.
XBAP
XAML Browser Application. A WPF application that is designed to be run in Internet Explorer under partial-trust conditions.
XmlDataProvider
A class that allows you to bind WPF elements to XML data.
XPS Document A fixed, read-only document that conforms to the XML Paper
Specification (XPS).
Index Symbols and Numbers .csv file, 430 .msi files, 445 .NET Framework, 288 application events, 67 commands, 72–73 event architecture, 59 Navigation applications, 9 tunneling events, 61 .NET properties, 373 .wav files, 176–79 .xaml files Application, 393 Classic.xaml, 379 Generic.xaml, 376, 379
A AccelerationRatio, 324, 331 access control file system, 11–12, 14 objects, 48–49 registry, 11, 14 XBAPs, 11, 14 access keys, 104 Action, 315–16, 327–29, 335–36 Activated events, 67 AddBackEntry, 26 AddExtension, 346 AddHandler, 28 ADO.NET, 213, 226–29 Aero.NormalColor.xaml, 379 aligning content, 144–45 AllowsTransparency, 5, 7 alphanumeric characters, 352 Alt key, 102–4, 120 ancestor properties, 214–15 AncestorLevel, 214 AncestorType, 214 Anchor, 350 animation, 303, 316, 323–24 case scenarios, 340–41 coding, 335–36 dependency properties, 373 key frames, 323, 333–35 lab, animation of controls, 336–37 non-double types, 332–34 playback timelines, 330–35
properties, 324–25 Triggers, 327–30 Animation, 323–24, 326, 330–35, 363 Application Folder, 446 Application Manifest, 461–63 Application objects, 66–67 Application property, 87 application tasks. See commands Application.Find, 74 Application.GetResourceStream, 189 Application.Resources, 393, 396 Application.Startup, 431 Application.xaml, 393 ApplicationCommands, 73 ApplicationCommands.Print, 419 ApplicationDeployment, 456 ApplicationName, 198 applications, 1 binary resources, 187 content files, 190 embedding, 187, 191–92 loading, 188–89 retrieving, 189–91 communication between, 57 deploying, 3, 11, 441, 443–44 Application Manifest, 461–63 case scenario, 470 Certificates, 463–64 ClickOnce, 451–58, 464–65 Setup projects, 443, 448–49 Windows Installer, 444–48 XBAPs, 458–61 downloading, 3 events, 66–68 journal, 24 localizing, 426–28 case scenario, 439 elements, 428–29 extracting content, 429–30 lab, practice with, 433–35 resources, 431 subdirectories, culture codes, 430–31 translating content, 430 UICulture attributes, 428 validators and converters, 432 logical resources, 393 navigating through, 9 Navigation, 3, 9–10, 444
503
504
architecture
Page-based, 411 performance, 394 responsiveness, 41 selection of, 14 settings, 86–91 shopping cart, 32 Windows, 3–4, 444 creating, 4–5 displaying, 8–9 lab, creating, 15–16 properties, 5–7 Windows Forms, 3, 5 XBAPs, 3, 11–13, 444 architecture command, 73 intra-application communication, 57 Argument, 43 Assembly, 379 AssemblyInfo.cs, 379 AssemblyInfo.vb, 379 asynchronous processing, 41–42, 47–48 attached events, 62–63 attached properties, 110, 134, 143 attributes, serializable, 26 audio lab, creating a media player, 183–85 MediaElement, 179–82 MediaPlayer, 179–82 SoundPlayer, 176–79 Author, 198 AutoReverse, 324, 331
B Back buttons, 9 Background, 5, 155, 212, 403, 428–29 background processing, 42–43. See also BackgroundWorker cancelling, 45–46 changing threads, 47–48 operation cancellation, 42 operation completion, 42 parameters, 43–44 progress reporting, 46 returning values, 44 background, window color, 157–60 BackgroundWorker, 41–42, 45–46, 49–51 BackgroundWorker.ProgressChanged, 46 Balance, 179 BaseOn, 306, 311 BeginAnimation, 335–36 BeginInvoke, 47–48 BeginStoryboard, 328
BeginStoryboardName, 329 BeginTime, 324 binary resources, 187 content files, 190 embedding, 187, 191–92 loading, 188–89 retrieving, 189–91 Binding class, 207, 209–11, 285, 364–65 ADO.NET object binding, 226–27 Binding.Mode, 215–16 case scenarios, 256–57 data filtering, 246–48 grouping, 243–46 sorting, 241–43 templates, 238–41 elements, binding to, 211–12 hierarchical data, binding, 228–29 labs data templates and groups, 248–51 database access, 232–35 practice with, 217–18 lists, binding to, 221–26 ObjectDataProvider, 230–31 objects, binding to, 212–15 UpdateSourceTrigger, 216–17 validation rules, 282–83 XmlDataProvider, 231–32 Binding property, 315 Binding.Mode, 215–16 BindingInError, 285 BindingListCollectionView, 225 BindingListCollectionView.CustomFilter, 247–48 bindings, command, 75–78 BitmapDecoder, 199 BitmapDecoder.Create, 199 BitmapFrame, 199 BitmapImage, 199 BitmapImage.Metadata, 199 BitmapMetadata, 198–99 block elements, 401 BlockUIContainer, 409–10, 415 flow documents, 402–4 List, 406 Paragraph, 405 Section, 409 Table, 407–8 BlockUIContainer, 409–10, 415 BlockUIElement, 412–13 Blue channel, 156 Bnzier curve, 334–35 Boolean?, 104 BorderBrush, 5, 155, 404
Command.CanExecute
BorderThickness, 5, 404 Both, 195 Box, 407 Brush, 5, 48–49, 155–56, 196, 403–4 Brush.Freeze, 156 brushes, 155–56 bubbling events, 60, 63, 286 bubbling, commands, 76–77 BufferingProgress, 179 BuildAction, 187 built-in commands, 73 Button, 62–63, 101, 103–5 Back, 9 clipping, 171 databinding, 212 Forward, 9 lab, creating control templates, 367–69 Toolbar, 121 transforming, 170 XAML, 101–2 Button.Click event, 359–60 ButtonBase class, 104
C CameraManufacturer, 198 CameraModel, 198 CancelAsync, 42, 45–46 CancellationPending, 42, 45–46 CanExecute, 77–78 CanExecuteRoutedEventArgs, 77–78 CanGoBack, 24–25 CanGoForward, 24 CanMinimize, 6 CanResize, 6 CanResizeWithGrip, 6 Canvas controls, 101, 142–43 Canvas.ZIndex, 142–43 cart, shopping, 32 CAS (Code Access Security), 460 case scenario animation of controls, 340–41 controls, streaming stock quotes, 151 custom controls, 386 data conversion and validation, 301–2 databinding, 256–57 deploying applications, 470 designing user interfaces, 54 international business, 439 multimedia, 205 updating applications, 470 user input, validating, 95
user interface, user input, 96 CenterOwner, 7 CenterScreen, 7 Certificates, 463–64 change notification, 282, 287–94, 373, 394 channels, color, 156 characters, 351 Checkbox control, 104, 117, 121 CheckFileExists, 346 CheckForUpdate, 456 CheckForUpdateCompleted, 457 CheckForUpdateCompletedEventArgs, 457 CheckPathExists, 346 child controls, 4, 9, 143–44 Children, 326 Children.Add, 143–44 Children.Remove, 144 Chinese language, 426 Circle, 407 Classic.xaml, 379 Click, 62, 103–5 ClickOnce, 191, 443–44, 451–58, 464–65 Clip, 171 clipping, graphics, 171 Close, 8–9 Code Access Security (CAS), 460 code execution, 41, 59–61, 75 coding animations, 335–36 databinding, 211–13 styles, 309–10 collections, databinding, 223–26, 246–48 CollectionViewSource, 241 CollectionViewSource.GetDefaultView, 224–25 color, 348–49, 428–29 control, 108–9 gradients, 157–61 themes, 378–80 Color, 48, 158 Color.FromArgb, 157 ColorAnimation, 332 ColumnDefinitions, 133–37 columned page view, 416 columns, flow documents, 416 columns, grid, 133–38 CombinedGeometry, 166–68 ComboBox, 117–18, 121 ComboBox.Content, 118 ComboBox.Text, 118 Command, 73–74, 120 command handler, 73 Command.CanExecute, 77–78
505
506
Command.Execute
Command.Execute, 75 CommandBinding, 73, 76 commands, 57 architecture, 73 configuring, 72–73 bubbling, 76–77 custom, 78–80 disabling, 77 handlers and bindings, 73, 75–78 implementing, 73–74 invoking, 74–75 hyperlinks, 411 lab, creating custom, 80–83 menus, 119–21 Comment, 198 Compare, 242–43 compiling, embedded resources, 187 ComponentCommands, 73 compressed files (.wav), 176–79 Condition, 315 configuring application settings, 86–91 commands, 72–73 custom, 78–80 disabling, 77 handlers and bindings, 73, 75–78 implementing, 73–74 invoking, 74–75 databinding, 207–9 ADO.NET object binding, 226–27 Binding.Mode, 215–16 case scenarios, 256–57 data templates, 238–41 data, filtering, 246–48 data, grouping, 243–46 data, sorting, 241–43 elements, binding to, 211–12 hierarchical data, 228–29 lab, data templates and groups, 248–51 lab, database access, 232–35 lab, practice with, 217–18 lists, binding to, 221–26 ObjectDataProvider, 230–31 objects, binding to, 212–15 UpdateSourceTrigger, 216–17 XmlDataProvider, 231–32 events, 59–61 application level, 66–68 EventManager, 63 handlers, 62–63, 66 routed events, 61–62, 64–65, 68–69 lab, change notification and validation, 289–94 page-based navigation, 21
event handling, 27–30 hosting pages in frames, 21 hyperlinks, 22–23 journal, using, 25–27 NavigationService, 23–25 PageFunction objects, 30–32 simple, 32 structured, 32 using pages, 21 XBAPs, 11 constructor, 27 ConstructorParameters, 230 containers, flow document, 416 ContainerStyle, 244 ContainerStyleSelector, 244 content adding, 153–54 binary resources, 187 content files, 190 embedding, 187, 191–92 loading resources, 188–89 retrieving, 189–91 graphics, 155 brushes, 155–56 clipping, 171 Ellipse, 164–65 hit testing, 171–72 ImageBrush, 161–62 lab, practice with, 172–73 Line, 165 LinearGradientBrush, 157–60 Polygon, 165–68 Polyline, 165 RadialGradientBrush, 160–61 Rectangle, 164–65 shapes, 163–64 SolidColorBrush, 156–57 Transforms, 168–70 VisualBrush, 163 images, 194 bitmap metadata, 198–99 lab, practice with, 200–1 stretching and sizing, 194–96 transforming graphics, 196–98 managing, 153–54 multimedia case scenarios, 205 lab, creating a media player, 183–85 MediaElement, 179–82, 190 MediaPlayer, 179–82, 190, 196 media-specific event handling, 182–83 SoundPlayer, 176–79 Content, 101–2, 211–13, 360
creating
content controls, 101–5, 111, 372 ContentControl, 4, 101–5, 372 ContentPresenter, 360–61 ContentTemplate, 240 ContentType, 189 ContextMenu, 119, 121, 124 Control class, 372 control containment heirarchy, 59–60 control templates, 359 creating, 367–69, 378–79 part names, predefined, 366 source code, 366 Styles, 365 templated parent properties, 363–65 Triggers, 362–63 Control.ContextMenu, 121 Control.Triggers, 313 controls, 101. See also content controls; individual control names animation of controls, 336–37 case scenarios animation of controls, 340–41 international business, 439 streaming stock quotes, 151 commands, associating with, 74 control templates, 359–62 lab, creating, 367–69 part names, predefined, 366 source code, 366 Styles, 365 templated parent properties, 363–65 Triggers, 362–63 custom, 378 case scenario, 386 choosing, 373 consuming controls, 377 creating, 372, 376–77 dependency properties, 373–75 lab, creating custom controls, 380–83 selecting, 373 theme-based appearance, 378–80 user controls, 376 item controls binding to lists, 221–23 ComboBox, 117–18, 121 ContextMenu, 119, 121, 124 lab, practice with, 124–26 ListBox, 101, 116–17, 121, 124 menus, 119–21 StatusBar, 123 Toolbar, 119, 121–23 TreeView, 101, 118–19 virtualization, 123–24
layout controls, 4, 101, 130–31 aligning content, 144–45 Canvas, 101, 142–43 child elements, accessing, 143–44 DockPanel, 139–42, 146–48 Grid, 101, 110, 131–37 HorizontalAlignment, 131–33, 135, 138 lab, practice with, 146–48 Margin, 131–33, 135 StackPanel, 101, 123–24, 131–32, 138 UniformGrid, 137–38 VerticalAlignment, 131–33, 136 WrapPanel, 139 menus, 121–22 Navigation applications, 9 Page objects, 9 styles, 309 tab order, 111 user, creating, 372 virtualization, 123–24 Windows Forms dialog boxes, 345–49 MaskedTextBox, 351–52 PropertyGrid, 353–54 WindowsFormsHost, 349–51 WindowsFormsHost, 349–51 ControlTemplate, 361–63 ControlTemplate.Triggers, 362–63 Convert, 245–46, 261–62, 273–76, 432 ConvertBack, 245–46, 261–62, 265, 273–76, 432 converting data, 261 bound data, formatting, 273 case scenario, 301–2 formatting, conditional, 268–69 IValueConverter, 261–64 lab, string and conditional formatting, 276–79 localizing data, 271, 432 multi-value converters, 273–76 objects, return, 268–69 string formatting, 264–67 Copyright, 198 CreatePrompt, 346 creating application settings, 87 commands, custom, 78–83 content, 153–54 control templates, 359–62, 367–69 data groups, custom, 245–46 data-based objects, 261 bound data, formatting, 273 case scenario, data conversion, 301–2 formatting, conditional, 268–69
507
508
Ctrl key
IValueConverter, 261–64 lab, string and conditional formatting, 276–79 localizing data, 271 multi-value converters, 273–76 returning objects, 268–69 string formatting, 264–67 dialog boxes, 8 event handlers, 28, 66–69 graphics, 155 brushes, 155–56, 196 clipping, 171 Ellipse, 164–65 hit testing, 171–72 ImageBrush, 161–62 lab, practice with, 172–73 Line, 165 LinearGradientBrush, 157–60 Polygon, 165–68 Polyline, 165 RadialGradientBrush, 160–61 Rectangle, 164–65 shapes, 163–64 SolidColorBrush, 156–57 Transforms, 168–70 VisualBrush, 163 labs control templates, 367–69 custom commands, 80–83 custom controls, 380–83 flow documents, 421–22 media player, 183–85 Navigation applications, 16–17 Setup project, 448–49 user interface, 111–12 Windows applications, 15–16 XBAPs, 17–19 Navigation applications, 10, 16–17 resource dictionary, 395–96 Setup projects, Windows Installer, 443, 448–49 styles, 308–10, 318–20 user controls, 372, 376 Windows applications, 4–5, 15–16 XBAPs, 11–12, 17–19 Ctrl key, 74 culture case scenario, localizing applications, 439 elements, localizable, 428–29 extracting content, 429–30 localizing, 426–28, 433–35 resources, 431 subdirectories, culture codes, 430–31 translating content, 430
UICulture attributes, 428 validators and converters, 432 Culture, 271 CultureInfo, 431–32 currency formats, 265, 352 CurrentDeployment, 456 CurrentItem, 224–26 CurrentPosition, 224 CurrentThread.CurrentUICulture, 431 CurrentUICulture, 426 Cursor, 5 Custom Actions Editor, 448 custom dialog boxes, 8. See also dialog boxes CustomContentState class, 26 CustomSort, 242–43 Cyrillic language, 426
D data Boolean, 104 change notification, 287–89 converting, 261 bound data, formatting, 273 case scenario, 301–2 formatting, conditional, 268–69 IValueConverter, 261–64 lab, string and conditional formatting, 276–79 localizing, 271 multi-value converters, 273–76 objects, return, 268–69 string formatting, 264–67 culture settings, 432 databinding, 207–9 ADO.NET object binding, 226–27 Binding.Mode, 215–16 case scenarios, 256–57 data templates, 238–41 data, grouping, 243–46 data, sorting, 241–43 elements, binding to, 211–12 filtering, 246–48 hierarchical data, 228–29 lab, data templates and groups, 248–51 lab, database access, 232–35 lab, practice binding, 217–18 lists, binding to, 221–26 ObjectDataProvider, 230–31 objects, binding to, 212–15 UpdateSourceTrigger, 216–17 XmlDataProvider, 231–32
documents
lab, online pizza ordering, 32–34 settings, 86–87 templates, 248–51 validation, 282 binding rules, 282–83 case scenario, 301–2 custom rules, 283–84 error handling, 284–87 ExceptionValidationRule, 283 lab, configuring, 289–94 ObservableCollection, 288–89 data triggers, 315 databases, 11, 14, 228–29 databinding, 207–9, 364–65 ADO.NET object binding, 226–27 Binding.Mode, 215–16 case scenarios, 256–57 change notification, 287–89 data templates, 238–41 dependency properties, 373 elements, binding to, 211–12 ExceptionValidationRule, 283 filtering data, 246–48 grouping data, 243–46 hierarchical data, 228–29 labs data templates and groups, 248–51 database access, 232–35 practice with, 217–18 lists, binding to, 221–26 Multibinding, 275–76 ObjectDataProvider, 230–31 objects, binding to, 212–15 sorting data, 241–43 UpdateSourceTrigger, 216–17 validation rules, 282–83 XmlDataProvider, 231–32 DataContext, 213–14, 226–27 DataRelation, 228–29 DataSet, 227 DataTable, 226–27 DataTrigger, 312 DataView, 247–48 date, formats, 267, 352 DateTake, 198 DateTime.ToString, 267 Deactivated events, 67 DecelerationRatio, 324, 331 Decimal, 407 decimal characters, 352, 432 Default Windows XP theme, 379 DefaultLocation, 446 delegates, 48
509
dependency properties, 373–75, 394 DependencyObject class, 172, 373 deploying applications, 441, 443–44 Application Manifest, 461–63 case scenario, 470 Certificates, 463–64 ClickOnce, 451–58, 464–65 downloading, 3 Setup projects, 443, 448–49 to server, 3 to Web site, 3 Windows Installer, 443–48 XBAPs, 11, 458–61 Designer, Visual Studio, 5 desktop applications, 2, 9, 190–91. See also applications dialog boxes, 14 creating custom, 8 file dialog boxes, 345–47 lab, practice with Windows Forms elements, 354–56 MaskedTextBox, 351–52 PropertyGrid, 353–54 WindowsFormsHost, 349–51 dictionaries, resource, 395–98 digital signatures, 463–64 digits, 351 direct events, 60 disabling commands, 77 Disc, 407 DiscreteKeyFrame, 334–35 Dispatcher, 41, 47–51 DispatcherPriority, 48 DispatcherUnhandledException, 67 DispatcherUnhandledExceptionEventArgs.Handled, 67 DisplayMemberPath, 221–22, 226, 241 DLL (Dynamic Link Library), 377, 418, 430 Dock, 139, 350 DockPanel, 139–42, 146–48 DockPanel.Dock, 139 Document, 231 DocumentPaginator, 420 documents, 401 flow documents, 401 block elements, 405–10 containers, 416 creating, 402–3, 421–22 formatting, 403–4 inline elements, 410–15 scaling text, 417 white space, 415 printing, 418–20 XPS documents, 418
510
DocumentViewer
DocumentViewer, 418–19 DoubleAnimationUsingPath, 324 downloading applications, 3, 107 DownloadProgress, 180 DownOnly, 195 DoWork, 41–44 DoWorkEventsArgs, 43–44 drag-and-drop functionality, 14 Drawing, 196–98 DrawingGroup, 196 DrawingImage, 197–98 Duration, 325, 331–32 Dynamic Link Library (DLL), 377, 418, 430 DynamicResource, 378, 393–95, 397
E e.Action, 286 e.Cancel, 30 e.CanExecute, 77–78 EditingCommands, 73 Element, 213, 217 ElementName, 209–11 elements. See also databinding; objects; Resources animation, 323–24 block elements, 401 BlockUIContainer, 409–10 flow documents, 401–4 List, 406 Paragraph, 405 Section, 409 Table, 407–8 control templates, 359–62 custom, 373 databinding, 211–12 file associations, 461–63 flipping, 170 inline elements, 401, 410 Bold, 410–11 Figure, 414–15 Floater, 412–14 flow documents, 401–4 Hyperlink, 411 InlineUIContainer, 415 Italic, 410–11 LineBreak, 411–12 Run, 410 Span, 412 Underline, 410–11 inline flow, 22 localization, 428–29 property setters, 306–7
styles, setting, 308 transforming, 170 Underline, 410–11 user interface (UI), 4, 72 visual, 163, 420 Ellipse, 164–65 EllipseGeometry, 166, 171 embedded files binary resources, 187 content files, 190 lab, using embedded resources, 191–92 loading, 188–89 retrieving, 189–91 English language, 426 EnterActions, 313 Error objects, 285 ErrorCondition, 283–84 ErrorContent, 285 ErrorException, 183 Esc key, 103 EvenOdd, 166 event handlers, 59–61 application level, 67–68 Application.Startup, 431 attaching, 62–63 commands, 72, 75–78 creating, 28, 66–68 Hyperlink, 411 lab, routed events, 68–69 media-specific, 182–83 setters, 307 Validation.Error, 285–87 Windows Forms controls, 350 XAML, 62 Event property, 307 event triggers, 315–16 EventArgs, 62 EventManager, 63, 66 events, 57 application level, 66–68 attached, 62–63 bubbling, 60, 63, 286 Button.Click, 359–60 Click, 103–5 configuring, 59–61 application-level events, 66–68 EventManager, 63 handlers, 62–63, 66 routed events, 64–65, 68–69 RoutedEventArgs, 61–62 defining, 64–65 direct, 60 DoWork, 41–44
formatting
EventManager, 63 lab, routed practice, 68–69 navigation, handling, 27–30 PageFunction, 30–32 raising, 65 registration, 63 ReturnEventArgs, 30–31 routed, 57, 61–62, 64–65, 68–69, 76–77, 183 setters, 307 tunneling, 60–61, 63 ValueChanged, 110 EventSetter, 307 EventTrigger, 313, 327 Exception, 285 ExceptionRoutedEventArgs, 183 exceptions, 48, 67, 183, 283 ExceptionValidationRule, 283 Exclude, 167 ExecutedRoutedEventArgs, 75–77 Exit, 67 ExitActions, 313 Extensible Application Markup Langugage (XAML), 1 attached properties, 110 binary resources, 190–91 Button controls, 101–2 Canvas, 142 ContextMenu, 121 custom commands, 80 event handlers, 62 ListBox controls, 116–17 menus, 119–21 multimedia formats, 179 resources, accessing, 393 TreeView controls, 118–19 extensions, filename, 346
F FallbackValue, 209 Figure, 414–15 file associations, 461–63 file system, 11–12, 14, 444 File System Editor, 445–46 FileName, 346 filename extensions, 346 filename filter, 346 FileNames, 346 files associations, 444 binary resources, 187 content files, 190
embedding, 187 lab, embedded resources, 191–92 loading, 188–89 loose files, retrieving, 189–91 retrieving manually, 189 overwriting, 347 sharing, 443–44 files downloading, 107 Files Of Type dialog box, 346 Files Type Editor, 448 Fill, 155 Polygon, 165 Shape, 164 Stretch, 162, 164, 195 FillBehavior, 325, 331 FillRule, 165, 167 Filter, 247, 346 filtering, 246–48, 346 FindAncestor, 214 FindResource, 396–97 flipping, elements, 170 FlipX, 163 FlipXY, 163 FlipY, 163 Floater, 412–14 flow documents, 401 block elements, 405–10 containers, 416 creating, 402–3, 421–22 formatting, 403–4 inline elements, 410–15 scaling text, 417 white space, 415 FlowDirection, 130, 138–39, 428–29 FlowDocumentPageViewer, 416–17, 419 FlowDocumentReader, 415–17, 419 FlowDocumentScrollContainer, 415 FlowDocumentScrollViewer, 416–17, 419 FlowPanel, 60 FontFamily, 403 FontSize, 403 FontStretch, 403 FontStyle, 403 FontWeight, 404 FontWidth, 428–29 Foreground, 5, 155, 403, 428–29 Forever, 331–32 FormatProvider, 352 formatting data, 261 bound data, 273 case scenario, data conversion, 301–2 conditional formatting, 268–69
511
512
Forward buttons
IValueConverter, 261–64 lab, string and conditional formatting, 276–79 localizing, 271 multi-value converters, 273–76 objects, return, 268–69 strings, 264–67 document text, 401–4 flow documents, 403–4 block elements, 405–10 containers, 416 creating, 421–22 inline elements, 410–15 scaling text, 417 white space, 415 XPS documents (XML Paper Standard), 418 Forward buttons, 9 fragment navigation, 23, 28 FragmentNavigation, 28 Frame control, 21, 199 frames, hosting pages in, 21 FrameworkPropertyMetadata, 375 Freezable class, 41, 48–49, 156, 364–65 French language, 427
G Generic.xaml, 376, 379 Geometry, 196–97 Geometry objects, 166–68, 171 GeometryCombineMode, 167 GeometryDrawing, 196–97 GeometryGroup, 166–67 gestures, 72, 74 GetContentState, 27 GetNavigationService, 23 GetRoutedEvents, 64 GetRoutedEventsForOwner, 64 GetValue, 375 GlyphRun, 196 GlyphRunDrawings, 196 GradientStop, 158, 160 graphic handles, 108–9 graphics. See also visual effects brushes, 155–56 clipping, 171 creating, 155 Ellipse, 164–65 freezable objects, 48–49 hit testing, 171–72 ImageBrush, 161–62 lab, practice with, 172–73 Line, 165 LinearGradientBrush, 157–60
managing, 153–54 Polygon, 165–68 Polyline, 165 RadialGradientBrush, 160–61 Rectangle, 164–65 shapes, 163–64 SolidColorBrush, 156–57 transforming to images, 196–98 Transforms, 168–70 VisualBrush, 163 Green channel, 156 Grid, 9, 132–37 attached properties, 110 block elements, 409–10 buttons, 62–63 databinding, 213 margins, 131–32 Grid.Column, 134 Grid.ColumnSpan, 134, 137 Grid.Row, 134 Grid.RowSpan, 135 GridSplitter, 134–37, 146–48 GroupHeader, 244 grouping, data, 243–46 GroupName property, 104 GroupStyle, 244
H Handled, 61, 63 Handler property, 307 handlers command, 73, 75–78 event, 59–61 application level, 67–68 Application.Startup, 431 attaching, 62–63 commands, 72, 75–78 creating, 28, 66–68 Hyperlink, 411 media-specific, 182–83 routed events, 68–69 setters, 307 Validation.Error, 285–87 Windows Forms controls, 350 XAML, 62 HasAudio, 180 HasVideo, 180 HeaderTemplate, 244 HeaderTemplateSelector, 244 Height, 5–6, 130 Grid, 133–37 Shape, 164
IsReadOnly
hexadecimal notation, 156 hierarchical data, 228–29 history, 3, 9. See also journals hit testing, 171–72 HitTestResult, 171–72 HitTestResult.VisualHit, 172 HorizontalAlignment, 131–33, 135, 138, 415 HorizontalAnchor, 414–15 HorizontalContentAlignment, 131 HorizontalOffset, 414 HorizontalScrollBar, 107 HTML (Hypertext Markup Language), 11, 22, 407 hyperlinks, 22–23, 411 Hypertext Markup Language (HTML), 11, 22, 407
I IBindingList, 225 ICollectionView, 246–48 ICollectionView.GroupDescriptions, 243 ICollectionViews, 224–26, 241–43 ICommandSource, 74 IComparer, 242–43 Icon, 6 icons, 447–48 IDE (integrated development environment), 4–5 IDocumentPaginatorSource, 420 IEnumerable, 225 IList, 225 Image, 106, 188–89, 194, 199, 413 Image.Stretch property, 106 ImageBrush, 161–62 ImageDrawing, 196 images binary resources, 187 content files, 190 embedding, 187, 191–92 loading, 188–89 loose files, retrieving, 189–91 bitmap metadata, 198–99 case scenarios, 205 display of, 106 Image element, 194 ImageBrush, 161–62 lab, practice with, 200–1 retrieving manually, 189 stretching and sizing, 194–96 transforming graphics, 196–98 ImageSource, 6, 161, 194, 199, 428–29 IMultiValueConverter, 273–76 index, 116, 143–44 individual controls, 101 information passing, navigation events, 29
inheritance, styles, 311–12, 317 InitialDirectory, 346 inline elements, 401, 410 Bold, 410–11 Figure, 414–15 Floater, 412–14 flow documents, 402–4 Hyperlink, 411 InlineUIContainer, 415 Italic, 410–11 LineBreak, 411–12 Run, 410 Span, 412 Underline, 410–11 inline flow elements, 22 INotifyPropertyChanged, 287 input gestures, 72, 74 InputGestures, 74 Installation Folder URL, 453 integrated development environment (IDE), 4–5 IntelliSense, 88 international business, 426–28 case scenario, 439 elements, localizable, 428–29 extracting content, 429–30 lab, localizing an application, 433–35 resources, 431 subdirectories, culture codes, 430–31 translating content, 430 UICulture attributes, 428 validators and converters, 432 Internet applications, 443–44 Internet Explorer, 11 Internet security zone, 14 Intersect, 167 Invoke, 47–48 IProvideCustomContentState interface, 26–27 IsAsynchronous, 230 IsBusy, 42 IsCancel, 103 IsChecked, 104 IsCurrentAfterLast, 224 IsCurrentBeforeFirst, 224 IsDefault, 103 IsDropDownOpen, 118 IsEditable, 118 IsEnabled, 6 IsLoadCompleted, 176 IsMainMenu property, 119 isolated storage, XBAPs, 12–13 IsolatedStorageFileStream class, 13 IsolateStorageFile class, 12 IsReadOnly, 107, 118
513
514
IsSynchronizedWithCurrentItem
IsSynchronizedWithCurrentItem, 222, 225–26, 228 IsToolBarVisible, 417 IsValid, 283–84 item controls, 101 binding to lists, 221–23 ComboBox, 117–18, 121 ContextMenu, 119, 121, 124 lab, practice with, 124–26 ListBox, 101, 116–17, 121, 124 menus, 119–21 StatusBar, 123 Toolbar, 119, 121–23 TreeView, 101, 118–19 virtualization, 123–24 ItemsPresenter, 361 ItemsSource, 222, 226 ItemsTemplate, 240 ItemTemplate, 222, 241 IValueConverter, 245–46, 261, 432
J JournalEntry, 25 JournalEntryName property, 26 journals, 3, 25 adding items, 26–27 lab, online pizza ordering, 34–38 Navigation applications, 9 NavigationService, 24 removing items, 25, 32
K Key, 241, 396 key frame-based animations, 323, 333–35 keyboard gestures, 74 keyboard shortcuts, 102–4, 120 KeyboardNavigation.IsTabStop, 111 KeySpline, 334 KeyTime, 333 Keywords, 199
L Label controls, 60, 101–3, 123 databinding, 211–13, 226 labs animation of controls, 336–37 BackgroundWorker, 49–51 change notification and validation, configuring, 289–94 commands, creating custom, 80–83 control templates, creating, 367–69
controls, creating custom, 380–83 data templates and groups, 248–51 databases, accessing, 232–35 databinding, practice with, 217–18 embedded resources, using, 191–92 flow documents, creating, 421–22 graphics, practice with, 172–73 images, practice with, 200–1 item controls, practice, 124–26 layout controls, practice, 146–48 localizing an application, 433–35 media player, creating, 183–85 Navigation applications, creating, 16–17 online pizza ordering, 32–38 publishing with ClickOnce, 464–65 resources, practice with, 397–98 routed events, practice, 68–69 settings, practice with, 89–91 string and conditional formatting, 276–79 styles, creating high-contrast, 318–20 user interface, building, 111–12 Windows applications, creating, 15–16 Windows Forms elements, practice with, 354–56 XBAP, creating, 17–19 languages, 426 LastChildFill, 139, 141–42 Launch Conditions Editor, 448 layout controls, 4, 101, 130–31 aligning content, 144–45 Canvas, 101, 142–43 case scenario, streaming stock quotes, 151 child elements, accessing, 143–44 control templates, 360 DockPanel, 139–42, 146–48 Grid, 101, 110, 131–37 HorizontalAlignment, 131–33, 135, 138 lab, practice with, 146–48 Margin, 131–33, 135 Navigation applications, 9 StackPanel, 101, 123–24, 131–32, 138 UniformGrid, 137–38 user interface, 4 VerticalAlignment, 131–33, 136 WindowsFormsHost, 350 WrapPanel, 139 Left, 6, 350 letters, 351 Line, 165 line breaks, 415 linear animations, 323 LinearGradientBrush, 157–60 LinearGradientBrush.EndPoint, 158 LinearGradientBrush.Spread, 160
MouseDown
LinearGradientBrush.StartPoint, 158 LinearKeyFrame, 334–35 LineGeometry, 166 LineHeight, 404 LineStackingStrategy, 404 links, page-based navigation, 22–23 List, 412 list-based controls. See item controls ListBox controls, 101, 116–17, 409–10 ContextMenu, 121 databinding, 221–23, 226, 238–40 virtualizing, 124 ListBox.SelectedIndex, 116 ListBox.SelectedItem, 116 ListBoxItem, 116–17 ListCollectionView, 225, 242–43 ListCollectionView.CustomSort, 242–43 ListItem, 406 lists, databinding to, 221–26 ListView, 124 Load, 176 LoadAsync, 177 LoadComplete, 177 LoadCompleted, 28 LoadedBehavior, 180 LoadTimeout, 177 localizing applications, 426–28 case scenario, 439 elements, 428–29 extracting content, 429–30 lab, practice with, 433–35 resources, 431 subdirectories, culture codes, 430–31 translating content, 430 UICulture attributes, 428 validators and converters, 432 Location, 199 LocBaml, 429–31 logical resources, 212, 389–92 accessing in XAML, 393 application resources, 393 declaring, 392–93 lab, practice with, 397–98 resource dictionary, 395–96 retrieving in code, 396–97 static and dynamic, 393–95 LostFocus, 217 lowercase characters, 352 LowerLatin, 407 LowerRoman, 407 Luna.Metallic.xaml, 379 Luna.NormalColor.xaml, 379
M managing application responsiveness, 41 binary resources, 187 content files, 190 embedding, 187, 191–92 loading, 188–89 retrieving, 189–91 content, 153–54 images, 194 bitmap metadata, 198–99 case scenarios, 205 Image element, 194 ImageBrush, 161–62 lab, practice with, 200–1 retrieving, 189 stretching and sizing, 194–96 transforming graphics, 196–98 Margin, 131–33, 135, 164, 404 MarkerStyle, 406 Mask, 351 MaskedTextBox, 351–52, 354–56, 386 MaskedTextProvider, 351 MatrixAnimationUsingPath, 324 MatrixTransform, 169 MaxHeight, 131 MaxWidth, 131 Media Player 11, 179 MediaCommands, 73 MediaElement, 179–83, 190 MediaEnded, 182 MediaFailed, 182–83 MediaOpened, 182–83 MediaPlayer, 179–83, 190, 196 memory, NavigationService, 24 Menu, 101, 124 MenuItem, 119–21 menus, 72, 119–22 metadata, 317 Metadata, 199 MethodName, 230 MethodParameters, 230 Microsoft Windows Installer, 190–91, 443–48 Microsoft Windows Media Player, 179 Microsoft Windows Vista, 7, 179 Microsoft Windows XP, 179 migrating, settings, 458 MinHeight, 131 MinWidth, 131 mnemonic keys, 102–3 Mode, 210 mouse gestures, 74 MouseDown, 60
515
516
MouseLeave
MouseLeave, 60 MoveCurrentTo, 224 MoveCurrentToFirst, 224 MoveCurrentToLast, 224 MoveCurrentToNext, 224–25 MoveCurrentToPosition, 224 MoveCurrentToPrevious, 224–25 Msbuild.exe, 429 Multibinding, 275–76 MultiDataTrigger, 313, 315 multimedia case scenarios, 205 lab, creating a media player, 183–85 MediaPlayer, 179–82 media-specific event handling, 182–83 SoundPlayer, 176–79 Multiselect, 346 multithreaded code, 48–49 MultiTrigger, 312 multi-triggers, 314 My, 88 myFilter, 247
N Name, 86–87, 363 NaturalDuration, 180 NaturalVideoHeight, 180 NaturalVideoWidth, 180 Navigate, 23–25 Navigated, 27 NavigateUri, 22–23 Navigating, 27 navigation, 3, 9 collections and lists, 223–26 flow documents, 416 fragment, 23, 28 journal, using, 25–27 NavigationService, 23–25 page-based, 21 event handling, 27–30 hosting pages in frames, 21 hyperlinks, 22–23 PageFunction objects, 30–32 simple, 32 structured, 32 using pages, 21 XBAPs, 11 Navigation applications, 3, 9 creating, 10 deploying, 444, 451 lab, creating, 16–17
NavigationCommands, 73 NavigationFailed, 28 NavigationProgress, 28 NavigationService, 23–30 NavigationService.AddBackEntry, 27 NavigationService.GoForward, 24 NavigationService.Navigate, 27 NavigationService.Refresh, 24 NavigationService.StopLoading, 24 NavigationStopped, 28 NavigationWindow, 9 NeutralResourcesLanguage, 427 NoBorder, 7 non-double animation, 332–34 nonlinear animation, 333–35 NonZero, 166 NoResize, 6 notification, change, 282, 287–94, 394 NotifyOnSourceUpdated, 210 NotifyOnTargetUpdated, 210 NotifyOnValidationError, 285 null, 75
O Object array, 273–76 ObjectDataProvider, 230–31 ObjectInstance, 230 objects. See also databinding; elements; Resources ADO.NET, 247–48 change notification, 282 CommandBinding, 76–78 commands, 72–75 data-based, 261 bound data, formatting, 273 case scenario, data conversion and validation, 301–2 change notification, 287–89 formatting, conditional, 268–69 IValueConverter, 261–64 lab, string and conditional formatting, 276–79 localizing data, 271 multi-value converters, 273–76 returning objects, 268–69 string formattting, 264–67 databinding, 212–15 displaying, 123 freezable, 48–49, 156, 364–65 Geometry, 166–68 Page, 9–11, 30–32 PageFunction, 22, 30–32 read-only, 48–49 read-write, 48–49 static, 73, 212
properties
validating, 282 binding rules, 282–83 custom rules, 283–84 error handling, 284–87 ExceptionValidationRule, 283 lab, configuring, 289–94 ObjectType, 230 ObservableCollection, 242–43, 287 Offset, 158 OneTime, 215 OneWay, 215–16 OneWayToSource, 216 online ordering, 32–38 OnReturn, 30–31 opacity, 156 OpacityMask, 156 Open, 181 OpenFile, 347 OpenFileDialog, 345–47 Orientation, 138 OriginalSource, 61 Overflow menu, 121–22 overloads, 29 OverwritePrompt, 346
P Pack URIs, 188–91 Pad, 160 Padding, 131, 404 Page objects, 9–11, 30–32 page view, 416 page-based navigation, 21 event handling, 27–30 hosting pages in frames, 21 hyperlinks, 22–23, 411 journal, using, 25–27 NavigationService, 23–25 PageFunction objects, 30–32 simple, 32 structured, 32 using pages, 21 XBAPs, 11 PageFunction, 22, 30–38 PageFunction.Returned, 31 pages, 21, 24, 416 Panel, 244 Paragraph, 402, 405–7, 412–14 parent properties, templated, 363–65 Part, 366 part names, 366 Path, 166–68, 364–65 animations, 323–24 databinding, 210–11, 227–28
PathDate, 167 PathGeometry, 166, 168 Pause, 181 PauseStoryboard, 328 Pen, 196 performance application, 394 freezable objects, 49 permissions, XBAPs, 11 Play, 177, 181 playback timelines, animation, 330–35 PlayLooping, 177 PlaySync, 177 PointAnimationUsingPath, 324 Points, 165 policies, security, 13–14 Polygon, 165–68 Polyline, 165 pop-up menus, 119 Position, 180 positioning, user interface items. See layout controls precedence, properties, 316–18 Predicate, 246–47 PreviewKeyDown, 61 PreviewMouseDown, 60–61 PreviousData, 214 Print, 420 PrintDialog, 419–20 PrintDialog.PrintVisual, 420 PrintDocument, 419–20 printing, 401, 418–20 PrintVisual, 419 priority, delegate execution, 48 processing asynchronous, 41–42, 47–48 background, 42–43. See also BackgroundWorker cancelling, 45–46 changing threads, 47–48 parameters, 43–44 progress reporting, 46 returning values, 44 progress monitoring, 46, 180, 386 ProgressBar controls, 107–8, 123, 386 ProgressChanged, 42 properties ancestor, 214–15 attached, 110, 134, 143 databinding, 207–9 ADO.NET object binding, 226–27 Binding.Mode, 215–16 case scenarios, 256–57 data templates, 238–41 elements, binding to, 211–12
517
518
Properties.Settings.Default
filtering data, 246–48 grouping data, 243–46 hierarchical data, 228–29 lab, data templates and groups, 248–51 lab, database access, 232–35 lab, practice binding, 217–18 lists, binding to, 221–26 ObjectDataProvider, 230–31 objects, binding to, 212–15 sorting data, 241–43 UpdateSourceTrigger, 216–17 XmlDataProvider, 231–32 dependency, 373–75, 394 e.Cancel, 30 JournalEntryName, 26 precedence, 316–18 setters, 306–7 triggers, 312, 330 Visual Studio, 5 Windows applications, 5–7 Properties.Settings.Default, 88 Property, 313 property setters, 306–7 property triggers, 312 property value inheritance, 373 property value providers, 373 PropertyChanged, 217, 287 PropertyChangedEventArgs, 287 PropertyGrid, 353–54 PropertyGroupDescription, 243, 245–46 PropertySort, 354 Publish, 452–55 Publishing Folder Location, 452–54 Publishing Options, 454
R RadialGradientBrush, 160–61 RadialGradientBrush.GradientOrigin, 160 RadioButton controls, 104–5, 121 RadiusX, 160, 164–65 RadiusY, 160, 164–65 RaiseEvent, 65 Rating, 199 ReachFramework, 418 read-only objects, 48–49 read-write objects, 48–49 Rectangle, 164–65 RectangleGeometry, 167 Red channel, 156 Reflect, 160 refresh, 24 RegisterClassHandler, 64
RegisterRoutedEvent, 64 registration, events, 63 registry, 11, 14, 444 Registry Editor, 448 relational data, 233–35 RelativeSource, 210, 213–15, 217, 364–65 RelativeSource.Mode, 214–15 RemoveAt, 144 RemoveBackEntry, 25 RemoveFromJournal, 32 RenderTransform, 168–70 RenderTransformOrigin, 168–70 Repeat, 160 RepeatBehavior, 325, 331–32 Replay, 26–27 ReportProgress, 42, 46 reports, 46 ResizeBehavior, 135, 137 ResizeDirection, 135 ResizeMode, 6 resizing. See also Stretch Canvas containers, 142 Grid, 133–37 images, 106 ToolBar controls, 122–23 windows, 6 resource dictionaries, 395–98 ResourceDictionaryLocation.ExternalAssembly, 380 ResourceDictionaryLocation.None, 379 ResourceDictionaryLocation.SourceAssembly, 379 resources, 378 application, 393, 396 binary, 187 content files, 190 embedding, 187, 191–92 loading, 188–89 retrieving, 189–91 ContextMenu, 121 culture, loading, 431 images, 194 bitmap metadata, 198–99 case scenarios, 205 Image element, 194 ImageBrush, 161–62 lab, practice with, 200–1 retrieving, 189 stretching and sizing, 194–96 transforming graphics, 196–98 logical, 212 satellite assemblies, 431 static and dynamic, 397 templates as, 361
Source
Resources, 306, 308–9, 378 lab, practice with, 397–98 logical, 389–92 accessing in XAML, 393 application resources, 393 declaring, 392–93 resource dictionary, 395–96 retrieving in code, 396–97 static and dynamic, 393–95 responsiveness, application, 41 Result, 44 ResumeStoryboard, 328 return types, PageFunction, 30–31 return value, PageFunction, 31 Returned, 30–31 ReturnEventArgs, 30–31 returning data, online pizza ordering, 32–34 RotateTransform, 169 routed events, 57, 59–62, 183 command bubbling, 76–77 defining, 64–65 lab, practice with, 68–69 registration, 63 RoutedEvent, 61 RoutedEventArgs, 61–63, 183 RowDefinitions, 133–37 rows, grid, 133–38 RuleInError, 285 RunWorkerAsync, 41–44 RunWorkerCompleted, 42, 44 RunWorkerCompletedEventArgs, 44
S satellite assemblies, 431 Save As dialog box, 346 Save As File Type dialog box, 346 SaveFileDialog, 345–47, 354–56 SaveFileDialog.OpenFile, 347 ScaleTransform, 169–70 ScaleX, 170 ScaleY, 170 scaling text, 417 Scope, 86–87 scroll bars, 107, 116 scroll view, 416 Section, 409 security, 13–14 Certificates, 463–64 code access, 458 Navigation applications, 9 trust environments, 444, 451, 458–61 XBAPs, 11
SeekStoryboard, 328 SelectedIndex, 117 SelectedItem, 117, 226 SelectedObject, 354 SelectionMode, 117 Self, 214 Separator, 121 Serializable attribute, 26 servers, application deployment, 3, 11 SessionEnding, 67 SetBinding, 211–12 SetStoryboardSpeedRatio, 328–29 Setters, 306–7, 313–14, 363 settings, 57 application, 86–91 Internet security, 14 migrating, 458 Settings Editor, 87 Settings object, 88 Setup projects, 443, 445–46, 448–49 SetValue, 375 Shape class, 163–64 shapes, 163–64 clipping, 171 Ellipse, 164–65 Line, 165 Polygon, 165–68 Polyline, 165 Rectangle, 164–65 Transforms, 168–70 shopping cart, 32 shortcut keys, 102–4, 120, 447–48 Show display method, 8–9 ShowDialog display method, 8–9, 347, 420 ShowInTaskbar, 6 ShowsPreview, 135 Silver Window XP theme, 379 simple navigation, 32 SingleBorderWindow, 7 siteOfOrigin, 189–91 SizeToContent, 6 SkewTransform, 169 SkipStoryboardToFill, 328 slider controls, 108–10, 211–12, 416–17 Snaplines, 144–45 snapshots, journal entries, 26 SolidColorBrush, 48, 156–57 Solution Explorer, Visual Studio, 5 SortDescriptions, 241–42 SoundLocation, 177 SoundLocationChanged, 177 SoundPlayerAction, 178–79, 315–16 Source, 210, 212–15, 217, 231
519
520
source properties
source properties, 208–9 ADO.NET object binding, 226–27 Binding.Mode, 215–16 case scenarios, databinding, 256–57 data templates, 238–41 data, filtering, 246–48 data, grouping, 243–46 data, sorting, 241–43 elements, binding to, 211–12 hierarchical data, binding, 228–29 lab, data templates and groups, 248–51 lab, database access, 232–35 lab, practice binding, 217–18 lists, binding to, 221–26 ObjectDataProvider, 230–31 objects, binding to, 212–15 UpdateSourceTrigger, 216–17 XmlDataProvider, 231–32 Source property, 21, 61, 106, 180, 194 spaces, 415 Span, 412 SpeedRatio, 180, 325, 329, 332 SplineKeyFrame, 334–35 StackPanel controls, 101, 123–24, 131–32, 138 stand-alone windows, 14. See also dialog boxes StartingIndex, 407 StartUp, 67 static objects, 73, 212 StaticResource, 378, 393–95, 397 StatusBar, 123 Stop, 177, 181 StopStoryboard, 328 storage, isolated, 12–13 Storyboard, 324–27, 330–36 Stream, 177, 189 StreamChanged, 177 StreamGeometry, 167 StreamReader, 12–13 StreamWriter, 12–13 Stretch, 132, 135–36 ImageBrush, 162 images, 194–96 Shape, 164 VisualBrush, 163 StretchDirection, 194–96 strings, formatting, 264–67, 276–77 Stroke, 156, 164 StrokeThickness, 164 structured navigation, 32 Style, 305–6, 308, 312, 316, 404 animation triggers, 327 case scenario, custom controls, 386
control templates, 365 logical resources, 392 Style.Triggers, 313 styles, 303, 305 case scenarios, 340–41, 386 creating, 308–10 inheritance, 311–12 lab, creating high-contrast styles, 318–20 properties of, 305–6 property value precedence, 316–18 setters, 306–7 triggers, 312–16 Subject, 199 system culture, 426 System.Globalization.CultureInfo, 431–32 System.Globalization.Info, 426 System.IO.Stream, 347 System.Threading.Thread.CurrentThread .CurrentUICulture, 426 System.Windows.Forms, 348 System.Windows.Forms.Integration, 349 System.Windows.Media.Animation, 323 System.Windows.Media.Color, 348 System.Windows.Media.ImageSource class, 106 System.Windows.Resources.StreamResourceInfo, 189 System.Windows.Xps.Packaging, 418 SystemColors, 378 SystemColors.WindowColor, 212 SystemDeployment, 456 SystemFonts, 378 SystemParameters, 378
T Tab key, 111 tab order, controls, 111 TabIndex, 111 Table, 412–14 TableCell, 407 TableRowGroup, 407 tabs, 415 target properties, 208–9 ADO.NET object binding, 226–27 Binding.Mode, 215–16 case scenarios, databinding, 256–57 data templates, 238–41 data, filtering, 246–48 data, grouping, 243–46 data, sorting, 241–43 elements, binding to, 211–12 hierarchical data, binding, 228–29 lab, data templates and groups, 248–51 lab, database access, 232–35
Universal Naming Convention (UNC)
lab, practice binding, 217–18 lists, binding to, 221–26 ObjectDataProvider, 230–31 objects, binding to, 212–15 UpdateSourceTrigger, 216–17 XmlDataProvider, 231–32 Target property, 103 TargetType, 306, 309, 392 task execution, 41 tasks. See commands Template, 316, 386 TemplateBinding, 364 TemplatedParent, 214, 316, 364–65 templates, 373 control creating, 378–79 lab, creating, 367–69 parent properties, 363–65 part names, predefined, 366 source code, 366 Styles, 365–66 Triggers, 362–63 controls, custom, 376 resources as, 361 theme-specific, 379–80 templates, data, 238–41, 248–51, 268–69 text ComboBox, 118 display of, 107 flow documents, 401 block elements, 405–10 containers, 416 creating, 402–3 formatting, 403–4 inline elements, 410–15 lab, creating, 421–22 scaling text, 417 white space, 415 local culture, 428–29 wrapping, 107, 130, 139, 407 XPS documents (XML Paper Standard), 418 TextAlignment, 404 TextBlock, 105–6 TextBox, 101, 107, 121, 217, 351–52 TextWrapping, 107 ThemeInfoAttribute, 379–80 themes, 376, 378–80 Thickness, 131–32 thousands separator, 352 threads, 41, 47–51 ThreeDBorderWindow, 7 thumb, 108–9
Tile, 163 TileMode, 162–63 time formats, 352, 432 time separator, 352 time stamps, 29 Timeline, 326 Title, 7, 199 Toolbar control, 119, 121–23 ToolBar.OverflowMode, 121–22 ToolBarTray, 122–23 Toolbox, 5 ToolTip, 404 ToolWindow, 7 Top, 7, 350 Topmost, 7 ToString, 102, 247, 265 Transformations, 168–70 TransformGroup, 169 transforming elements, 170 graphics, 196–98 Transforms, 168–70 TranslateTransform, 169 TreeView, 101, 118–19 TreeView.SelectedItem, 119 TreeViewItem, 118–19 triggers, 312–16 Triggers, 306, 308, 312–16, 327–30, 362–63 trust environments, 3, 190–91, 444, 451, 458–61 TryFindResource, 396 tunneling events, 60–61, 63 TwoWay, 216 Type, 86–87 Type array, 273–76
U UI (user interface). See user interface (UI) UI (user interface) thread, 41 UICulture, 428, 431 Uid, 429 UIElement, 102 UNC (Universal Naming Convention), 191 uncompressed files, .wav, 176–79 underscore (_) symbol, 102–4, 120 Uniform, 162, 164, 195–96 Uniform Resource Identifier (URI), 23–25, 106, 188–91, 194 Uniform Resource Locator (URL), 191 UniformGrid, 137–38 UniformToFill, 162, 164, 195–96 Union, 168 Universal Naming Convention (UNC), 191
521
522
UnmanagedMemoryStream
UnmanagedMemoryStream, 189 Update, 456 UpdateAsynch, 457 updates, 27, 443–58, 470 UpdateSourceTrigger, 216–17 updateuid, 429 UpOnly, 195 uppercase characters, 352 UpperLatin, 407 UpperRoman, 407 URI (Uniform Resource Identifier), 23–25, 106, 188–91, 194 URL (Uniform Resource Locator), 191 user controls, 378 creating, 372, 376 case scenario, custom controls, 386 choosing, 373 theme-based appearance, 378–80 custom, 373–75, 377 lab, creating custom controls, 380–83 user experience. See also user interface (UI) Internet, XBAP, 3 navigation, 3 ProgressBar controls, 107–8 settings, saving, 88 user input. See also user interface (UI) case scenario, user interface, 96 case scenario, validating, 95 commands, 72 gestures, 74 PageFunction, 32 window display methods, 8 user interface (UI), 99. See also databinding; graphics; multimedia content; visual effects attached properties, 110 case scenario designing, 54 streaming stock quotes, 151 user input, 96 control templates, 359 lab, creating, 367–69 part names, predefined, 366 source code, 366 Styles, 365 templated parent properties, 363–65 Triggers, 362–63 controls, 9, 59, 72, 101–5 controls, customizing, 343, 372 case scenario, custom controls, 386 consuming controls, 377 creating, 376–77 dependency properties, 373–75 lab, creating custom controls, 380–83
selecting controls, 373 theme-based appearance, 378–80 user controls, 372, 376 data display data templates, 238–41 filtering data, 246–48 grouping data, 243–46 sorting, 241–43 deploying, 444 elements, 72 Image controls, 106 item controls ComboBox, 117–18, 121 ContextMenu, 119, 121, 124 lab, practice with, 124–26 ListBox control, 101, 116–17, 121, 124 menus, 119–21 StatusBar, 123 Toolbar, 119, 121–23 TreeView, 101, 118–19 virtualization, 123–24 lab, building, 111–12 lab, updating, 49–51 layout controls, 130–31 aligning content, 144–45 Canvas, 101, 142–43 child elements, accessing, 143–44 DockPanel, 139–42, 146–48 Grid, 101, 110, 131–37 HorizontalAlignment, 131–33, 135, 138 lab, practice with, 146–48 Margin property, 131–33, 135 StackPanel, 101, 123–24, 131–32, 138 UniformGrid, 137–38 VerticalAlignment, 131–33, 136 WrapPanel, 139 localizing (culture variations), 426–28 case scenario, 439 elements, 428–29 extracting content, 429–30 lab, practice with, 433–35 resources, 431 UICulture attributes, 428 validators and converters, 432 logical resources, 389–92 accessing in XAML, 393 application resources, 393 declaring, 392–93 lab, practice with, 397–98 resource dictionary, 395–96 retrieving in code, 396–97 static and dynamic, 393–95 Navigation application, 9
visual elements, printing
ProgressBar controls, 107–8 responsiveness, 41 Slider control, 108–10 subdirectories, culture codes, 430–31 tab order, controls, 111 TextBlock, 105–6 TextBox, 107 translating content, 430 updating, 47–48 Windows applications, 4 Windows Forms controls, 344–45 file dialog boxes, 345–47 lab, practice with, 354–56 MaskedTextBox, 351–52 PropertyGrid, 353–54 Windows properties, 5–7 WindowsFormsHost, 349–51 XBAPs, 11 User Interface Editor, 448 User property, 87 UserControl, 372 user-defined styles, 317 User's Desktop, 446 User's Program Menu, 446
V Validate, 283–84, 432 ValidateNames, 346 validation data, 282 binding rules, 282–83 case scenario, 301–2 change notification, 287–89 custom rules, 283–84 error handling, 284–87 ExceptionValidationRule, 283 lab, configuring, 289–94 ObservableCollection, 288–89 navigation events, 27 Validation.Error, 285–87 ValidationCollection, 282–83 ValidationErrorEventArgs, 285–87 ValidationResult, 283–84 ValidationRules, 282–84, 286, 432 validators, culture settings, 432 Value, 86–87, 313 Value property, 87 ValueChanged event, 110 ValueConversion, 262 values background processing, 44 returning, 31 vertical scroll bars, 107, 116
VerticalAlignment, 131–33, 136 VerticalAnchor, 414 VerticalContentAlignment, 131 VerticalOffset, 414 VerticalScrollBarVisibility, 107 video, 179–85, 196, 205 VideoDrawing, 196 ViewBox, 162 Viewport, 162 views, document, 416 virtualization, item controls, 123–24 VirtualizingStackPanel, 123–24 Visual Basic, settings, 88 Visual class, 163 visual effects animation, 303, 323–24 coding, 335–36 key frames, 333–35 lab, animation of controls, 336–37 non-double types, 332–34 playback timelines, 330–35 properties, 324–25 with Triggers, 327–30 case scenarios, 340–41 control templates, 359 part names, predefined, 366 source code, 366 Style, 365 templated parent properties, 363–65 Triggers, 362–63 controls, custom, 372, 376–77 case scenario, 386 consuming controls, 377 dependency properties, 373–75 lab, creating custom controls, 380–83 selecting, 373 theme-based appearance, 378–80 user controls, 376 local culture, 428–29 styles, 303, 305 creating, 308–10 inheritance, 311–12 lab, creating high-contrast styles, 318–20 properties of, 305–6 property value precedence, 316–18 setters, 306–7 triggers, 312–16 Windows Forms controls, 344–45 ColorDialog box, 348–49 lab, practice with, 354–56 MaskedTextBox, 351–52 PropertyGrid, 353–54 WindowsFormsHost, 349–51 visual elements, printing, 420
523
524
Visual Studio
Visual Studio creating Windows applications, 4–5 Designer, 5 settings editor, 87 Snaplines, 144–45 window properties, 7 visual tree, 59–60 VisualBrush, 163 VisualTree, 214 VisualTreeHelper.HitTest, 171–72 volume control, 108–9
W WCF Web services, 14 Web pages page-based navigation, 22 XBAPs and, 11 Web servers, application deployment, 11 Web services, WCF, 14 Web sites, application deployment, 3 white space, 415 Width, 6–7, 131, 133–37, 164 WidthAndHeight, 6 Window, 60 Window class, 4–7 Window.Resources, 121, 212, 268–69, 392, 396 Window-based applications, 411 windows background, 157–60 borders, 5 display of, 8–9 resize, 6 stand-alone, 14 style, 7 Windows applications, 3–4, 444 creating, 4–5 deploying, 451 displaying, 8–9 lab, creating, 15–16 properties, 5–7 Windows Classic theme, 379 Windows Forms applications, 1, 3, 5, 345–47 Windows Forms controls, 344–45 ColorDialog dialog box, 348–49 dialog boxes, 345–49 lab, practice with, 354–56 MaskedTextBox, 351–52 PropertyGrid, 353–54 WindowsFormsHost, 349–51 Windows Installer, 190–91, 443–49 Windows Internet Explorer, XBAPs, 3 Windows Media Player, 10, 179 Windows Vista, 179, 378–80
Windows XP, 179 Windows.Resources, 308–9 WindowsFormsHost, 354 WindowsFormsHost.Child, 351, 354 WindowsFormsIntegration, 349 WindowsStyle, 5 WindowStartupLocation, 7 WindowState, 7 WindowStyle, 7 worker threads, 47–48 WorkerReportsProgress, 42 WorkerSupportsCancellation, 42, 45–46 Wrap, 107 WrapDirection, 415 WrapPanel, 139 WrapWithOverflow, 107 writing, isolated storage, 12–13
X X:Key, 392–93 XAML (Extensible Application Markup Language), 1 attached properties, 110 binary resources, 190–91 Button controls, 101–2 Canvas, 142 ContextMenu, 121 custom commands, 80 event handlers, 62 ListBox controls, 116–17 menus, 119–21 multimedia formats, 179 resources, accessing, 393 TreeView controls, 118–19 XAML Browser Application (XBAP). See XBAPs XBAPs, 3, 11, 191, 444 creating, 11–12 deploying, 451, 458–61 isolated storage, 12–13 lab, creating, 17–19 web pages and, 11 XML Paper Standard (XPS), 418 XML, databinding, 231–32 XmlDataProvider, 231–32 Xor, 168 XPath, 210, 231–32 XPS documents (XML Paper Standard), 418 XpSDocument, 418
Z zoom, 401, 416–17 Zoom, 417 Z-order, 142–43
Z04S625662.fm Page 1 Thursday, June 5, 2008 12:53 PM
System Requirements We assume that before using this training kit, you already have a working knowledge of Windows, Microsoft Visual Basic or C# (or both), and Extensible Application Markup Language (XAML).
Hardware Requirements The following hardware is required to complete the practice exercises: ■
A computer with a 1.6-gigahertz (GHz) or faster processor
■
A minimum of 384 megabytes (MB) of random access memory (RAM)
■
A minimum of 2.2 gigabytes (GB) of available hard disk space is required to install VS 2008. Additionally, 50 megabytes (MB) of available hard disk space is required to install the labs.
■
A DVD-ROM drive
■
A 1024 x 768 or higher resolution display with 256 colors or more
■
A keyboard and Microsoft mouse or compatible pointing device
Software Requirements The following software is required to complete the practice exercises: ■
■
One of the following operating systems: ❑
Windows Vista (any edition except Windows Vista Starter)
❑
Windows XP with Service Pack 2 or later (any edition except Windows XP Starter)
❑
Windows Server 2003 with Service Pack 1 or later (any edition)
❑
Windows Server 2003 R2 or later (any edition)
❑
Windows Server 2008
Microsoft Visual Studio 2008
Exam 70-502: Microsoft .NET Framework 3.5 – Windows Presentation Foundation Objective
Location in Book
Creating a WPF Application Select an application type.
Chapter 1, Lesson 1
Configure event handling.
Chapter 2, Lesson 1
Configure commands.
Chapter 2, Lesson 2
Configure page-based navigation.
Chapter 1, Lesson 2
Configure application settings.
Chapter 2, Lesson 3
Manage application responsiveness.
Chapter 1, Lesson 3
Building User Interfaces Select and configure content controls.
Chapter 3, Lesson 1
Select and configure item controls.
Chapter 3, Lesson 2
Select and configure layout panels.
Chapter 3, Lesson 3
Integrate Windows Forms controls into a WPF application.
Chapter 8, Lesson 1
Create user and custom controls.
Chapter 8, Lesson 3
Adding and Managing Content Create and display two-dimensional and three-dimensional graphics.
Chapter 4, Lesson 1
Create and manipulate documents.
Chapter 9, Lesson 2
Add multimedia content.
Chapter 4, Lesson 2
Manage binary resources.
Chapter 4, Lesson 3
Manage images.
Chapter 4, Lesson 4
Binding to Data Sources Configure binding options.
Chapter 5, Lesson 1
Bind to a data collection.
Chapter 5, Lesson 2
Bind to a property of another element.
Chapter 5, Lesson 3
Convert and validate data.
Chapter 6, Lesson 1
Configure notification of changes in underlying data.
Chapter 6, Lesson 2
Objective
Location in Book
Customizing Appearance Create a consistent user interface appearance by using styles.
Chapter 7, Lesson 1
Change the appearance of a UI element by using triggers.
Chapter 7, Lesson 2
Add interactivity by using animations.
Chapter 7, Lesson 3
Share logical resources throughout an application.
Chapter 9, Lesson 1
Change the appearance of a control by using templates.
Chapter 8, Lesson 2
Localize a WPF application.
Chapter 9, Lesson 3
Configuring and Deploying WPF Applications Deploy for standalone access.
Chapter 10, Lesson 1
Deploy to a partial trust environment.
Chapter 10, Lesson 2
Deploy an XBAP application.
Chapter 10, Lesson 3
Manage upgrades.
Chapter 10, Lesson 4
Configure the security settings of an application deployment.
Chapter 10, Lesson 5
NOTE
Exam objectives
The exam objectives listed here are current as of this book’s publication date. Exam objectives are subject to change at any time without prior notice and at Microsoft’s sole discretion. Please visit the Microsoft Learning Web site for the most current listing of exam objectives: http://www.microsoft.com/learning/exams/70-502.mspx.