This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Imar Spaanjaars graduated in Leisure Management at the Leisure Management School in the Netherlands, but he quickly changed his career path into the Internet world.
After working in the Internet business at various web agencies for the past twelve years, he recently started up his own company called De Vier Koeden (www.devierkoeden.nl), a small Internet agency specializing in consultancy and development of Internet and intranet applications with Microsoft technologies such as ASP.NET 4. Imar has written books on ASP.NET and Macromedia Dreamweaver, all published under the Wrox brand. He is also one of the top contributors to the Wrox Community Forum at p2p.wrox.com, where he shares his knowledge with fellow programmers. In 2008 and 2009, Imar received Microsoft’s Most Valuable Professional (MVP) award for his contributions to the ASP.NET community. Imar lives in Utrecht, the Netherlands, with his girlfriend, Fleur. You can contact him through his personal web site at http://imar.spaanjaars.com or by e‑mail at [email protected].
Credits
Acquisitions Editor
Paul Reese
Vice President and Executive Group Publisher
Richard Swadley project Editor
Brian Herrmann
Vice President and Executive Publisher
Barry Pruett Technical Editor
Michael J. Apostol
Associate Publisher
Jim Minatel Production Editor
Rebecca Anderson
Project Coordinator, Cover
Lynsey Stanford Copy Editor
Kim Cofer
Compositor
Craig Johnson, Happenstance Type-O-Rama Editorial Director
Just as with my prior books, I really enjoyed writing this one. The process from an empty Word document at the very beginning to a printed copy as the final result is a really interesting one. Along the way, I discovered, understood and used a lot of the new features that ASP.NET 4 and Visual Web Developer 2010 bring. Since this book is a major update on the previous edition — Beginning ASP.NET 3.5 in C# and VB — I have been able to incorporate a lot of reader feedback into this edition. While working on this book, I went through the more than 450 threads in the book’s forum, looking for feedback that could improve this edition. This allowed me to add clarification and more detailed instructions where needed. If you have the previous edition and posted a question in the Wrox forums: thanks for your valuable feedback; you’ve really helped to make this book better.
Besides my readers, I owe a lot to other people who helped me write this book. First of all I’d like to thank Brian Herrmann for his editorial work. Just as before, it was a pleasure to work with you! I also want to thank Michael J. Apostol for his work as a technical editor, and the people from Wrox for their support and contributions to this book. Another person I owe a lot to is my friend Anne Ward from Blue Violet, a UK-based web and graphic design company. Anne has done most of the new designs used in this book and I highly appreciate her input. Thanks again, Anne! The concert pictures you see in this book come from Nigel D. Nudds, who kindly let me use pictures from his collection. Finally, I would like to thank my good friends Joost and René and my lovely girlfriend Fleur for their support during this project.
Contents
Foreword Introduction Chapter 1: Getting Started with ASP.NET 4
Microsoft Visual Web Developer Getting Visual Web Developer Installing Visual Web Developer Express
Creating Your First ASP.NET 4 Web Site An Introduction to ASP.NET 4 Understanding HTML A First Look at ASP.NET Markup
A Tour of the IDE The Main Development Area Informational Windows
Customizing the IDE Rearranging Windows Modifying the Toolbox Customizing the Document Window Customizing Toolbars Customizing Keyboard Shortcuts Resetting Your Changes
The Sample Application Practical Tips on Visual Web Developer Summary Chapter 2: Building an ASP.NET Web Site
Creating Web Sites with VWD 2010 Different Project Types Choosing the Right Web Site Template Creating and Opening a New Web Site
Working with Files in Your Web Site The Many File Types of an ASP.NET 4 Web Site Adding Existing Files Organizing Your Site Special File Types
xxv xxvii 1
2 3 3
5 9 10 14
15 15 21
22 22 23 25 26 26 27
27 29 30 33
34 34 35 37
40 40 44 46 47
CONTENTS
Working with Web Forms The Different Views on Web Forms Choosing between Code Behind and Pages with Inline Code Adding Markup to Your Page Connecting Pages
47 49 54 60
Practical Tips on Working with Web Forms Summary
62 63
Chapter 3: Designing Your Web Pages
65
Why Do You Need CSS? Problems of HTML Formatting How CSS Fixes Formatting Problems
An Introduction to CSS CSS—The Language The Style Sheet Adding CSS to Your Pages
66 66 67
68 71 72 84
Working with CSS in Visual Web Developer
87
Creating New Styles in External Style Sheets Creating Embedded and Inline Style Sheets Applying Styles Managing Styles
87 92 98 99
Practical Tips on Working with CSS Summary Chapter 4: Working with ASP.NET Server Controls
Introduction to Server Controls A Closer Look at ASP.NET Server Controls Defining Controls in Your Pages Common Properties for All Controls
Types of Controls Standard Controls HTML Controls Data Controls Validation Controls Navigation Controls Login Controls Ajax Extensions WebParts Dynamic Data
xvi
47
102 103 105
106 110 110 110
113 113 126 127 127 127 127 127 128 128
CONTENTS
The ASP.NET State Engine What Is State and Why Is It Important? How the State Engine Works Not All Controls Rely on View State A Note about View State and Performance
Practical Tips on Working with Controls Summary Chapter 5: Programming Your ASP.NET Web Pages
Introduction to Programming Data Types and Variables Converting and Casting Data Types Using Arrays and Collections
Statements Operators Making Decisions Loops
Organizing Code Methods: Functions and Subroutines The App_Code Folder Organizing Code with Namespaces Writing Comments
Object Orientation Basics Important OO Terminology Events
Practical Tips on Programming Summary Chapter 6: Creating Consistent Looking Web Sites
Using a Centralized Base Page An Introduction to the ASP.NET Page Life Cycle Implementing the Base Page Creating Reusable Page Templates
Themes Different Types of Themes Choosing Between Theme and StyleSheetTheme Applying Themes Extending Themes Dynamically Switching Themes
128 128 129 133 134
135 136 139
140 141 144 146
151 152 159 166
170 170 172 176 179
181 182 192
194 195 197
198 200 201
207 208 210 214
218 219 219 220 224 226 xvii
CONTENTS
Skins Creating a Skin File Named Skins Disable Theming for Specific Controls
Practical Tips on Creating Consistent Pages Summary Chapter 7: Navigation
Different Ways to Move around Your Site Understanding Absolute and Relative URLs Understanding Default Documents
Using the Navigation Controls Architecture of the Navigation Controls Examining the Web.sitemap File Using the Menu Control Using the TreeView Control Using the SiteMapPath Control
Programmatic Redirection Programmatically Redirecting the Client to a Different Page Server-Side Redirects
233 235 236
236 237 239
240 240 244
245 245 246 248 257 261
263 263 266
Practical Tips on Navigation Summary
268 269
Chapter 8: User Controls
271
Introduction to User Controls
272
Creating User Controls Adding User Controls to a Content Page or Master Page Site-Wide Registration of User Controls User Control Caveats
Adding Logic to Your User Controls Creating Your Own Data Types for Properties Implementing View State Properties View State Considerations
Practical Tips on User Controls Summary Chapter 9: Validating User Input
Gathering Data from the User Validating User Input in Web Forms Understanding Request Validation
xviii
232
272 276 279 280
282 283 288 294
294 295 297
298 299 316
CONTENTS
Processing Data at the Server Sending E‑mail from Your Web Site Reading from Text Files
Practical Tips on Validating Data Summary Chapter 10: ASP.NET AJAX
Introducing Ajax Using ASP.NET AJAX in Your Projects Creating Flicker-Free Pages Providing Feedback to Users The Timer Control
Using Web Services and Page Methods in Ajax Web Sites What Are Web Services? Creating Web Services Using Web Services in Your Ajax Web Site Introducing Page Methods The Client-Side ASP.NET AJAX Library This Is Just the Beginning
316 317 322
328 329 331
332 334 334 340 345
346 346 347 351 358 361 362
Practical Ajax Tips Summary
363 364
Chapter 11: jQuery
367
An Introduction to jQuery Choosing the Location for Your jQuery Reference Different Ways to Include the jQuery Library
jQuery Syntax
368 368 369
373
jQuery Core Selecting Items Using jQuery
374 374
Modifying the DOM with jQuery
382
CSS Methods Handling Events Miscellaneous jQuery Functionality Common Mistakes When Working with jQuery
Effects with jQuery jQuery and Extensibility Practical Tips on jQuery Summary
382 383 385 386
391 396 400 401
xix
CONTENTS
Chapter 12: Introducing Databases
What Is a Database? Different Kinds of Relational Databases Using SQL to Work with Database Data Retrieving and Manipulating Data with SQL Reading Data Creating Data Updating Data Deleting Data
Creating Your Own Tables Data Types in SQL Server Understanding Primary Keys and Identities Creating Relationships Between Tables
Practical Database Tips Summary Chapter 13: Displaying and Updating Data
Data Controls Data-bound Controls Data Source Controls Other Data Controls
Data Source and Data-bound Controls Working Together Displaying and Editing Data with GridView Inserting Data with DetailsView Storing Your Connection Strings in web.config Filtering Data
Customizing the Appearance of the Data Controls Configuring Columns or Fields of Data-bound Controls
Updating and Inserting Data Using DetailsView to Insert and Update Data
Practical Tips for Displaying and Updating Data Summary Chapter 14: LINQ and the ADO.NET Entity Framework
Introducing LINQ LINQ to Objects LINQ to XML LINQ to ADO.NET
Introducing the ADO.NET Entity Framework Mapping Your Data Model to an Object Model xx
403
404 405 405 407 408 417 418 418
421 421 422 426
430 431 433
433 434 435 436
436 436 442 445 446
452 453
459 459
473 474 47 7
478 479 479 479
480 481
CONTENTS
Introducing Query Syntax Standard Query Operators Shaping Data with Anonymous Types
Using Server Controls with LINQ Queries Using Data Controls with the Entity Framework A Few Notes about Performance
Practical LINQ and ADO.NET Entity Framework Tips Summary Chapter 15: Working with Data — Advanced Topics
487 487 491
496 496 526
526 527 529
Formatting Your Controls Using Styles
530
An Introduction to Styles Combining Styles, Themes, and Skins
531 535
Handling Events The ASP.NET Page and Control Life Cycles Revisited The ASP.NET Page Life Cycle and Events in Data Controls Handling Errors that Occur in the Data Source Controls
Hand-Coding Data Access Code Caching Common Pitfalls with Caching Data Different Ways to Cache Data in ASP.NET Web Applications
Practical Data Tips Summary Chapter 16: Security in Your ASP.NET 4 Web Site
Introducing Security Identity: Who Are You? Authentication: How Can You Prove Who You Are? Authorization: What Are You Allowed to Do? An Introduction to the ASP.NET Application Services
Introducing the Login Controls The Login Controls Configuring Your Web Application
The Role Manager Configuring the Role Manager Managing Users with the WSAT Configuring the Web Application to Work with Roles Programmatically Checking Roles
Practical Security Tips Summary
539 540 545 550
554 565 565 566
575 576 579
580 580 580 580 581
582 587 599
603 603 604 608 612
616 617
xxi
CONTENTS
Chapter 17: Personalizing Web Sites
Understanding Profile
620
Configuring the Profile Using the Profile
621 627
Other Ways of Dealing with Profile Anonymous Identification Cleaning Up Old Anonymous Profiles Looking at Other Users’ Profiles
Exception Handling Different Types of Errors Catching and Handling Exceptions Global Error Handling and Custom Error Pages
The Basics of Debugging Tools Support for Debugging
645 645 646 648
651 651 655
656 656 658 666
673 677
Moving around in Debugged Code Debugging Windows
677 677
Debugging Client-Side Script Tracing Your ASP.NET Web Pages
684 688
Using the Standard Tracing Capabilities Adding Your Own Information to the Trace Tracing and Performance A Security Warning
Practical Debugging Tips Summary
688 691 694 694
694 695
Chapter 19: Deploying Your Web Site
697
Preparing Your Web Site for Deployment
698
Avoiding Hardcoded Settings The web.config File Expression Syntax The WebConfigurationManager Class
Copying Your Web Site Creating a Simple Copy of Your Web Site Publishing Your Web Site
xxii
619
698 699 699 700
706 707 709
CONTENTS
Running Your Site under IIS Installing and Configuring the Web Server Installing and Configuring ASP.NET Understanding Security in IIS NTFS Settings for Planet Wrox Troubleshooting Web Server Errors
Moving Data to a Remote Server Using the Database Publishing Wizard Recreating the Database
The Deployment Checklist What’s Next Summary Appendix A: Exercise Answers
Configuring SQL Server 2008 Terminology and Concepts Using SQL Server Management Studio Enabling Remote Connections in SQL Server Connecting Your Application to SQL Server 2008
Configuring Application Services Configuring Your Database for the Application Services Overriding the LocalSqlServer Connection String Overriding the Settings of the Application Services
Index
xxiv
771 771 773 773
777
Foreword
This is a very nice book written by one of ASP.NET’s very own Most Valuable Professionals, Imar Spaanjaars. It takes a lot to be a Microsoft MVP — not only do you need expertise in the subject matter, you also need to be a great teacher and a technology philanthropist. Through his blogs, articles, and books, Imar has given a considerable amount of his time to benefit the web development community. Imar has been active in the ASP.NET community for a long time and has written several books on web development, including the previous version of this book, Beginning ASP.NET 3.5. What makes this book so special is that it does not assume the reader has any prior knowledge of web development and at the same time it is a great book for anyone hoping to upgrade to ASP.NET 4. The book uses the free Visual Studio edition, Visual Web Developer Express 2010, starting from a chapter covering how to get started and then slowly introducing more advanced concepts in a seamless fashion. In the process of helping create Visual Studio 2010 for Web Developers, I often interacted with community leaders to collect feedback on how we could make the product better for every web developer. Imar has been using ASP.NET 4 and VWD 2010 Express since their first Beta versions, and has been pouring in feedback on which bugs are important to fix and which features to emphasize. His passion for the technology is reflected in this book and I am positive that the readers of this book will feel that same passion. ASP.NET 4 and Visual Studio 2010 have feature improvements in many areas, including building standards-compliant web sites, JScript IntelliSense, jQuery integration, Ajax, CSS improvements, HTML and markup snippets, Web Deployment, and data integration. I believe ASP.NET 4 and Visual Studio 2010 are great technologies to build fine web sites, and this book will be an excellent companion to these products, helping users excel in the world of web development. —Vishal R. Joshi Senior Program Manager Lead Microsoft Web Platform & Tools http://vishaljoshi.blogspot.com
Introduction
To build effective and attractive database-driven web sites, you need two things: a solid and fast framework to run your web pages on and a rich and extensive environment to create and program these web pages. With ASP.NET 4 and Visual Web Developer 2010 you get both. Together they form the platform to create dynamic and interactive web sites. ASP.NET 4 builds on top of its popular predecessors ASP.NET 2.0 and ASP.NET 3.5. While maintaining backward compatibility with sites built using these older versions, ASP.NET 4 and Visual Web Developer 2010 introduce new, exciting features and bring many smaller, but much needed changes to the framework and development tools. With each new release of Visual Studio (which includes Visual Web Developer) since Visual Studio 2003, I am surprised by the sheer amount of new functionality and changes Microsoft has been able to put in the product. Visual Studio 2010 is no exception. A major new feature in Visual Studio 2010 is the full integration of the ADO.NET Entity Framework 4 that lets you work with databases with very little code. Another great change in Visual Studio is the use of Windows Presentation Foundation (WPF) for the User Interface which brings a better user experience and new behavior in Visual Studio itself. Although not a new feature by itself, the inclusion of jQuery in Visual Web Developer is an excellent decision that will help you write fancier web sites in less time. jQuery is a compelling client side, cross-browser JavaScript framework and is discussed in detail in Chapter 11. If you’re familiar with earlier versions of ASP.NET, you’ll be happy to find many small gems in the new version of the framework that will make your life as a developer easier. I’ll mention and discuss these new features throughout this book where appropriate. For a complete list of all new features in ASP.NET, check out the following white paper at the official ASP.NET web site: http://www.asp.net/learn/whitepapers/aspnet4/
Probably the best thing about Visual Web Developer 2010 is its price: it’s still available for free. Although the commercial versions of Visual Studio 2010 ship with Visual Web Developer, you can also download and install the free Express edition. This makes Visual Web Developer 2010 and ASP.NET 4 probably the most attractive and compelling web development technologies available today.
Who This Book Is For This book is for anyone who wants to learn how to build rich and interactive web sites that run on the Microsoft platform. With the knowledge you gain from this book, you create a great foundation to build any type of web site, ranging from simple hobby-related web sites to sites you may be creating for commercial purposes.
introduction
Anyone new to web programming should be able to follow along because no prior background in web development is assumed, although it helps if you do have a basic understanding of HTML and the web in general. The book starts at the very beginning of web development by showing you how to obtain and install Visual Web Developer. The chapters that follow gradually introduce you to new technologies, building on top of the knowledge gained in the previous chapters. Do you have a strong preference for Visual Basic over C# or the other way around? Or do you think both languages are equally cool? Or maybe you haven’t made up your mind yet and want to learn both languages? Either way, you’ll like this book because all code examples are presented in both languages! Even if you have some experience with prior versions of ASP.NET, you may gain a lot from this book. Although many concepts from previous versions are brought forward into ASP.NET 4, you’ll discover there’s a lot of new stuff to be found in this book, including an introduction to the ADO.NET Entity Framework, the inclusion of jQuery, ASP.NET AJAX, the many changes to the ASP.NET 4 Framework, and much more.
What This Book Covers This book teaches you how to create a feature-rich, data-driven, and interactive web site called Planet Wrox. Although this is quite a mouthful, you’ll find that with Visual Web Developer 2010, developing such a web site isn’t as hard as it seems. You’ll see the entire process of building a web site, from installing Visual Web Developer 2010 in Chapter 1 all the way up to putting your web site on a live server in Chapter 19. The book is divided into 19 chapters, each dealing with a specific subject.
xxviii
➤➤
Chapter 1, “Getting Started with ASP.NET 4.” In this chapter you’ll see how to obtain and install Visual Web Developer 2010. You’ll get instructions for downloading and installing the free edition of Visual Web Developer 2010, called the Express edition. You are also introduced to HTML, the language behind every web page. The chapter closes with an overview of the customization options that Visual Web Developer gives you.
➤➤
Chapter 2, “Building an ASP.NET Web Site.” This chapter shows you how to create a new web site and how to add new elements like pages to it. Besides learning how to create a wellstructured site, you also see how to use the numerous tools in Visual Web Developer to create HTML and ASP.NET pages.
➤➤
Chapter 3, “Designing Your Web Pages.” Visual Web Developer comes with a host of tools that enable you to create well-designed and attractive web pages. In this chapter, you see how to make good use of these tools. Additionally, you learn about CSS, the language that is used to format web pages.
➤➤
Chapter 4, “Working with ASP.NET Server Controls.” ASP.NET Server Controls are one of the most important concepts in ASP.NET. They enable you to create complex and featurerich web sites with very little code. This chapter introduces you to the large number of server controls that are available, explains what they are used for, and shows you how to use them.
introduction
➤➤
Chapter 5, “Programming Your ASP.NET Web Pages.” Although the built-in CSS tools and the ASP.NET Server Controls can get you a long way in creating web pages, you are likely to use a programming language to enhance your pages. This chapter serves as an introduction to programming with a strong focus on programming web pages. Best of all: all the examples you see in this chapter (and the rest of the book) are in both Visual Basic and C#, so you can choose the language you like best.
➤➤
Chapter 6, “Creating Consistent Looking Web Sites.” Consistency is important to give your web site an attractive and professional appeal. ASP.NET helps you create consistent-looking pages through the use of master pages, which enable you to define the global look and feel of a page. Skins and themes help you to centralize the looks of controls and other visual elements in your site. You also see how to create a base page that helps to centralize programming code that you need on all pages in your site.
➤➤
Chapter 7, “Navigation.” To help your visitors find their way around your site, ASP.NET comes with a number of navigation controls. These controls are used to build the navigation structure of your site. They can be connected to your site’s central site map that defines the pages in your web site. You also learn how to programmatically send users from one page to another.
➤➤
Chapter 8, “User Controls.” User controls are reusable page fragments that can be used in multiple web pages. As such, they are great for repeating content such as menus, banners, and so on. In this chapter, you learn how to create and use user controls and enhance them with some programmatic intelligence.
➤➤
Chapter 9, “Validating User Input.” A large part of interactivity in your site is defined by the input of your users. This chapter shows you how to accept, validate, and process user input using ASP.NET Server Controls. Additionally, you see how to send e‑mail from your ASP.NET web site and how to read from text files.
➤➤
Chapter 10, “ASP.NET AJAX.” Microsoft ASP.NET AJAX enables you to create good-looking, flicker-free web pages that close the gap between traditional desktop applications and web sites. In this chapter you learn how to use the built-in Ajax features to enhance the presence of your web pages, resulting in a smoother interaction with the web site.
➤➤
Chapter 11, “jQuery.” jQuery is a popular, open source and cross-browser JavaScript library designed to make it easier to interact with web pages in the client’s browser. In this chapter you learn the basics of jQuery and see how to add rich visual effects and animations to your web pages.
➤➤
Chapter 12, “Introducing Databases.” Understanding how to use a database is critical to building web sites, as most modern web sites require the use of a database. You’ll learn the basics of SQL, the query language that enables you to access and alter data in a database. In addition, you are introduced to the database tools found in Visual Web Developer that help you create and manage your SQL Server databases.
xxix
introduction
➤➤
Chapter 13, “Displaying and Updating Data.” Building on the knowledge you gained in Chapter 12, this chapter shows you how to use the ASP.NET data-bound and data source controls to create a rich interface that enables your users to interact with the data in the database that these controls target.
➤➤
Chapter 14, “LINQ and the ADO.NET Entity Framework.” LINQ is Microsoft’s solution for accessing objects, databases, XML, and more. The ADO.NET Entity Framework (EF) is Microsoft’s new technology for database access. This chapter shows you what LINQ is all about, how to use the visual EF designer built into Visual Studio, and how to write LINQ to EF queries to get data in and out of your SQL Server database.
➤➤
Chapter 15, “Working with Data — Advanced Topics.” While earlier chapters focused mostly on the technical foundations of working with data, this chapter looks at the same topic from a front-end perspective. You see how to change the visual appearance of your data through the use of control styles. You also see how to interact with the data-bound controls and how to speed up your web site by keeping a local copy of frequently accessed data.
➤➤
Chapter 16, “Security in Your ASP.NET 4 Web Site.” Although presented quite late in the book, security is a first-class, important topic. This chapter shows you how to make use of the built-in ASP.NET features related to security. You learn about a number of application services that facilitate security. You also learn how to let users sign up for an account on your web site, how to distinguish between anonymous and logged-on users, and how to manage the users in your system.
➤➤
Chapter 17, “Personalizing Web Sites.” Building on the security features introduced in Chapter 16, this chapter shows you how to create personalized web pages with content targeted at individual users. You see how to configure and use ASP.NET Profile that enables you to store personalized data for known and anonymous visitors.
➤➤
Chapter 18, “Exception Handling, Debugging, and Tracing.” In order to understand, improve, and fix the code you write for your ASP.NET web pages you need good debugging tools. Visual Web Developer ships with great debugging support that enables you to diagnose the state of your application at runtime, helping you find and fix problems before your users do.
➤➤
Chapter 19, “Deploying Your Web Site.” By the end of the book, you should have a web site that is ready to be shown to the world. But how exactly do you do that? What are the things you need to know and understand to put your web site out in the wild? This chapter gives the answers and provides you with a good look at configuring different production systems in order to run your final web site.
How This Book Is Structured This book takes the time to explain concepts step by step using working examples and detailed explanations. Using the famous Wrox Try It Out and How It Works sections, you are guided through a task step by step, detailing important things as you progress through the task. Each
xxx
introduction
Try It Out task is followed by a detailed How It Works section that explains the steps you performed in the exercise. At the end of each chapter, you find exercises that help you test the knowledge you gained in this chapter. You’ll find the answers to each question in Appendix A at the end of this book. Don’t worry if you don’t know all the answers to the questions. Later chapters do not assume you followed and carried out the tasks from the exercise sections of previous chapters. Since this is a beginner’s book, I can’t go into great detail on a number of topics. For pretty much each chapter in this book, you’ll easily find numerous other books that exclusively deal with the topic discussed. Where appropriate, I have included references to these books so you can easily decide where to go to next if you want to deepen your knowledge on a specific subject.
What You Need to Use This Book This book assumes you have a system that meets the following requirements: ➤➤
Capable of running Visual Web Developer. For the exact system requirements, consult the readme file that comes with the software.
➤➤
Running Windows Vista or Windows 7 (both require at least the Home Premium edition), or one of the Windows Server 2008 editions.
Although you should be able to follow along with most of the exercises using another version of Windows such as Windows XP (as long as it’s supported by Visual Web Developer), the exercises in Chapter 19 on deployment require the use of Microsoft’s web server IIS 7 or later, which only ships with the Windows versions in the requirements list. Chapter 1 shows you how to obtain and install Visual Web Developer 2010, which in turn installs the Microsoft .NET Framework version 4 and SQL Server 2008 Express edition; all you need is a good operating system and the drive to read this book!
Conventions To help you get the most from the text and keep track of what’s happening, a number of conventions are used throughout the book.
Try It Out Conventions The Try It Out is an exercise you should work through, following the text in the book.
1. 2. 3. 4.
They usually consist of a set of steps. Each step has a number. Follow the steps through with your copy of the code. Then read the How It Works section to find out what’s going on. xxxi
introduction
How It Works After each Try It Out, the actions you carried out and the code you’ve typed in will be explained in detail.
NOTE Boxes like this one hold important, not-to-be forgotten information that is directly relevant to the surrounding text.
COMMON MISTAKES Mistakes that are easily made while following the exercises are presented in a box like this. Be sure to read these carefully when you get stuck in an exercise.
As for styles in the text: ➤➤
New terms and important words are italicized when they are introduced.
➤➤
Code within the text is presented like this: Request.QueryString.Get(“Id”)
➤➤
URLs that do not start with www are prefixed with http:// to make it clear it’s an Internet address. URLs within the text are presented like this: http://imar.spaanjaars.com.
➤➤
You’ll see many URLs that start with tinyurl.com which is a handy, online service to make URLs shorter (and thus easier to type). Entering a tinyurl.com address in your browser should take you to its final destination.
➤➤
Menu items that require you to click multiple submenus have a special symbol that looks like this: ➪. For example: File ➪ New ➪ Folder.
➤➤
Code or content irrelevant to the discussion is either left out completely or replaced with ellipsis points (three dots), like this:
... Menu items go here; not shown
The three dots are used regardless of the programming language used in the example, so you’ll see it for C#, Visual Basic, HTML, CSS, and JavaScript. When you see it in code you’re instructed to type into the code editor, you can simply skip the three dots and anything that follows them on the same line.
xxxii
introduction
➤➤
Code shown for the first time, or other relevant code, is in the following format: Dim roles As New ArrayList() roles.Add(“Administrators”) roles.Add(“ContentManagers”)
To put emphasis on a block of code surrounded by other code, I used a bolded font like this:
The surrounding code is used to make it easier to see where the bolded code should be placed. ➤➤
Quite often, white space in code is irrelevant, as is mostly the case with ASP.NET markup and HTML. To fit code within the boundaries of this book, I often wrap code over multiple lines and indent the part that should have been on the previous line like this:
If you’re typing this code yourself, you can put it all on one line, or use the same line breaks if you prefer. ➤➤
Text that appears on screen often has Each Word Start With A Capital Letter, even though the original screen text uses a different capitalization. This is done to make the screen text stand out from the rest of the text.
Source Code As you work through the examples in this book, you may choose either to type in all the code manually or to use the source code files that accompany the book. All of the source code used in this book is available for download from the book’s own page on the Wrox web site at www.wrox.com/ go/beginningaspnet4. If somehow this link no longer works, go to www.wrox.com and locate the book either by using the Search box or by using one of the title lists. Click the Download Code link on the book’s detail page to obtain all the source code for the book. Because many books have similar titles, you may find it easiest to search by ISBN; for this book the ISBN is 978-0-470-50221-1. You can download the full source for this book as a single file for each programming language used in the book (C# or Visual Basic). You can decompress these files with your favorite decompression tool. If you extract the source, make sure you maintain the original folder structure that is part of the code download. The different decompression tools use different names for this feature, but look for a feature like Use Folder Names or Maintain Directory Structure. Once you have extracted the files from the code download, you should end up with a folder called Source and a folder called Resources.
xxxiii
introduction
Then create a new folder in the root of your C drive, call it BegASPNET, and move the Source and Resources folders into this new folder so you end up with folders like C:\BegASPNET\Source and C:\BegASPNET\Resources. The Source folder contains the source for each of the 19 chapters of this book and the final version of the Planet Wrox web site. The Resources folder contains files you need during some of the exercises in this book. If everything turned out correctly, you should end up with the structure shown in Figure I-1.
Figure I-1
Later chapters have you create folders called Site and Release inside the same C:\BegASPNET folder giving you a folder structure similar to that in Figure I-2. The Site folder contains the site as you’ll build it throughout this book, while the Release folder will contain your final version at the end of this book. Whenever you’re stuck with some examples in this book, you can take a peek in the Source folder to see how things should have ended up. If you want to run the site for a specific chapter to see how it works, be sure to open the chapter’s folder in Visual Web Developer as a web site. So you should open a folder such as C:\BegASPNET\ Source\Chapter 12 directly rather than opening its parent folder C:\BegASPNET\Source.
xxxiv
introduction
Figure I-2
If you want to following along in both programming languages, create a second folder called C:\ BegASPNETVB to hold the files for the Visual Basic version. This way, the two sites can coexist without any problems. If you create a folder specifically for the C# language, don’t include the hash symbol (#) as that’s an invalid character in the path name for a web site. Sticking to this structure ensures a smooth execution of the Try It Out exercises in this book. Incorrectly mixing or nesting these folders make it harder to carry out the exercises and may even lead to unexpected situations and errors. Whenever you run into an issue or error that is not explained in this book, ensure that your site structure is still closely related to the one presented here.
Errata I have made every effort to ensure that there are no errors in the text or in the code. However, no one is perfect, and mistakes do occur. If you find an error in this book, such as a spelling mistake or a faulty piece of code, I’d be very grateful for your feedback. By sending in errata you may save another reader hours of frustration and at the same time you will be helping me provide even higher quality information. To find the errata page for this book, go to www.wrox.com/go/beginningaspnet4 or go to www.wrox .com and locate the title using the Search box or one of the title lists. Then, on the book details page, click the Errata link. On this page you can view all errata that has been submitted for this book and posted by Wrox editors. A complete book list including links to each book’s errata is also available at www.wrox.com/misc-pages/booklist.shtml. If you don’t spot “your” error on the book’s Errata page, go to www.wrox.com/contact/techsupport .shtml and complete the form there to send us the error you have found. I’ll check the information and, if appropriate, post a message to the book’s errata page and fix the problem in subsequent editions of the book.
xxxv
introduction
p2p.wrox.com For author and peer discussion, join the P2P forums at p2p.wrox.com. The forums are a web-based system for you to post messages relating to Wrox books and related technologies and interact with other readers and technology users. The forums offer a subscription feature to e‑mail you topics of interest of your choosing when new posts are made to the forums. I am a frequent visitor of the Wrox forums, and I’ll do my best to help you with any questions you may have about this book. At p2p.wrox.com you will find a number of different forums that will help you not only as you read this book, but also as you develop your own applications. To join the forums, just follow these steps: Go to p2p.wrox.com and click the Register Now link.
1. 2. 3.
4.
You will receive an e‑mail with information describing how to verify your account and complete the joining process.
Read the terms of use and click Agree. Complete the required information to join as well as any optional information you wish to provide and click Submit.
You can read messages in the forums without joining P2P but in order to post your own messages, you must join (which is free). After you join, you can post new messages and respond to messages other users post. You’ll find this book’s own forum under the ASP.NET 4 category that is available from the homepage. You can read messages at any time on the Web. If you would like to have new messages from a particular forum e‑mailed to you, click the Subscribe to this Forum icon by the forum name in the forum listing. For more information about how to use the Wrox P2P, be sure to read the P2P FAQs for answers to questions about how the forum software works as well as many common questions specific to P2P and Wrox books. To read the FAQs, click the FAQ link on any P2P page.
xxxvi
1
Getting Started with ASP.NET 4 What you will learn in this chapter: ➤➤
How to acquire and install Visual Web Developer 2010 Express and Visual Studio 2010
➤➤
How to create your first web site with Visual Web Developer
➤➤
How an ASP.NET page is processed by the server and sent to the browser
➤➤
How you can use and customize the development environment
Ever since the first release of the .NET Framework 1.0 in early 2002, Microsoft has put a lot of effort and development time into ASP.NET, the part of the .NET Framework that enables you to build rich web applications. This first release meant a radical change from the older Microsoft technology to build web sites called Active Server Pages (ASP), now often referred to as classic ASP. The introduction of ASP.NET 1.0 and the associated Visual Studio .NET 2002 gave developers the following benefits over classic ASP: ➤➤
A clean separation between presentation and code. With classic ASP, your programming logic was often scattered throughout the HTML of the page, making it hard to make changes to the page later.
➤➤
A development model that was much closer to the way desktop applications are programmed. This made it easier for the many Visual Basic desktop programmers to make the switch to web applications.
➤➤
A feature-rich development tool (called Visual Studio .NET) that allowed developers to create and code their web applications visually.
➤➤
A choice between a number of object-oriented programming languages, of which Visual Basic .NET and C# (pronounced as C-Sharp) are now the most popular.
2 ❘ Chapter 1 Getting Started with ASP.NET 4
➤➤
Access to the entire .NET Framework, which for the first time meant that web developers had a unified and easy way to access many advanced features to work with databases, files, e‑mail, networking tools, and much more.
Despite the many advantages of ASP.NET over the older model, using ASP.NET also meant an increase of complexity and the knowledge you needed to build applications with it, making it harder for many new programmers to get started with ASP.NET. After the initial release in 2002, Microsoft released another version of the .NET Framework (called .NET 1.1) and the development IDE Visual Studio .NET in 2003. Many people saw this as a service pack for the initial release, although it also brought a lot of new enhancements in both the framework and the development tools. In November 2005, Visual Studio 2005 and ASP.NET 2.0 were released. To the pleasant surprise of many developers around the world, Microsoft had again been able to drastically improve and expand the product, adding many features and tools that helped reduce the complexity that was introduced with ASP.NET 1.0. New wizards and smart controls made it possible to reduce the code required to build an application, decreasing the learning curve for new developers and increasing their productivity. Although Visual Studio 2005 and ASP.NET 2.0 were already very feature rich, Microsoft managed again to add a whole bunch of cool new features in Visual Studio 2008 and ASP.NET 3.5, which were released in November 2007. Major new functionality included LINQ (discussed in Chapter 14) and the integration of the AJAX Framework (which you learn more about in Chapter 10). In August 2008 Microsoft released Service Pack 1 for Visual Studio and the .NET Framework, introducing major new features like the ADO.NET Entity Framework (discussed in Chapter 14) and Dynamic Data. The current versions, Visual Studio 2010 (often pronounced as “twenty-ten”) and ASP.NET 4, build on top of the successful Visual Studio 2008 and ASP.NET 3.5 releases, leaving many of the beloved features in place, while adding new features and tools in other areas. Over the next 19 chapters, you learn how to build full-featured ASP.NET web sites using Visual Web Developer 2010, Microsoft’s development tool for ASP.NET web applications, which is part of the full Visual Studio 2010 suite. This book guides you through the process of creating a fully functional, database-driven web site, starting with a bare-bones web site in this chapter, all the way down to the deployment of it to a production environment in Chapter 19. The sample site that comes with this book and all the examples are built with Visual Web Developer 2010 (VWD), so it’s important that you have it installed on your development machine. The next section shows you how to acquire and install VWD. Once you have it up and running, you see how to create your first web site, followed by an extensive tour through the many features of VWD.
Microsoft Visual Web Developer Although you could theoretically write ASP.NET web applications with Notepad or another text editor alone, you really want to install a copy of Microsoft Visual Web Developer 2010. VWD is developed specifically for building ASP.NET web sites, and as such, hosts an enormous amount of tools that will help you in rapidly creating complex ASP.NET web applications.
Microsoft Visual Web Developer ❘ 3
Visual Web Developer comes in two flavors: as a standalone and free version called Microsoft Visual Web Developer 2010 Express, and as part of the larger development suite called Visual Studio 2010, which is also available in different editions, each with its own price tag. Although the Express edition of VWD is free, it contains all the features and tools you need to create complex and feature-rich web applications. All the examples you find in the book can be built with the free Express edition so there’s no need to shell out big bucks for the commercial versions of Visual Studio 2010 to follow along with this book. Getting VWD is easy. You can download it from the Microsoft site as discussed next.
Getting Visual Web Developer You can get the free version of VWD from the Microsoft site at www.microsoft.com/express/. On the Express home page, follow the Downloads link until you reach the page that offers the downloads for the Express products, including Visual Web Developer 2010 Express. From this page, you can download Visual Web Developer 2010 Express as a Web Install, where you download only the installer, while the remaining files are downloaded during the installation process. Make sure you choose Visual Web Developer 2010 from the page, and not one of the other free Express products or one of the older editions of Visual Web Developer. The page also enables you to download all Express products conveniently as an ISO image that you can burn onto a DVD. Don’t be fooled by the file size of the Web Install download, which is around 3.5MB. The file you download is just the installer that downloads the required files over the Internet. The total download depends on your current system and will be somewhere between 180 MB and 270 MB. If you want to try out the full version of Visual Studio 2010, which also contains Visual Web Developer, you can sign up for a free trial that you can get from the Microsoft site at http://msdn .microsoft.com/vstudio. You can choose to download an ISO image that you’ll need to burn on a DVD. Finally, you can download VWD as part of the Microsoft Web Platform Installer (WPI) application available for download at www.microsoft.com/web and at www.asp.net/vwd/. Besides VWD, this tool also gives you easy access to many other web development related tools and programs. The WPI is an excellent tool to get a whole bunch of web development-related programs and tools in one fell swoop. I often use it to get up and running real quick on a clean development machine.
Installing Visual Web Developer Express Installing Visual Web Developer is a straightforward, although somewhat lengthy, process. Depending on your installation method, your computer and your Internet connection speed, installing VWD may take anywhere between twenty minutes and an hour or even more.
Try It Out
Installing Visual Web Developer 2010 Express
This Try It Out exercise guides you through installing VWD Express on your computer. It assumes you’re using the web download option as explained earlier, although the process for installing the Express edition from a DVD is almost identical. The steps you need to perform to install the full versions of Visual Studio 2010 are similar as well, although the screens you’ll see will be somewhat different.
4 ❘ Chapter 1 Getting Started with ASP.NET 4
No matter which version of VWD you install, it’s important that you also install SQL Server 2008 Express with Service Pack 1 — a required component if you want to follow along with many of this book’s examples. When you install the full version of Visual Studio 2010, the option to install SQL Server is included on the list with features to install that you see during setup. If you install VWD Express, you get the option to choose SQL Server on the Installation Options dialog box. The Web Platform Installer has a similar option that enables you to install SQL Server 2008 Express with SP1 or later located under Web Platform ➪ Database. 1.
When you’re installing the web version, run the file you downloaded from the Microsoft web site. Otherwise, start the setup process from the Visual Web Developer DVD.
. 2
Once the installer has started, click Next, read and accept the license terms, and click Next once more.
. 3
On the Installation Options page, make sure you select Microsoft SQL Server 2008 Express. Although this option adds considerably to the size of the download, you really need it when building data-driven ASP.NET web applications. If you don’t see the SQL Server option, you already have it installed. If you’re unsure whether SQL Server 2008 is installed, refer to Appendix B. Click Next again. You may also see other optional components such as Microsoft Silverlight that you can install as well, although they are not required for this book.
. 4
On the Destination Folder page, you can leave the Install In Folder field set to its default if you have enough space on your primary disk. Otherwise, click the Browse button and select a different location.
. 5
Click the Install button. If you’re using the web-based installer, the setup application will first download the files over the Internet to your computer. During the installation process, you’ll see a screen (similar to Figure 1-1) that shows you the progress of the download and installation of VWD.
Figure 1-1
6.
The installer may need to reboot your machine during or after the installation. Once the installer has finished, VWD is ready for use.
Creating Your First ASP.NET 4 Web Site ❘ 5
How It Works The straightforward installation process guided you through the setup of VWD 2010 Express. In the Installation Options dialog box, you selected Microsoft SQL Server 2008 Express, Microsoft’s free version of its database engine. SQL Server 2008 is discussed and used a lot in this book, starting with Chapter 12. Appendix B shows you how to configure security settings for the various versions of SQL Server 2008 using the free SQL Server Management Studio Express.
Now that VWD is installed, it’s time to fire it up and start working with it. The next section shows you how to create your very first site in VWD. You see how to create a site, add content to a web page, and view that page in your browser.
Creating Your First ASP.NET 4 Web Site You probably can’t wait to get started with your first ASP.NET web site, so instead of giving you a theoretical overview of web sites in VWD, the next Try It Out exercise dives right into the action and shows you how to build your first web project. Then, in the How It Works explanation and the section that follows, you get a good look at what goes on behind the scenes when you view an ASP.NET page in your browser.
Try It Out
Creating Your First ASP.NET Web Site
1.
Start VWD 2010 from the Windows Start menu if you haven’t done so already. The first time you start VWD, there might be a delay before you can use VWD because it’s busy configuring itself. Subsequent starts of the application will go much faster.
. 2
If you’re using a commercial version of Visual Studio, you also get a dialog box that lets you choose between different collections of settings the first time you start Visual Studio. The choice you make on that dialog box influences the layout of windows, toolboxes, menus, and shortcuts. Choose the Web Development settings because those settings are designed specifically for ASP.NET developers. You can always choose a different profile later by resetting your settings, as explained later in this chapter.
. 3
Once VWD is fully configured, you see the main screen appear, as shown in Figure 1-2. You get a full description of all the windows, toolbars, panels, and menus in the next section, so for now, just focus on creating a new web site. Click the File menu in the upper-left corner and choose New Web Site. If you’re using a commercial version of Visual Studio, depending on the settings you chose when starting Visual Studio the first time, you may have to open the submenu New first. (Make sure you don’t accidentally use the New Project menu, because that is used to create different types of .NET applications.) The New Web Site dialog box appears as shown in Figure 1-3.
6 ❘ Chapter 1 Getting Started with ASP.NET 4
Figure 1-2
Figure 1-3
4.
In the Installed Templates section on the left you can choose a programming language you will use for your site. This book shows all examples in both Visual Basic and Visual C# so you can choose a language to your liking.
Creating Your First ASP.NET 4 Web Site ❘ 7
5.
In the list with templates in the middle, verify that ASP.NET Web Site is selected. Verify that File System is the selected option in the Web Location drop-down list at the bottom left. If you want, you could change the location on disk where the web site is stored by clicking the Browse button and choosing a new location on your computer’s hard drive. For now, the default location — a folder under your Documents folder — is fine, so you can leave the location as is.
. 6
Click OK. VWD creates a new web site for you that includes a number of files and folders to jump start your web site as shown in Figure 1-4. It also opens the file Default.aspx so you can see the code for the page.
. 7
Remove the code inside the block (it starts with and ends with ) and replace it with the following bolded text and code:
Figure 1-4
Hello World
Welcome to Beginning ASP.NET 4 on
You’ll see code formatted like this a lot more in this book. When you are instructed to type in code formatted like this with some code in bold, you only need to type in the highlighted code. The other code should already be present in the file. Don’t worry about the code with the angle brackets () and percentage symbol in the welcome message; you learn how it works later. Although this code may not look familiar to you now, you can probably guess what it does: it writes out today’s date and time. . 8
Press Ctrl+F5 to open the page in your default web browser. You see a page similar to the one shown in Figure 1-5.
Figure 1-5
If you get a dialog box asking for your user name and password, close your browser and go back to VWD. Right-click your site in the Solution Explorer (it’s the first item in Figure 1-4) and choose Property Pages. In the Start Options section clear the checkbox for the NTLM Authentication item. Then click OK and press Ctrl+F5 again to view the page in the browser. If you see an information
8 ❘ Chapter 1 Getting Started with ASP.NET 4
bar warning about Intranet settings in Internet Explorer, click the bar and choose Enable Intranet Settings. If you want to learn more about the implications of these settings first, choose What are Intranet Settings from the popup menu. If you don’t see the date and time in the page, or if you get an error, look again at the code in the welcome message. It starts with an angle bracket (). Also, make sure you typed in the code exactly as shown here, including capitalization. This is especially true when you are using C#, because that language is case sensitive.
9 .
Notice how a small icon with a screen tip appeared in the tray bar of Windows, visible in Figure 1-6. If you don’t see the icon, right-click the arrow near the other icons in the Windows tray and choose Customize Notification Icons. Then set the WebDev.WebServer40.exe option to Show Icon and Figure 1-6 Notifications. The icon belongs to the ASP.NET Development Server. This web server has been started by VWD automatically to serve the request for your page. You learn more about how the web server processes your page later in this chapter. That’s it. You just created your very first ASP.NET 4 web site with VWD.
How It Works Although the web site you created in this Try It Out is quite simple, the process that eventually results in the page Default.aspx being displayed in your browser isn’t so simple. All by itself, an ASP.NET page (also referred to as an ASPX page because of its extension) can’t do much. It needs to be processed and served by a web server before your browser can display it. That’s why VWD automatically started up the built-in ASP.NET Development Server to handle the request for the page. Next, it started up your default web browser and directed it to the address of the web server, http://localhost:49212/ WebSite1/Default.aspx in the Try It Out example, although the actual number in the address may change every time you start the web server because the number is randomly chosen by VWD. It’s important to realize that the ASPX file you modified in VWD is not the same as the one that eventually gets displayed by the browser. When you create a page in VWD, you add markup to it. The markup in an ASPX page is a combination of plain text, HTML, code for ASP.NET Server Controls (which you learn more about in this chapter and in Chapter 4), code written in Visual Basic.NET or C#, and more. When you request an ASPX page in your browser, the web server processes the page, executes any code it finds in the file, and effectively transforms the ASP.NET markup into plain HTML that it then sends to the browser, where it is displayed. In the preceding Try It Out, the resulting HTML causes the browser to display the current date and time. HTML, or HyperText Markup Language, is the language that browsers use to display a web page. You learn how HTML looks and how to use it later in this chapter. To see how the final HTML differs from the original ASPX page, open the source for the page in your browser. In most browsers, you can bring up the source window by right-clicking the page in the browser and choosing View Source or View Page Source. This brings up your default text editor, showing the HTML for the page.
An Introduction to ASP.NET 4 ❘ 9
If you already closed your browser after the preceding Try It Out, press Ctrl+F5 in VWD to open the page and choose View Source again. Most of the HTML you see in the text editor is similar to the original ASPX page. However, if you look at the line that displays the welcome message and the current date and time, you’ll notice a big difference. Instead of the code between the angle brackets and percentage signs, you now see the actual date and time: Hello World
Welcome to Beginning ASP.NET 4 on 10/30/2009 6:19:16 PM
When the web server processed the page, it looked up the current date and time from the server, and inserted it in the HTML that got sent to the browser. Depending on the language settings of your Windows installation, you may see the date and time formatted differently to accommodate the Windows Regional Settings.
In the following section, you see how ASP.NET works in much more detail.
An Introduction to ASP.NET 4 When you type a web address like www.wrox.com in your web browser and press Enter, the browser sends a request to the web server at that address. This is done through HTTP, the HyperText Transfer Protocol. HTTP is the protocol by which web browsers and web servers communicate. When you send the address, you send a request to the server. When the server is active and the request is valid, the server accepts the request, processes it, and then sends the response back to the client browser. The relationship between the request and response is shown in Figure 1-7. Because you are using the built-in Development Web Server, the server and the client are really the same machine. However, in a real-world scenario, you’ll host your web site on an external web server where it can be accessed by many different clients. For simple, static files, like HTML files or images, the web server simply reads the file from its local hard drive and sends it to the browser. However, for dynamic files, such as ASPX pages, this is obviously not good enough. If the web server were to send the ASPX file directly to the browser as a text file, you wouldn’t have seen the current date and time in the browser, but instead you would have seen the actual code (). So, instead of sending the file directly, the web server hands over the request to another piece of software that is able to process the page. This is done with a concept called Application Mapping or Handler Mapping, where an extension of a file (.aspx in this example) is
Web Server Request 1
2 Response
Browser Figure 1-7
10 ❘ Chapter 1 Getting Started with ASP.NET 4
mapped to an application that is capable of handling it. In the case of an .aspx page, the request is eventually handled and processed by the ASP.NET runtime, part of the Microsoft .NET Framework designed specifically to handle web requests. During the processing of the page, three main areas can influence the way the page eventually ends up in the browser: ➤➤
Static text. Any static text, like HTML, CSS, or JavaScript code you place in a page, is sent to the browser directly. You learn more about HTML, CSS, and JavaScript (a programming language used at the client) in this and subsequent chapters, including Chapter 3, which gives you a detailed look at CSS.
➤➤
ASP.NET Server Controls. These controls are placed in your ASPX page and when they are processed, they emit HTML that is inserted in the page. You learn more about Server Controls after the discussion of HTML in this chapter, and Chapter 4 is devoted entirely to ASP.NET Server Controls.
➤➤
Programming code. You can embed code, like Visual Basic .NET or C#, directly in a page, as you saw in the previous Try It Out. In addition, you can place code in a separate code file, called a Code Behind file. This code can be executed by the runtime automatically, or based on a user’s action. Either way, execution of the code can greatly influence the way the page is displayed, by accessing databases, performing calculations, hiding or showing specific controls, and much more. You learn more about this Code Behind file in the next chapter, and programming your ASP.NET web pages is discussed in great detail in Chapter 5.
Once the page is done processing, and all the HTML for the page has been collected, the HTML is sent back to the browser. The browser then reads it, parses it and, finally, displays the page for you to look at. Because HTML is so critical for displaying web pages, the next section gives you an overview of HTML.
Understanding HTML HTML is the de facto language for creating web pages and is understood by every web browser that exists today. Since the beginning of the ‘90s it has been the driving force of the World Wide Web, the part of the Internet that deals with web pages. HTML documents are simple text files that contain markup, text, and additional data that influences that text.
HTML Elements and Tags HTML uses text surrounded by angle brackets to indicate how your content should be rendered (or displayed) in the browser. The text with angle brackets is referred to as a tag; a pair of tags holding some text or other content is referred to as an element. Take another look at the HTML you saw in the previous Try It Out where you opened the source window for the page in the browser: Hello World
Welcome to Beginning ASP.NET 4 on 10/30/2009 6:19:16 PM
The first line of this example contains an element with an opening tag () and a closing tag (). This element is used to signify a heading at the second level (if you scroll up a bit in the
An Introduction to ASP.NET 4 ❘ 11
final source in the browser, you also see an element). Notice how the element is closed with a similar tag, but with an additional forward slash (/) in it: . Any text between these opening and closing tags is considered part of the element, and is thus rendered as a heading. In most browsers, this means the text is rendered in a larger font. Similar to the tag are tags for creating headings up to level six, such as , , and so on. Below the heading element, you see a
element, which is used to denote a paragraph. All text within the pair of
tags is considered part of the paragraph. By default, a browser renders a paragraph with some additional margin spacing at the bottom, although you can override that behavior. Many tags are available in HTML; too many to cover them all here. The following table lists some of the most important tags and describes how they can be used. For a complete list of all HTML elements, take a look at the web site of the organization that maintains the HTML standard: www.w3.org/TR/html401/index/elements.html. Tag
Description
Ex ample
Used to denote the start and end of the entire page.
...All other content goes here
Used to denote a special section of the page that contains data about the page, including its title and references to external resources.
... Content goes here
Used to define the title of the page. This title will appear in the browser’s title bar.
Welcome to Planet Wrox 4
Used to denote the start and end of the body of the page.
Page body goes here
Used to link one web page to another.
Visit the Wrox site
Used to embed images in a page.
Used to format text in a bold, italic, or underline font.
This is bold text while this text is in italic
<select>
Used for input forms that enable users to submit information to the server.
continues
12 ❘ Chapter 1 Getting Started with ASP.NET 4
(continued) Tag
Description
Ex ample
These tags are used to create a layout with a table. The
tag defines the entire table, and the
and
tags are used to define rows and cells, respectively.
This is a Cell in Column 1
This is a Cell in Column 2
These three tags are used to create numbered or bulleted lists. The
and the tags define the looks of the list (either unordered, with a simple bullet, or ordered, with a number), and the
tag is used to represent items in the list.
First item with a bullet
Second item with a bullet
First item with a number
Second item with a number
<span>
This tag is used to wrap and influence other parts of the document. It appears as inline, so it adds no additional line break on the screen.
This is some normal text while <span style=”color: red;”>this text appears in red
Just like the <span> tag, the tag is used as a container for other elements. However, the acts as a block element, which causes an explicit line break after the element by default.
This is some text on 1 line This text is put directly under the previous text on a new line.
HTML Attributes In addition to the HTML elements, the examples in the preceding table also showed you HTML attributes. Attributes contain additional information that changes the way a specific element behaves. For example, with the tag that is used to display an image, the src attribute defines the source of that image. Similarly, the <span> tag contains a style attribute that changes the color of the text to red. The value of the style attribute (color: red;) is part of a Cascading Style Sheet (CSS), which is discussed in much more detail in Chapter 3. Just as with the HTML elements, there is a long list of available attributes on the W3C web site: www.w3.org/TR/html401/index/attributes.html. You don’t need to memorize all these elements and attributes. Most of the time, they are generated for you automatically by VWD. In other cases, where you need to enter them by hand, VWD offers you IntelliSense to help you find the right tag or attribute. IntelliSense is discussed in the next chapter.
An Introduction to ASP.NET 4 ❘ 13
The Difference Between HTML and XHTML In addition to HTML, you may also run into the term XHTML. Although the two have very similar names, they have some interesting differences that you need to be aware of. XHTML is a reformulation of HTML in XML — eXtensible Markup Language. This is a generic, text- and tag-based language used to describe data and is used as the base language for many other languages, including XHTML. So, XHTML is in fact largely just HTML rewritten with XML rules. These rules are pretty simple, and most of the time VWD will help you get it right or show you a list of errors and suggestions on how to fix them.
Always Close Your Elements In XHTML, all elements must be closed. So when you start a paragraph with
, you must use
somewhere later in your page to close the paragraph. This is also the case for elements that don’t have their own closing tags, like or (to enter a line break). In XHTML, these tags are written as self-closing tags, where the closing slash is embedded directly in the tag itself as in or .
Always Use Lowercase for Your Tag and Attribute Names XML is case sensitive, and XHTML applies that rule by forcing you to write all your tags in lowercase. Although the tags and attributes must be in all lowercase, the actual value doesn’t have to be. So, the preceding example that displays the logo image is perfectly valid XHTML, despite the uppercase L in the image name.
Always Enclose Attribute Values in Quotes Whenever you write an attribute in a tag, make sure you wrap its value in quotes. For example, when writing out the tag and its src attribute, write it like this:
And not like this:
You could also use single quotes to enclose the attribute value, as in this example:
It’s also sometimes necessary to nest single and double quotes. When some special ASP.NET syntax requires the use of double quotes, you should use single quotes to wrap the attribute’s value:
You’ll see this syntax used a lot more in later chapters in this book. For consistency, this book uses double quotes where possible in all HTML that ends up in the client.
14 ❘ Chapter 1 Getting Started with ASP.NET 4
Nest Your Elements Correctly When you write nested elements, make sure that you first close the inner element you opened last, and then close the outer element. Consider this correct example that formats a piece of text with both bold and italic fonts: This is some formatted text
Notice how the tag is closed before the tag. Swapping the order of the closing tags leads to invalid XHTML: This is some formatted text
Always Add a DOCTYPE Declaration to Your Page A DOCTYPE gives the browser information about the kind of HTML it can expect. By default, VWD adds a DOCTYPE for XHTML 1.0 Transitional to your page:
The DOCTYPE greatly influences the way browsers like Internet Explorer render the page. VWD’s default DOCTYPE of XHTML 1.0 Transitional gives you a good mix between valid markup and pages that render the same in all major browsers.
NOTE If you want to learn more about XHTML, get a copy of Wrox’s Beginning Web Programming with HTML, XHTML, and CSS, 2nd Edition, ISBN: 978-0-47025931-3.
Besides HTML, an ASP.NET web page can contain other markup as well. Most pages will have one or more ASP.NET Server Controls on the page to give it some additional functionality. The next section briefly looks at these ASP.NET Server Controls, but you get an in-depth look at them in Chapter 4.
A First Look at ASP.NET Markup To some extent, the markup for ASP.NET Server Controls is similar to that of HTML. It also has the notion of tags, elements, and attributes, using the same angle brackets and closing tags as HTML does. However, some differences also exist. For starters, most of the ASP.NET tags start with an asp: prefix. For example, a button in ASP.NET looks like this:
A Tour of the IDE ❘ 15
Note how the tag is self-closed with the trailing slash (/) character, eliminating the need to type a separate closing tag. Another thing you may have noticed is that the tag and attribute names are not necessarily in all lowercase. Because an ASP.NET Server Control lives on the server, it doesn’t have to adhere to the XHTML rules used in the browser at the client. However, when a Server Control is asked to emit its HTML to a page that is configured to output XHTML, it will do so in XHTML. So, the code for the same button looks like this when rendered in the browser as XHTML:
Notice how the entire tag and its attributes conform to the XHTML standard. The process of converting the Server Control to its HTML representation is similar to the code you saw earlier that displayed the current date. The Server Control is processed at the server by the ASP.NET handler. This processing results in HTML, which is sent to the browser where it’s displayed. Now that you understand the basics of an ASP.NET page and the HTML that it generates, it’s time to look at VWD again. Knowing how to use the application and its many tools and windows is an important step in building fun, good-looking, and functional web sites.
A Tour of the IDE VWD is by far the most extensive and feature-rich integrated development environment (IDE) for building ASP.NET web pages. The abbreviation IDE refers to the way all the separate tools you need to build complex web applications are integrated in a single environment. Instead of writing code in a text editor, compiling code at the command line, writing HTML and CSS in a separate application, and managing your database in yet another, VWD enables you to perform all of these tasks, and more, from the same environment. Besides the efficiency this brings because you don’t have to constantly switch tools, this also makes it much easier to learn new areas of VWD, because many of the built-in tools work in the same way.
The Main Development Area To get familiar with the many tools that are packed in VWD’s interface, take a look at Figure 1-8. It shows the same screen you got after you created your first web site in VWD, but now it highlights some of the most important screen elements. If you are already familiar with a previous version of Visual Web Developer, you could skip this section and pick up again at the next Try It Out exercise later in this chapter. If you had a previous version of Visual Studio installed, your screen may look different, because Visual Studio 2010 is able to import settings from older versions.
16 ❘ Chapter 1 Getting Started with ASP.NET 4
Main Menu Toolbar Area
Toolbox Solution Explorer
Database Explorer
Document Window
Properties Grid
Figure 1-8
Choosing Your Development Profile Because Visual Web Developer Express targets people new to ASP.NET development as well as seasoned web developers, you can choose among different developer profiles: Basic Settings, Code Optimized, and Expert Settings. In Basic Settings mode, many menu items you don’t frequently use have been hidden or are placed in their own sub menu. The Code Optimized profile is great for pure coding sessions where you’re not interested in many of the design features of VWD. It hides items like the Toolbox and the Properties Grid, both shown in Figure 1-8. Expert Settings mode gives you access to the full functionality of VWD. You can switch between settings using the Tools ➪ Settings menu. This book assumes you are using Expert Settings mode right from the beginning. You may not need all features you see right from the start but you sure will use most of them by the end of the book. Since the menu items change location depending on the profile you choose, I decided to use Expert Settings mode right away, to make it easier to refer to a specific menu item or feature.
The Main Menu At the top of the application, right below the Windows title bar, you see the main menu. This menu bar contains familiar items you find in many other Windows applications, like the File, Edit, and Help menus as well as menus that are specific to VWD, such as the Website and Debug menus. The menu changes dynamically depending on the task you’re working on, so you’ll see menu items appear and disappear as you work your way through the application. You can use the Help ➪ Manage Help Settings menu to configure online and offline help. Offline helps needs to be installed first, while online help requires a connection to the Internet.
The Toolbar Area Right below the menu, you see the toolbar area that is capable of showing different toolbars that give you quick access to the most common functions in VWD. In Figure 1-8, only two of the
A Tour of the IDE ❘ 17
toolbars are enabled, but VWD comes with many other toolbars that you can use in specific taskoriented scenarios. Some toolbars appear automatically when you’re working on a task that requires a particular toolbar’s presence, but you can also enable and disable toolbars to your liking. To enable or disable a toolbar, right-click an existing toolbar or the menu bar and choose the toolbar from the menu that appears.
The Toolbox On the left of the main screen, tucked away at the border of VWD, you see the tab for the Toolbox. If you hover your mouse over the tab, the Toolbox folds out, giving you a chance to see what it contains. If you click the little pin icon in the upper-right corner of the Toolbox (or any of the other panels that have this pin icon), it gets pinned to the IDE so it remains open. Just as with the menu bar and the toolbars, the Toolbox automatically updates itself to show content that is relevant to the task you’re working on. When you’re editing a standard ASPX page, the Toolbox shows the many controls you have available for your page. You can simply drag an item from the Toolbox and drop it on a location of your page where you want it to appear. These controls are discussed in great detail in Chapter 4. The Toolbox contains multiple categories with tools that can be expanded and collapsed as you see fit to make it easier to find the right tool. You can also reorder the items in the list, add and remove items from the Toolbox, and even add your own tools to it. Customizing the IDE is discussed later in this chapter. If the Toolbox is not visible on-screen, press Ctrl+Alt+X to open it or choose View ➪ Toolbox, provided you have chosen the Expert Settings option in the Tools ➪ Settings menu. The two additional tabs below the Toolbox tab, CSS Properties and Manage Styles, are discussed extensively in Chapter 3.
The Solution Explorer At the right of the screen, you see the Solution Explorer. The Solution Explorer is an important window because it gives you an overview of the files that comprise your web site. Instead of placing all your files in one big folder, the Solution Explorer enables you to store files in separate folders, creating a logical and organized site structure. You can use the Solution Explorer to add new files to your site, move existing files around using drag and drop or cut and paste, rename files and delete them from the project, and more. Much of the functionality of the Solution Explorer is hidden behind its right-click menu, which changes depending on the item you right-click. At the top of the Solution Explorer, you see a small toolbar that gives you quick access to some functionality related to your web site, including opening the Properties Grid for the selected item, refreshing the Solution Explorer window, an option to nest related files, and two buttons that allow you to copy and configure your web site. Most of this functionality is discussed later in the book. You can access the Solution Explorer by choosing View ➪ Solution Explorer from the main menu or by pressing Ctrl+Alt+L.
18 ❘ Chapter 1 Getting Started with ASP.NET 4
The Database Explorer This window, hidden behind the Solution Explorer in Figure 1-8, enables you to work with your databases. If you have a commercial version of Visual Studio, such as Visual Studio 2010 Professional, this window is called the Server Explorer and may be located at the left of your screen. The Database Explorer is discussed in more detail in the chapters about databases, starting with Chapter 12.
The Properties Grid With the Properties Grid, you can view and edit the properties of many items in Visual Studio, including files in the Solution Explorer, controls on a web page, properties of the page itself, and much more. The window constantly updates itself to reflect the selected item. You can quickly open the Properties Grid by pressing F4. This same shortcut can be used to force the Properties Grid to show the details of a selected item.
The Document Window The Document Window is the main area in the middle of the application. This is where most of the action takes place. You can use the Document Window to work with many different document formats, including ASPX and HTML files, CSS and JavaScript files, code files for VB and C#, XML and text files, and even images. In addition, you can use the same window to manage databases, create copies of your site, view the pages in your site in the built-in mini-browser, and much more. At the bottom of the Document Window in Figure 1-8, you see three buttons called Design, Split, and Source. These buttons appear automatically when you’re working with a file that contains markup, such as ASPX and HTML pages. They allow you to open the Design View of a page (giving you an idea of how the page will look in the browser), its Markup View (the HTML and other markup), or both at the same time. How this works is explained in more detail in Chapter 2, but for now, it’s important to realize you can switch between Markup, Split, and Design View by clicking the appropriate buttons. The Markup View is also often called the Source View or Code View window. However, to avoid confusion with the code editor that is used to edit Code Behind files, this book uses the term Markup View exclusively. The Document Window is a tabbed window by default, which means it can host multiple documents, each one distinguished by a tab with the file name at the top of the window. The right-click menu of each tab contains some useful shortcuts for working with the file, including saving and closing it and opening the file’s parent folder in Windows Explorer. To switch between documents, you can press Ctrl+Tab or you can click the down arrow in the upper-right corner of the Document Window, next to the Solution Explorer, shown in Figure 1-8. Clicking the down arrow reveals a list of open documents so you can easily select one. Another way to switch documents is to press Ctrl+Tab and then hold down the Ctrl key. On the window that pops ups, you can select a document you want to work with in the right-hand column. You can then use the cursor keys to move up and down in the list with open documents. This makes it super easy to select the correct file.
A Tour of the IDE ❘ 19
On the same dialog box, you see a list with all active tool windows. Clicking one of the windows in the list will show it on-screen, moving it in front of other windows if necessary.
The Start Page Whenever you start up VWD, the Start Page is loaded in the Document Window. With the Start Page, you can quickly create new or open existing web sites and other projects. The Start Page also provides a number of links to related news and information about web development. To get a feel for how you can use all these windows, the following Try It Out shows you how to build a simple web page that contains a few ASP.NET Server Controls.
Try It Out
Creating Your First ASP.NET Web Page
This Try It Out exercise guides you through creating a new web site with a single page that contains a number of ASP.NET Server Controls. You see how to use windows like the Document Window and the Solution Explorer, and how to use the Toolbox and the Properties Grid to add ASP.NET Server Controls to the page and change their looks. 1.
Make sure Visual Web Developer 2010 is started.
. 2
If you’re using the Express edition, choose Tools ➪ Settings and choose Expert Settings to turn on the developer profile that gives you access to the full feature set of VWD.
. 3
On the File menu choose New Web Site. If you are using a commercial version of Visual Studio, you may have to choose File ➪ New ➪ Web Site instead. This triggers the New Web Site dialog box.
. 4
In this dialog, make sure that ASP.NET Empty Web Site is selected and not the ASP.NET Web Site item that you used in a previous exercise. Ensure that File System is chosen in the Web Location drop-down list. Click OK to create the new site.
. 5
Next, right-click the new web site in the Solution Explorer. Make sure you click the uppermost element that says something like C:\..\WebSite2\. It’s the highlighted element in Figure 1-4. From the context menu that appears, choose Add New Item.
. 6
In the new window that appears, click Web Form and type ControlsDemo as the name. The ASPX extension is added for you automatically when you click the Add button. You can leave the other settings in the dialog box at their default settings. The page should open in Markup View, showing you the default HTML, like the , , , and elements that Visual Web Developer adds there for you automatically when you create a new page.
. 7
Switch the page to Design View by clicking the Design button at the bottom of the Document Window.
8 .
If the Toolbox isn’t open yet, press Ctrl+Alt+X to open it or hover your mouse over the Toolbox tab to show it and then click the pin icon to make the Toolbox visible at all times. Drag a TextBox and a Button from the Toolbox into the dashed area in the Design View of the page. You should end up with a Figure 1-9 Design View that looks similar to Figure 1-9.
20 ❘ Chapter 1 Getting Started with ASP.NET 4
9.
Right-click the button in Design View and choose Properties. In the Properties Grid, locate the Text property under the Appearance category (shown in Figure 1-10) and change it from Button to Submit Information. As soon as you press Tab or click somewhere outside the Properties Grid, the Design View of the page is updated and shows the new text on the button.
1 0.
Press Ctrl+F5 to open the page in your default browser. Note that it’s not necessary to explicitly save the changes to your page (although it’s a good idea to do this often anyway using the shortcut Ctrl+S). As soon as you press Ctrl+F5 to run the page, VWD saves all changes to open documents automatically.
Figure 1-10
NOTE If you don’t like this behavior, you can change it. Choose Tools ➪ Options from the main menu. Then make sure that Show All Settings is checked, open the Projects and Solutions node, and choose Build and Run. In the Before Building list, you can change the way VWD behaves when you open a page in your browser.
11. Type some text in the text box and click the button. Note that after the page has reloaded, the text is still displayed in the text box. Other than that, not much has happened because you didn’t write any code for the button yet.
How It Works When you dragged the Button and the TextBox from the Toolbox on the page in Design View, VWD added the corresponding code for you in Markup View automatically. Similarly, when you changed the Text property of the button in the Properties Grid, VWD automatically updated the markup for the control in Markup View. Instead of using the Properties Grid, you could also have typed the text directly between the quotation marks of the Text property in Markup View. After changing the Text property, your page should now contain this code in Markup View:
When you press Ctrl+F5 to view the page in the browser, the web server receives the request, the page is processed by the ASP.NET runtime, and the resulting HTML for the page is sent to the browser. After you type in some text and click the button, the same process is more or less repeated: the web server receives the request, the page is processed, and the result gets sent back to the browser. When you click the button, you cause a postback to occur, where any information contained in the page — such as the text you typed in the text box — is sent back to the server. ASP.NET reacts to the postback by rendering the page again. However, this time it prepopulates controls, like the TextBox, with the values that were sent to the page.
A Tour of the IDE ❘ 21
Take a look at the resulting HTML for the page using the browser’s View Source command (rerun the page from VWD by pressing Ctrl+F5 if you already closed it). You should see code similar to this:
Just as with the earlier example, you can see that the resulting HTML is substantially different from the original ASPX markup. Postbacks are an important concept in ASP.NET, and you see more about them in other chapters, including Chapters 4 and 9.
VWD hosts many more windows and tool panels than those you have seen so far. The next section briefly touches upon some of the windows you’ll most frequently use when building ASP.NET web pages. All of the windows mentioned are accessible from the main View menu in VWD if you’re using the Expert Settings mode.
Informational Windows In addition to the windows that are visible by default when you start VWD, many more windows are available in VWD. You see most of them in action in the remainder of this book, but some are worth highlighting now. You access all windows that are discussed next from the main View menu.
The Error List The Error List gives you a list of the things that are currently somehow broken in your site, including incorrect markup in your ASPX or HTML files and programming errors in VB or C# files. This window can even show you errors in XML and CSS files. The Error List shows its messages in three categories — Errors, Warnings, and Messages — that signify the severity of the problem. Figure 1-11 shows the error list for a page that has some problems with its CSS and XHTML.
Figure 1-11
The Output Window When you try to build your site using the Build menu, the Output window tells you whether or not the build succeeded. If the build failed, for example because you have a programming error, it tells you why the build failed. In the commercial versions of Visual Studio, the Output window is used for other information as well, including the status of external plug-in programs. Building — or compiling — web sites is discussed later in this book, including Chapter 19, which deals with deployment of your web site.
22 ❘ Chapter 1 Getting Started with ASP.NET 4
The Find Results Window The Find and Replace features of VWD are invaluable tools when it comes to managing the content of your site. You will often need to replace some text in the current document or even in the entire site. Find in Files (Ctrl+Shift+F) and Replace in Files (Ctrl+Shift+H) both output their results in the Find Results window, as shown in Figure 1-12.
Figure 1-12
Because having several informational windows open at the same time may take up precious screen space, it’s often a good idea to dock them. This way, only one of them is visible at a time, while you still have quick access to the others. You learn how to customize the IDE, including the docking of windows, next.
Customizing the IDE Although the standard setup of VWD and its tool windows is pretty useful, there’s a fair chance you want to customize the IDE to your liking. You may want to rearrange some of the windows to a location where they are easier to reach, or you may want to open additional windows you frequently use. VWD is fully customizable and enables you to tweak every little detail of the IDE. In the next section, you learn how to perform the most common customization tasks.
Rearranging Windows To give each window the location it deserves, you can drag and drop them in the main IDE. Simply grab a window’s title bar or its bottom tab and drag it in the direction of the new location. Once you start dragging, you’ll see that VWD gives you visual cues as to where the window will end up (see Figure 1-13). If you drag the window over one of the four square indicators at the sides of the indicator, VWD shows a preview of how the window will be docked next to an existing window. Once you drop it, the window will pop to its new location. If you drop the window on the square in the middle of the large indicator, the window will dock with that window, sharing the same screen space. Each window has its own tab, as can be seen with the windows at the bottom of Figure 1-13. In addition to docking windows with others in the IDE, you can also have floating windows. To change a docked window into a floating one, either drag it away from its current location and drop it somewhere in the IDE without hitting one of the visual cues on the screen or choose Window ➪ Float from the main menu.
Customizing the IDE ❘ 23
Figure 1-13
To restore a floating panel to its previous docked location, choose Window ➪ Dock from the main menu. Make sure you don’t choose Dock as Tabbed Document for the tool windows like the Toolbox or the Solution Explorer or they’ll end up in the main Document Window. This makes it difficult to use these tool windows together with an open file as the two windows will share the same space.
Modifying the Toolbox The Toolbox can be modified as well. By default, the items are sorted alphabetically but you can reorder them using drag and drop. To do this, open the Toolbox (press Ctrl+Alt+X), and drag an item (such as the TextBox under the Standard category) to a different location. You can also delete items from the Toolbox by right-clicking them and choosing Delete from the context menu. Don’t worry about items getting lost forever; you can reset the Toolbox again by choosing Reset Toolbox from the same menu. You can also add your own items to the Toolbox. The most common use for this is code snippets. Simply highlight some text or code in the Document Window and drag it to the Toolbox. You can then rightclick the item and choose Rename Item to give it a more meaningful name that you can easily recognize. To avoid cluttering up the Toolbox with your own code snippets, consider creating a separate category for them. You can do this by choosing Add Tab from the Toolbox’s right-click menu. Enter a name and press Enter, and your Toolbox tab is ready for use.
24 ❘ Chapter 1 Getting Started with ASP.NET 4
In the next Try It Out exercise, you get the chance to play around with the VWD IDE so you can customize it to your liking.
Try It Out
Customizing the IDE
In this exercise you practice with opening and rearranging the windows in the Visual Web Developer IDE. Don’t be afraid to mess up the IDE. A little later in this chapter, instructions are given on how to reset the IDE to the way it was when you opened it the first time. 1.
If you closed your web site since the previous Try It Out, open it again, or create a new one using the File menu.
. 2
From the View menu, choose Error List to open the Error List window. If you don’t see the Error List item directly, choose Tools ➪ Settings ➪ Expert Settings first. Notice how the Error List gets docked below the Document Window by default.
. 3
From the same View menu, choose Task List. By default, it will be docked in the same space as the Error List, with the tabs for both windows next to each other.
. 4
Click the tab of the Task List and while holding down your mouse button, drag the Task List away from its location in the direction of the Document Window. Once you release the window, it will appear as a floating window in the IDE. To restore the window, right-click its title bar and choose Dock. Notice how the tab returns to the same tab group, but possibly at a different position. To change the order in which tabs appear in a tab group, drag a tab over the other tabs and release it at the desired location.
. 5
If you want, you can repeat the previous steps for other windows that are visible in the IDE by default or for the ones you find under the View menu. Spend some time familiarizing yourself with all the different windows and how you can arrange them on-screen. Because you’ll be working a lot with these windows in the remainder of this book, it’s good to be familiar with their locations.
. 6
Next, open the ControlsDemo.aspx page (or add a new ASPX first if you created a new web site) from the Solution Explorer by double-clicking it. When the page opens, the Toolbox becomes visible automatically. If it doesn’t, press Ctrl+Alt+X to open it.
. 7
Right-click the Toolbox and choose Add Tab. Type HTML Fragments as its new name and press Enter. This adds a new category to the Toolbox that behaves just like all the others.
. 8
With the Document Window showing your ASPX page in Markup View, type directly after the opening tag. Note that VWD automatically inserts the closing for you. You should end up with code in Markup View looking like this:
9.
Highlight the opening and closing tags, and then drag the selection from the Markup View window onto the new Toolbox tab you created in step 7. The selection shows up as Text: .
10. Right-click the Toolbox item you just created, choose Rename Item, and type Heading 1 as the name.
Customizing the IDE ❘ 25
11. Put your cursor in the Document Window again and press Ctrl+K directly followed by Ctrl+D to format the document in the Document Window. Alternatively, choose Edit ➪ Format Document from the main menu. This formats the document according to the rules you have set in the Text Editor options dialog. Formatting is also available for a number of other document types, including C# and VB.NET code and XML files. From now on, whenever you need a heading in your document in Markup View, simply place the cursor in the Document Window where you want the heading to appear and double-click the appropriate heading in the Toolbox.
How It Works Most of the steps in this Try It Out are self-explanatory. You started off by opening a few windows that you frequently need when building web sites. You then used the drag-and-drop features of the IDE to rearrange the window layout to your personal preferences. You then added an HTML fragment to a custom tab in the Toolbox. When you drag any markup to the Toolbox, VWD creates a Toolbox item for it that contains the selected markup. Whenever you need a copy of that markup in your page, simply double-click the item or drag it from the Toolbox into Markup View. This is a great time saver for HTML fragments that you frequently use. You typically use this technique for larger blocks of code; for elements like the VWD has a better tool called Code Snippets, which you meet later in this book. At the end you used VWD’s document formatting option to change the layout of the code in the document. This helps to keep the code organized and easier to read. You can fully change how the code is formatted by using the options dialog accessible through Tools ➪ Options. Then expand the path Text Editor ➪ HTML ➪ Formatting, and click Tag Specific Options.
Besides the Window layout and the Toolbox, VWD enables you to customize a lot more in the IDE. The following section explains how to customize three other important IDE features: the Document Window, toolbars, and keyboard shortcuts.
Customizing the Document Window Visual Web Developer gives you great flexibility with regard to how text is displayed in the Document Window. You can change things like font size, font color, and even the background color of the text. You can access the Font and Colors settings by choosing Tools ➪ Options, making sure that Show All Settings at the bottom of the dialog box is selected, and then choosing Environment ➪ Fonts and Colors. One thing I like to customize in the Document Window is the tab size, which controls the number of spaces that are inserted when indenting code. To change the tab size, choose Tools ➪ Options, and then under Text Editor choose All Languages ➪ Tabs. If you don’t see this option, choose Show All Settings at the bottom first. I usually set the Tab and Indent Size to 2 spaces, leaving the other settings in the Tab panel untouched. Another thing I like to customize is the number of line breaks before and after HTML elements. The Options window gives you full control over this by selecting Text Editor ➪ HTML ➪ Formatting and then clicking Tag Specific Options.
26 ❘ Chapter 1 Getting Started with ASP.NET 4
With the exception of the Tab Size being set to 2 and the number of line breaks around a few HTML elements, all screen shots in this book show the default setup of Visual Web Developer.
Customizing Toolbars Toolbars can be customized in three ways: you can show or hide the built-in toolbars, you can add and remove buttons on existing toolbars, and you can create your own toolbars with buttons you often use.
Enabling and Disabling Toolbars You disable and enable existing toolbars by right-clicking any existing toolbar or the menu bar and then selecting the appropriate item from the list. Once the toolbar is displayed, you can use its drag grip at its left to drag it to a new location within the Toolbar area.
Editing Existing Toolbars If you feel that an existing toolbar is missing an important button or that it contains buttons you rarely use, you can customize the buttons on the toolbar. To do this, right-click any toolbar or the menu bar, choose Customize, switch to the Commands tab and select the toolbar you want to modify from the Toolbar drop-down. With the command buttons at the right, you can add new and remove existing commands, or change their order. If you want to move a toolbar to the left, right or bottom of the window, switch back to the Toolbars tab of the Customize window, select the toolbar and click Modify Selection.
Creating Your Own Toolbars Creating your own toolbar is useful if you want to group some functions that you frequently use. To create a new toolbar, open the Customize window as explained in the preceding section. Click the New button and type a name for the toolbar. Then switch to the Commands tab and modify your toolbar as you would do with existing toolbars.
Customizing Keyboard Shortcuts Another setting many developers like to change is keyboard shortcuts. Keyboard shortcuts are a good way to save time because they allow you to perform a task with a simple keyboard command instead of reaching for the mouse and selecting the appropriate item from the menu. To change the keyboard shortcuts, choose Tools ➪ Options, expand Environment, and click Keyboard. Locate the command for which you want to change the shortcut in the list with commands. Because this list contains many items, you can filter the list by typing a few letters from the command. For example, typing print in the Show Commands Containing field gives you a list of all print-related commands. Next, in the Press Shortcut Keys field, type a new shortcut and click Assign. VWD allows you to enter a double shortcut key for a single command. For example, you can bind the command Close All Documents to the command Ctrl+K, Ctrl+O. To perform this command, you need to press both key combinations in rapid succession. Although a double shortcut key may seem like overkill, it greatly increases the number of available shortcut keys.
The Sample Application ❘ 27
Resetting Your Changes Don’t worry if you feel that you have messed up VWD by trying out the numerous customization options. You have many ways to restore VWD to its previous state.
Resetting the Window Layout This setting, accessible from the Window menu, resets all windows to the position they were in when you first started VWD. This command is useful if you misplaced too many windows and ended up with a cluttered IDE.
Resetting the Toolbox If you removed an item from the Toolbox by mistake or even deleted an entire tab, you can reset the Toolbox to its original state by right-clicking the Toolbox and choosing Reset Toolbox. You need to think twice before you use this command because it will also delete all your custom code snippets.
Resetting All Settings If you followed along with the previous Try It Out exercises, and then started experimenting with the customization possibilities, your IDE is now probably in one of two states: it either looks exactly the way you want it, or it looks like a complete mess. In the latter case, it’s good to know that it is easy to clean up the chaos. To completely revert all VWD settings to the way they were right after installation, choose Tools ➪ Settings ➪ Import and Export Settings or Tools ➪ Import and Export Settings, depending on the version of Visual Web Developer you're using. Next, choose the Reset All Settings option and click Next. If you want, you can create a backup of the existing settings; otherwise, choose No, Just Reset Settings. You get another screen that enables you to choose among a number of settings collections. Choose Expert Settings or Web Development because these options give you access to all features you need to follow along with this book. Finally, click Finish. This action will cause all settings to be reset to their defaults, including the Windows layout, Toolbox and Toolbox customizations, shortcut keys, and everything you may have changed in the VWD Options dialog box. So, use this command only when you’re really sure you want a fresh, new setup of VWD. With some basic knowledge about ASP.NET pages and VWD, it’s time for some real action. In the next chapter, you see how to create ASP.NET web sites and web pages in much more detail. You learn how to organize your site in a logical and structured way, how to add the many different types of files to your site and how to use them, and how to connect the pages in your site. However, before you proceed to the next chapter, there is one more important topic you need to look at: the sample application that comes with this book.
The Sample Application Building web sites is what this book is all about, so it makes a whole lot of sense that this book comes with a complete and functional sample site that is used to showcase many of the capabilities of ASP.NET.
28 ❘ Chapter 1 Getting Started with ASP.NET 4
The sample site you build in this book is called Planet Wrox, a site that serves as an online community for people interested in music. The site offers the following features to its visitors: ➤➤
Reviews about CDs and concerts that have been posted on the site by the administrator.
➤➤
The Gig Pics section, an online photo album where users can share pictures taken at concerts.
➤➤
The ability to switch between the different graphical themes that the site offers, giving you a chance to change the look and feel of the site without altering the content.
➤➤
Store musical preferences that influence the information users see on the site.
➤➤
Access to bonus features for registered users.
From an administrative perspective (that is you, as the owner of the site) the site enables you to do the following: ➤➤
Add and maintain the reviews.
➤➤
Manage the different musical genres in the system.
➤➤
Manage photo albums created by visitors to the site.
Figure 1-14 shows the Planet Wrox home page.
Figure 1-14
Figure 1-15 shows another page from Planet Wrox, but with a different theme applied. This page enables users to enter their personal information and specify preferences with regard to their favorite musical genres.
Practical Tips on Visual Web Developer ❘ 29
Figure 1-15
You can find an online running example of the site at www.PlanetWrox.com. There you can play around with the site from an end user’s perspective. You can also download the source for the sample application and all other examples from this book from the Wrox web site at www.wrox.com. By the end of this book, you’ll be able to build all of the functionality from the sample site (and hopefully even more) in other web sites. Don’t worry if it sounds like an awful lot of complex things. I guide you, step by step, from the beginning of the application all the way to the last feature. As long as you keep having fun doing this, I’m sure you’ll make it all the way.
Practical Tips on Visual Web Developer Most of the chapters in this book end with a short section of useful tips. These are tips that either didn’t fit in anywhere in the text or that encourage you to further explore or test out things. Sometimes they may seem irrelevant or hard to understand at first, but you’ll find that as you make your way through this book and look back at tips from previous chapters, things start to make sense. Don’t worry if you don’t understand certain things completely the first time you see them. Give the idea some
30 ❘ Chapter 1 Getting Started with ASP.NET 4
thought and revisit the topic a few days later. Hopefully, by letting the ideas sink in a little, things start to make more sense automatically. This applies not only to the Practical Tips section, but to the entire book. ➤➤
Before you move on to the next chapter, play around with VWD some more. Add a couple of pages to your site, drag and drop some controls from the Toolbox onto your pages, and view them in your browser. That way, you’ll have a better understanding of the tools and the many controls available when you start the next chapter.
➤➤
Familiarize yourself with the many options to tweak the Visual Web Developer IDE. When building web sites, you spend most of your time in this IDE, so it makes sense to tweak it as much as possible to your liking. Don’t be afraid to mess it up; you can always revert to previous settings.
➤➤
Take some time to browse through the settings you find in the Options dialog box of VWD (accessible through the Tools ➪ Options menu). Many of the settings are self-explanatory and can really help in further tweaking the IDE to your liking.
Summary This chapter covered a lot of important ground to get you started with ASP.NET 4 and VWD 2010. It started off with a brief history of the Microsoft .NET Framework in general and ASP.NET in particular. You then learned how to acquire and install Visual Web Developer 2010 Express. VWD is the most extensive and versatile tool available for creating ASP.NET 4 web pages. To enable you to work with it effectively, this chapter showed you how to use and customize the main features of the IDE. In subsequent chapters, you use and extend this knowledge to work with the many tools found in VWD. It’s important to understand how a page in VWD makes it to your web browser. Some knowledge of the web server that serves the request and how the page is processed to deliver the final HTML in the browser is critical in understanding ASP.NET. This chapter gave you a short introduction in the way a web page is requested and served to the browser. In the next chapter, you get a much more detailed explanation of creating web sites.
Exercises
1.
Explain the differences between the markup of a page in VWD and the final HTML page in the browser.
2.
Explain the difference between HTML and XHTML. How are the two related?
3.
Imagine you have a number of HTML fragments that you expect to use a lot throughout the site. What’s the best way to make these fragments available in VWD?
4.
What are three of the ways you can reset part or all of the IDE customization settings?
5.
If you want to change the property of a control on your page, for example the text of a button, which two options do you have available to make the change? Answers to Exercises can be found in Appendix A.
Summary ❘ 31
⊲⊲ What You Learned in this Chapter Attribute
Extra information in a tag to define or change its behavior
Element
A pair of tags holding some text or other content
HTML
HyperText Markup Language: the language that browsers use to display a web page
HTTP
HyperText Transfer Protocol: the protocol by which web browsers and web servers communicate
IDE
Integrated Development Environment: an integrated collection of applications and tools to develop applications
JavaScript
A programming language used to interact with a web page in the client’s browser
Tag
Text surrounded by angle brackets to create HTML elements
Visual Studio 2010
The development environment to build .NET applications
Visual Web Developer
The part of Visual Studio (but also available separately as the free Express edition) that enables you to build ASP.NET web applications
XHTML
HTML rewritten with XML rules
CONFER PROGRAMMER TO PROGRAMMER ABOUT THIS TOPIC.
Visit p2p.wrox.com
2
Building an ASP.NET Web Site What you will learn in this chapter: ➤➤
The different project types you can choose from as a starting point for your ASP.NET web sites
➤➤
The different project templates that are available to jumpstart your site development
➤➤
The numerous file types available in ASP.NET and what they are used for
➤➤
Ways to create structured web sites that are easy to manage, now and in the future
➤➤
How to use the designer tools to create formatted web pages
To create good-looking, functional, and successful web sites, you have to understand a number of important technologies and languages, including (X)HTML, ASP.NET, CSS (Cascading Style Sheets), a server-side programming language such as C# or VB, and a clientside language such as JavaScript. This and upcoming chapters provide a solid foundation in these technologies, so you should be comfortable with the most important concepts once you’ve finished this book. Besides these technologies, you also have to understand the Visual Web Developer IDE that was introduced in the previous chapter. You need to know how to create sites, add pages, and manage all the toolbars and windows that Visual Web Developer (VWD) offers you. In addition, you need to know how to build and design web pages in VWD with HTML and Server Controls. This chapter shows you, in detail, how to create and manage your web sites. It also shows you how to create your ASP.NET web pages and add markup to them, enabling you to create useful web pages that can present information to users and react to their response.
34 ❘ Chapter 2 Building an ASP.NET Web Site
Although you already created your first ASP.NET web site in the previous chapter, this chapter starts off with another in-depth look at creating a new web site. Because you have many choices to make when you start a new site, it’s important to understand all the different options and pick the right one for your scenario.
Creating Web Sites with VWD 2010 The previous chapter gave you a quick overview of creating a web site in VWD. You simply chose New Web Site from the File menu, selected a language, selected the standard ASP.NET Web Site template, and clicked OK. However, there’s more to the New Web Site dialog box than you saw in the previous chapter. You may have noticed that you can choose among a number of different templates that enable you to create different kind of sites. But before looking at the different templates on which you can base your new web site, you need to know a little more about the different project types that are available in VWD.
Different Project Types In the initial release of Visual Web Developer 2008 Express you could only use one project type: the Web Site Project. You needed one of the commercial versions of Visual Studio in order to use the second project type — the Web Application Project. In August 2008, Microsoft released Service Pack 1 for Visual Web Developer 2008, which gave users of the free Express edition access to the Web Application Project template as well. This addition is still present in VWD 2010 so you now have two options to choose from. Both project types are discussed next.
Web Site Projects Web Site Projects represent a project in VWD for a web site. You create a new Web Site Project by choosing File ➪ New Web Site or File ➪ New ➪ Web Site from Visual Web Developer’s main menu. Web Site Projects were introduced in Visual Studio 2005 and provide some new flexibility in creating and working with web sites. In contrast to web sites built with earlier versions of Visual Studio .NET, a Web Site Project site is simply a Windows folder with a bunch of files and subfolders in it. There is no collective file (known as the project file with a .vbproj or .csproj extension) that keeps track of all the individual files in the web site. You just point VWD to a folder, and it instantly opens it as a web site. This makes it very easy to create copies of the site, move them, and share them with others, because there are no dependencies with files on your local system. Because of the lack of a central project file, Web Site Projects are usually simply referred to as web sites, which is the term I use in the remainder of this book Besides a lot of positive feedback on this move, Microsoft also received a lot of negative response from developers who complained that Web Site Projects were too limiting for their development environment. Because there is no container file that keeps track of everything in the site, it became much harder to exclude files or folders from the site and work with source control systems — a centralized system that enables developers to work on a project collaboratively and that keeps track of changes in the project automatically. Also, Web Site Projects influenced the way web sites are compiled and deployed, making it harder for developers accustomed to the previous model to apply their knowledge and skills to the new project type.
Creating Web Sites with VWD 2010 ❘ 35
In response to the criticism, Microsoft released the Web Application Project template in May 2006 as an add-on for Visual Studio 2005 Standard Edition and up. The Web Application Project is now an integral part of all versions of Visual Web Developer, free and commercial.
Web Application Projects Web Application Projects make it easier for developers who work in teams or who need more control over the contents of the site and their compilation and deployment processes to build web sites with VWD, because the whole web site is managed as a project with a single project file that keeps track of all the content of the web site. In VWD 2010, you create a new Web Application Project through the File ➪ New Project dialog box. In that dialog box, click your preferred programming language (either Visual Basic or Visual C#) and click the Web category, where you’ll find a number of ASP.NET web application templates. One of the available project templates is the ASP.NET MVC 2 Web Application, which creates an application based on the Model View Controller pattern, another popular style of web application development. MVC is not used or discussed in this book, but if you want to learn more, check out Beginning ASP.NET MVC 1.0 by Simone Chiaretta and Keyvan Nayyeri (ISBN: 978-0-470-43399-7). This book uses the Web Site Project model because it’s easier to work with if you’re new to ASP.NET. However, you’ll find that sites built using the Web Application Project template have a lot in common with Web Site Projects. You need to use the Web Site Project template if you want to follow along with this book. When not referring to a specific project type, I’ll use the terms web site and web application interchangeably throughout this book when referring to web sites in general. Now that you know about the different project types, the next thing to consider is the different web site templates and their options.
Choosing the Right Web Site Template The New Web Site dialog box in VWD contains different web site templates, each one serving a distinct purpose. Figure 2-1 shows the New Web Site dialog box in VWD. You can open this dialog box by choosing File ➪ New Web Site, or File ➪ New ➪ Web Site depending on your version of VWD. If your dialog doesn’t look like Figure 2-1, make sure you chose File ➪ New Web Site and not accidentally File ➪ New Project. In the left-hand section you can choose between Visual Basic and Visual C# as the programming language for your site. The section in the middle shows the ASP.NET web site templates that are installed by default. Each of them is discussed in the next section. When you have created your own templates (which you learn how to do in Chapter 6), or have templates installed from other parties, they show up in this area as well. The ASP.NET Empty Web Site template is used throughout this book for the Planet Wrox web site. The others are described briefly in the following sections so you know how they can be used. The exact list of installed templates on your system depends on the version of Visual Studio and the installed components. Don’t worry if you have other templates as long as you have the ASP.NET Web Site and the ASP.NET Empty Web Site items.
36 ❘ Chapter 2 Building an ASP.NET Web Site
Figure 2-1
ASP.NET Web Site This template enables you to set up a basic ASP.NET web site. It contains a number of files and folders to jump start the development of your site. The different file types are all discussed later in this chapter. The special App_Data folder and the functionality of the pages in the Account folder are discussed later in this book. This template is a good starting point once you start developing real-world ASP.NET web sites.
ASP.NET Empty Web Site The ASP.NET Empty Web Site template gives you nothing but a single configuration file (web .config). The ASP.NET Empty Web Site template is useful if you have a bunch of existing files you want to use to create a new web site or when you want to create your site from scratch. You use this template as the basis for the sample web site you build in the book and add files and folders as you progress through the book.
WCF Service This template enables you to create a web site containing one or more WCF Services. A WCF Service is somewhat similar to a web service in that it enables you to create methods that are callable over a network. You see how to create and consume a web service from a browser in Chapter 10. However, WCF Services, or Windows Communication Foundation Services go much further than simple web services and offer you a lot more flexibility. WCF Services are outside the scope of this book, but if you want to learn more about them, pick up the book Professional WCF Programming: .NET Development with the Windows Communication Foundation by Scott Klein (ISBN: 978-0-470-08984-2).
Creating Web Sites with VWD 2010 ❘ 37
Dynamic Data Web Sites The two templates for Dynamic Data enable you to create a flexible yet powerful web site to manage data in a database without a lot of manual code. These templates are not discussed in this book, but you learn more about the Microsoft ADO.NET Entity Framework that one of the templates uses in Chapter 14. Although it seems you have to make a clear choice up front for the right web site template, this isn’t really the case. Because an ASP.NET web site in VWD is essentially just a reference to a folder, it’s easy to add types from one template to another. For example, it’s perfectly acceptable (and very common) to add a web service file to a standard ASP.NET Web Site or an ASP.NET Empty Web Site, as you see in Chapter 10.
Creating and Opening a New Web Site There are a number of different ways to create new and open existing web sites. The choices you have here are largely influenced by the way you access the web site (either from the local or a remote machine), and whether you want to use the built-in web server that ships with VWD or use the web server that comes with Windows. All the examples in this book assume that you open sites from your local hard drive and that you use the built-in web server, because it’s very convenient to develop sites with it. However, Chapter 19 shows you how to use and configure Internet Information Services, or IIS for short, the advanced web server that comes with most editions of Windows. IIS is mostly used for production hosting of your web sites, because it’s capable of serving web pages in high-traffic scenarios.
Creating New Web Sites The next Try It Out section guides you through creating the Planet Wrox web site that is the project you work on in this book. All exercises in the remainder of the book assume you have this web site open in VWD, except where stated otherwise. The exercise instructs you to store your web site in a folder called C:\BegASPNET\Site. Take note of this folder name, because it’s used throughout this book. If you decide to use a different folder, be sure to use your own location whenever you see this folder name in the book. Also make sure you don’t use special characters like the hash (#) or insert a space in the folder name because you’ll run into troubles when developing your site.
Try It Out 1.
Creating a New ASP.NET 4 Web Site
Start by creating a folder called BegASPNET in the root of your C drive using Windows Explorer or My Computer. Inside the folder, create another folder called Site. You should end up with a folder called C:\BegASPNET\Site. If you followed the instructions from the “Introduction” section of this book and unpacked the source for this book, you already have the BegASPNET folder, which in turn contains the Source and Resources folders. You still need to create the Site folder though. If you want to follow along with VB.NET and C# at the same time, you can create two folders: BegASPNETVB and BegASPNETCS and use two instances of VWD.
38 ❘ Chapter 2 Building an ASP.NET Web Site
2.
Start Visual Web Developer 2010 and choose File ➪ New Web Site or File ➪ New ➪ Web Site, depending on your version of VWD.
COMMON MISTAKES Don’t mistakenly create a new Web Application Project using File ➪ New Project as this project template is not compatible with the exercises in this book.
3.
In the Installed Templates area on the left, choose between Visual Basic and Visual C#. All the examples in this book are shown in both programming languages, so you can choose the one you like best.
4.
In the area in the middle select ASP.NET Empty Web Site.
. 5
In the Web Location drop-down list, make sure that File System is selected. The other two options (HTTP and FTP) enable you to open a remote site running IIS with the so-called Microsoft FrontPage Server Extensions and open a site from an FTP server, respectively.
. 6
Click the Browse button next to the location text box, browse to the folder you created in the first step of this exercise, and click Open. Your final screen should look like the one in Figure 2-2, except that you may have chosen Visual C# instead of Visual Basic.
Figure 2-2
7.
Click OK and VWD creates the new site for you.
Creating Web Sites with VWD 2010 ❘ 39
How It Works As soon as you click OK, VWD creates a new, empty web site for you. This new web site contains nothing but a configuration file (called web.config.) In the Solution Explorer, your web site now looks like Figure 2-3. Because a web site based on the Empty Web Site template is just a simple Figure 2-3 Windows folder that VWD looks at, the actual folder on disk contains the same file. No additional files are used to create the site, as shown in Figure 2-4, which shows a Windows Explorer displaying the files in the folder C:\BegASPNET\Site.
Figure 2-4
If you don’t see the .config extension of the web file, don’t worry. You see how to view file extensions in a later exercise. As you progress through this book, you’ll add new files and folders to the site. These additional files and folders show up in the Solution Explorer and will appear in the Windows folder at C:\BegASPNET\Site as well. Opening web sites based on the Web Site Project template is very similar to creating new ones. In the next section, you get a quick overview of opening existing sites in VWD.
Opening Existing Web Sites Just as with creating new sites, opening an existing site in VWD gives you a few options with regard to the source location of the web site. You can choose to open a site from the local file system, from a local IIS web server, from a remote server using FTP, or from a remote site using the Microsoft FrontPage Server Extensions. Figure 2-5 shows the Open Web Site dialog box in VWD. All the examples in the book assume that you always open the Planet Wrox web site from the local file system, using the File System button, which is the first button in the left column of the window. Then in the right pane, locate your web site (C:\BegASPNET\Site in this example) and click the Open button.
40 ❘ Chapter 2 Building an ASP.NET Web Site
The site you created in the previous Try It Out is a very bare-bones site. To make it more useful, you’ll need to add files to it. The many file types you have at your disposal and the way they are added to the site are the next topics of discussion.
Figure 2-5
Working with Files in Your Web Site An ASP.NET 4 web site consists of at least a single Web Form (a file with an .aspx extension), but usually it consists of a larger number of files. Many different file types are available in VWD, each offering a distinct functionality. In the next section, you see the most important file types that are used in VWD. In addition, you learn a few different ways to add these files to your site.
The Many File Types of an ASP.NET 4 Web Site To give you an idea of how many different files you can use in ASP.NET, Figure 2-6 shows the dialog box that enables you to add new files to the site (accessible by right-clicking your web site in the Solution Explorer and choosing Add New Item or by choosing Website ➪ Add New Item from the main menu). The files you can add to a site can be grouped in a few different categories. The most important files — the ones you’ll use throughout the examples in this book — are discussed next.
Working with Files in Your Web Site ❘ 41
Figure 2-6
Web Files Web Files are specific to web applications and can either be requested by a browser directly, or are used to build up part of the web page that is requested in the browser. The following table lists the various web files and their extensions, and describes how each file is used. File Type
Extension
Description
Web Form
.aspx
The workhorses of any ASP.NET web site, Web Forms represent the pages that your users view in their browser.
Master Page
.master
Enable you to define the global structure and the look and feel of a web site. You see how they can be used in Chapter 6.
Web User Control
.ascx
Contains page fragments that can be reused in multiple pages in your site. Chapter 8 is entirely devoted to user controls.
HTML Page
.htm / .html
Can be used to display static HTML in your web site.
Style Sheet
.css
Contains CSS code that enables you to style and format your web site. You learn more about CSS in the next chapter. continues
42 ❘ Chapter 2 Building an ASP.NET Web Site
(continued) File Type
Extension
Description
Web Configuration File
.config
Contains global configuration information that is used throughout the site. You see how to use the web.config later in this book, starting with Chapter 4.
Site Map
.sitemap
Contains a hierarchical representation of files in your site in an XML format. The Site Map is used for navigation and is discussed in Chapter 7.
JScript File
.js
Contains JavaScript (which Microsoft calls JScript) that can be executed in the client’s browser.
Skin File
.skin
Contains design information for controls in your web site. Skins are discussed in Chapter 6.
The next Try It Out exercise shows you how to add a new master page to the site, which is used throughout the book.
Try It Out
Adding Files to Your Site
1.
If it is not still open, open the Planet Wrox web site you created earlier by choosing File ➪ Open Web Site. Make sure that you open the site from the File System, locate the folder that contains your site (C:\BegASPNET\Site), and click the Open button.
. 2
In the Solution Explorer, right-click your site and choose New Folder as shown in Figure 2-7. COMMON MISTAKES Make sure you click the actual site and not the web.config file or you won’t get the correct menu item.
3.
. 4
Type MasterPages as the name of the folder and press Enter. Then right-click this new folder and choose Add New Item. Alternatively, you can choose File ➪ New File or Website ➪ Add New Item from Visual Web Developer’s main menu. In the dialog box that appears, choose Master Page and type Frontend as the name. VWD automatically adds the .master
extension for you when you add the file. Verify that under Installed Templates you have selected the language you want to use for this site and that Place Code in Separate File in the bottom right corner is checked. Finally, click the Add button. The master page is added to the site, and is opened automatically for you in the Document Window.
Figure 2-7
Working with Files in Your Web Site ❘ 43
How It Works This simple exercise showed you how to add a new item to your web site. Although at this stage the site isn’t very exciting yet, the file you added forms the basis for the rest of the book. The next sections briefly look at the remainder of the file types.
Code Files Adding code files to the site is identical to how you add web files. The following table describes the various types of code files. File Type
Extension
Description
Web Service
.asmx
Can be called by other systems, including browsers, and can contain code that can be executed on your server. Web services are covered in Chapter 10.
Class
.cs / .vb
Can contain code to program your web site. Note that Code Behind files (discussed later) also have this extension because they are essentially class files. C# uses files with the .cs extension and Visual Basic uses .vb files.
Global Application Class
.asax
Can contain code that is fired in response to interesting things that happen in your site, such as the start of the application or when an error occurs somewhere in the site. You see how to use this file in Chapter 18.
Besides the Code Files category, there is one more group of files worth looking into: Data Files.
Data Files Data Files are used to store data that can be used in your site and in other applications. The group consists of the XML files, database files, and files related to working with data. File Type
Extension
Description
XML File
.xml
Used to store data in XML format. In addition to plain XML files, ASP.NET supports a few more XML-based files, two of which you briefly saw before: web.config and the Site Map.
SQL Server Database
.mdf
Files with an .mdf extension are databases that are used by Microsoft SQL Server. Databases are discussed in Chapter 12 and later. continues
44 ❘ Chapter 2 Building an ASP.NET Web Site
(continued) File Type
Extension
Description
ADO.NET Entity Data Model
.dbml
Used to access databases declaratively, without the need to write code. Technically, this is not a data file, because it does not contain the actual data. However, because it is tied to the database so closely, it makes sense to group it under this header. You learn more about the ADO.NET Entity Framework in Chapter 14.
As you saw in the previous Try It Out, adding a new file of any of these types is really easy. It’s just as easy to add existing files to the site.
Adding Existing Files Not every file you create in your web site has to be brand new. In some cases it makes sense to reuse files from other projects. For example, you may want to reuse a logo or a CSS file across multiple sites. You can easily add existing files by right-clicking the web site in the Solution Explorer and choosing Add Existing Item. In the dialog box that appears, you can browse for the files, and optionally select multiple files by holding down the Ctrl key. Finally, when you click Add, the files are added to the web site. However, there is an even easier way to add files to the site, which can be a great time saver when you need to add multiple existing files and folders to your site: drag and drop. The following Try It Out shows you how this works.
Try It Out
Adding Existing Files to Your Site
1.
In Windows, minimize all open applications, right-click your desktop, and choose New ➪ Text Document. If you don’t see this option, simply create a new text document using Notepad and save it on your desktop.
. 2
Rename the text file Styles.css. Make sure the .txt extension is replaced by .css. If you don’t see the initial .txt extension and the icon of the file doesn’t change from a text file to a CSS file (by default this is the same icon as a text file but with a gear symbol on top of it, but you may have software installed that changed the icon for CSS files), Windows is configured to hide extensions for known file types. If that’s the case, open up Windows Explorer and choose Tools ➪ Folder Options in Windows XP or click the Organize button in Windows Vista and Windows 7 and then choose Folder and Search Options. In both cases, switch to the View tab and deselect the option labeled Hide Extensions for Known File Types. You now may need to rename the file from Styles.css.txt to Styles.css. When you rename the file from .txt to .css, Windows may give you a warning that the file becomes unusable if you proceed. You can safely answer Yes to this question to continue.
. 3
Rearrange VWD so you can see part of the desktop with the CSS file as well. You can use the Restore Down button next to the Close button on the Windows title bar of VWD to get it out of full screen mode.
Working with Files in Your Web Site ❘ 45
4.
Click the CSS file on the desktop and while holding down the mouse button, drag the file into the Solution Explorer. Make sure you drag the file into the Solution Explorer and not in other parts of VWD, or the file won’t be added. For example, when you drag it into the Document Window, VWD simply opens the file for you, but doesn’t add it to the site.
. 5
When you release the mouse while over the web site node or an existing file in the Solution Explorer (shown in Figure 2-8), the CSS file will be added to your site.
Figure 2-8
NOTE If you are using Windows Vista or Windows 7 and run VWD as an administrator, this might not work because Windows doesn’t allow the Windows Explorer and VWD to communicate. In that case, add existing files using the Add Existing Item menu discussed earlier or use copy and paste.
How It Works What’s important to take away from this Try It Out is that VWD creates a copy of the file when it adds it to the site. So, the original Styles.css file on the desktop is not affected when you make changes to the copy in VWD. This way, it’s easy to drag and drop files out of existing web sites into your new one, without affecting the originals. The same applies to files you add using the Add Existing Item dialog box in VWD.
46 ❘ Chapter 2 Building an ASP.NET Web Site
If you have added files to your web site’s folder outside of VWD, they may not show up right away. You can get a fresh copy of the file list by clicking the Refresh button on the Solution Explorer’s toolbar.
Organizing Your Site Because of the many files that make up your site, it’s often a good idea to group them by function in separate folders. For example, all Style Sheet files could go in a folder called Styles, .js files could go in Scripts, user controls could go in a Controls folder, and master pages could be stored in a folder called MasterPages. This is a matter of personal preference, but structured and well-organized sites are easier to manage and understand. The next Try It Out explains how you can move files around into new folders to organize your site.
Try It Out
Organizing Your Web Site
1.
Right-click the Planet Wrox site in the Solution Explorer and choose New Folder.
. 2 . 3 4. 5 .
Type Styles as the new folder name and press Enter. Create another folder, called Controls. These two folders will be used in the remainder of this book. Drag the file Styles.css that you added earlier and drop it into the Styles folder. If everything went well, your Solution Explorer should look like Figure 2-9. If your Solution Explorer looks different from the one shown in Figure 2-9, follow this Try It Out again until your site looks exactly the same, with the same folder structure and files in it. Future Try It Out exercises in this book assume you have the correct folders and files in your web site.
Figure 2-9
How It Works Structure and organization are important to keep your sites manageable. Although you may be tempted to add all of your files to the root of your project, it’s better not to do this. With a very small site, you may not notice any difference, but as soon as your site begins to grow, you’ll find it becomes a lot harder to manage when it lacks structure. Placing related files in separate folders is the first step to an organized site. Storing files of the same type in a single folder is only one way to optimize your site. In later chapters, you see that separate folders are also used to group files with similar functionality. For example, all files that are accessible only by an administrator of the site are grouped in a folder called Management.
The drag-and-drop features of VWD make it easy to reorganize your site. Simply pick up one file or multiple files and drop them in their new location. If you continue to apply these kinds of organization practices while expanding your site, you’ll find that tomorrow or six months from now, you won’t have any problems locating the right file when you need it.
Working with Web Forms ❘ 47
Special File Types Some of the files listed in the previous section require that you put them in a special folder as opposed to the optional organizational folder structure proposed in the previous section. The IDE will warn you when you try to add a file outside of its special folder, and will offer to create the folder and put the file there. For example, when you try to add a class file (with a .vb or .cs extension), you get the warning shown in Figure 2-10.
Figure 2-10
When you get this dialog box, always click Yes. Otherwise your file won’t function correctly. You get similar dialog boxes for other file types, including skin and database files. Now that you have a good understanding of the different types of files that make up your web site, it’s time to look at one of them in much more detail: .aspx files, also known as Web Forms.
Working with Web Forms Web Forms, represented by .aspx files, are the core of any ASP.NET 4 web application. They are the actual pages that users see in their browser when they visit your site. As you saw in the previous chapter, Web Forms can contain a mix of HTML, ASP.NET Server Controls, client-side JavaScript, CSS, and programming logic. To make it easier to see how all this code ends up in the browser, VWD offers a number of different views on your pages.
The Different Views on Web Forms VWD enables you to look at your Web Form from a few different angles. When you have a file with markup — like a Web Form or master page — open in the Document Window, you see three buttons at the bottom-left corner of the window. With these buttons, visible in Figure 2-11, you can switch between the different views.
Figure 2-11
48 ❘ Chapter 2 Building an ASP.NET Web Site
Source View is the default view when you open a page. It shows you the raw HTML and other markup for the page, and is very useful if you want to tweak the contents of a page and you have a good idea of what you want to change where. As I explained in the previous chapter, I use the term Markup View rather than Source View to refer to the markup of ASPX and HTML pages. The Design button enables you to switch the Document Window into Design View, which gives you an idea of how the page will end up. When in Design View, you can use the Visual Aids and Formatting Marks submenus from the main View menu to control visual markers like line breaks, borders, and spaces. Both submenus offer a menu item called Show that enables you to turn all the visual aids on or off at once. Turning both off is useful if you want to have an idea of how the page ends up in the browser. You should, however, use Design View only to get an idea of how the page will end up. Although VWD has a great rendering engine that renders the page in Design View pretty well, you should always check your pages in different browsers as well because what you see in VWD is the markup for the page before it gets processed. Server Controls on the page may emit HTML that changes the looks of the page in the browser. Therefore, it’s recommended to view the page in the browser as often as possible so you can check if it’s going to look the way you want it. It’s also recommended to test your site in as many different browsers as you can get your hands on because there may be small differences between them in the way they render a web page. The Planet Wrox web site has been tested against Microsoft Internet Explorer, Firefox, Google Chrome, Safari, and Opera. You’ll see screenshots of these browsers at various places in the book. The Split button enables you to look at Design View and Markup View at the same time, as you can see in Figure 2-12.
Figure 2-12
Split View is great if you want to see the code that VWD generates when you add controls to the Design View of your page. The other way around is very useful too: when you make changes to the markup of the page in Markup View, you can see how it ends up in Design View. Sometimes Design View becomes out-of-sync with Markup View. If that’s the case, a message appears at the top of Design View. Simply clicking the message or saving the entire page is enough to update the Design window. If you want your pages to open in a different view than Markup View, choose Tools ➪ Options. Then expand HTML Designer, and then in the General category, set your preferred view.
Working with Web Forms ❘ 49
In addition to the HTML and other markup you see in the Markup View window, a Web Form can also contain code in either C# or Visual Basic .NET. Where this code is placed depends on the type of Web Form you create. The next section explains the two options you have in more detail.
Choosing between Code Behind and Pages with Inline Code Web Forms come in two flavors: either as an .aspx file with a Code Behind file (a file named after the Web Form with an additional .vb or .cs extension) or as .aspx files that have their code embedded, often referred to as Web Forms with inline code. Although you won’t see much code until Chapter 5, it’s important to understand the difference between these types of Web Forms. At first, Web Forms with inline code seem a little easier to understand. Because the code needed to program your web site is part of the very same Web Form, you can clearly see how the code relates to the file. However, as your page gets larger and you add more functionality to it, it’s often easier if you have the code in a separate file. That way, it’s completely separate from the markup, enabling you to focus on the task at hand. In the next exercise, you add two files that demonstrate the difference between Code Behind and inline code.
Try It Out
Adding Web Forms with Code to Your Site
The files you add in this exercise aren’t needed for the final application. To avoid cluttering up the project, you should put them in a separate Demos folder.
1 .
In the Solution Explorer, right-click your web site and choose New Folder. Name the folder Demos and press Enter.
. 2
Right-click the Demos folder and choose Add New Item. In the dialog box that appears, choose your preferred programming language on the left, click the Web Form template and name the file CodeBehind.aspx. Make sure that the check box for Place Code in Separate File is selected. Finally, click the Add button. The page should open in Markup View so you can see the HTML for the page.
. 3
At the bottom of the Document Window, click the Design button to switch the page from Markup View into Design View. The page you see has a white background with a small, dashed rectangle at the top of it. The dashed rectangle represents the element you saw in Markup View.
. 4
From the Toolbox, drag a Label control from the Standard category and drop it in the dashed area of the page. Remember, you can open the Toolbox with the shortcut Ctrl+Alt+X if it isn’t open yet. In Design View, your screen should now look like Figure 2-13.
Figure 2-13
50 ❘ Chapter 2 Building an ASP.NET Web Site
5.
Double-click somewhere in the white area below the dashed line of the element. VWD switches from Design View into the Code Behind of the file and adds code that fires when the page loads in the browser: VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load End Sub C#
Although this odd syntax may look a little scary at this point, don’t worry about it too much now. In most cases, VWD adds it for you automatically, as you just saw. In later chapters, you see exactly how this code works, but for now it’s important to realize that the code you’re going to place between the lines that start with Protected Sub and End Sub in Visual Basic and between the curly braces in C# will be run when the page is requested in the browser. If you are using Visual Basic, you won’t have the underscore that is visible in this code snippet. I added that here to split the code over two lines. You see why in the How It Works section after this exercise. All code examples you’ll see from now on include a Visual Basic (VB.NET) and a C# version so always pick the one that matches your programming language. . 6
Place your cursor in the open line in the code that VWD created and add the bolded line of code that assigns today’s date and time to the label, which will eventually show up in the browser: VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Label1.Text = “Hello World; the time is now “ & DateTime.Now.ToString() End Sub C#
protected void Page_Load(object sender, EventArgs e) { Label1.Text = “Hello World; the time is now “ + DateTime.Now.ToString(); }
Note that as soon as you type the L for Label1, you get a list with options to choose from. This is part of VWD’s IntelliSense, a great tool that helps you rapidly write code. Instead of typing the whole word Label1, you simply type the letter L or the letters La and then you pick the appropriate item from the list, visible in Figure 2-14.
Working with Web Forms ❘ 51
Figure 2-14
To complete the selected word, you can press Enter or Tab or even the period. In the latter case, you immediately get another list that enables you to pick the word Text simply by typing the first few letters, completing the word by pressing the Tab or Enter key. This feature is a real productivity tool because you can write code with a minimum of keystrokes. IntelliSense is available in many other file types as well, including ASPX, HTML, CSS, JavaScript, and XML. In many cases, the list with options pops up automatically if you begin typing. If it doesn’t, press Ctrl+Spacebar to invoke it. If the list covers some of your code in the code window, press and hold the Ctrl key to make the window transparent. . 7
Right-click the page in the Solution Explorer and choose View in Browser. Click Yes if you get a dialog box that asks if you want to save the changes, and then the page will appear in the browser, similar to the browser window you see in Figure 2-15.
Figure 2-15
If you don’t see the message with the date and time appear or you get an error on the page in the browser, make sure you saved the changes to all open pages. To save all pages at once, press Ctrl+Shift+S or click the Save All button on the toolbar (the one with the multiple floppy disk symbols). Additionally, make sure you typed the code for the right language. When you created this new page, you chose a programming language that applies to the entire page. You can’t mix languages on a single page, so if you started with a Visual C# page, make sure you entered the C# code snippet from the Try It Out. . 8
Setting up a page with inline code is very similar. Start by adding a new Web Form to the Demos folder. Call it Inline.aspx and make sure you uncheck the Place Code in Separate File option.
52 ❘ Chapter 2 Building an ASP.NET Web Site
9.
Just as you did in steps 3, 4, and 5, switch the page into Design View, drag a label inside the element, and double-click the page somewhere outside the that now contains the label. Instead of opening a Code Behind file, VWD now switches your page into Markup View, and adds the Page_Load code directly in the page.
10. On the empty line in the code block that VWD inserted, type the bolded line you see in step 6 of this exercise. Make sure you use the correct programming language. You should end up with the following code at the top of your .aspx file: VB.NET
<script runat=”server”> Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Label1.Text = “Hello World; the time is now “ & DateTime.Now.ToString() End Sub C#
<script runat=”server”> protected void Page_Load(object sender, EventArgs e) { Label1.Text = “Hello World; the time is now “ + DateTime.Now.ToString(); }
11. Right-click the page in the Solution Explorer and choose View in Browser. Alternatively, press Ctrl+F5 to open the page in your browser. You should see a page similar to the one you got in step 7.
How It Works At runtime, pages with inline code behave the same as pages that use Code Behind. In both cases, the ASP.NET runtime sees the Page_Load code and executes any code it finds in it. In the Try It Out, this meant setting the Text of Label1 to a welcome message and today’s date and time. Note that in this example, the C# code looks very similar to the VB.NET code. The code that sets the Label’s text is almost identical in the two languages. One difference is that VB.NET uses an ampersand (&) to glue two pieces of text together, but C# uses the plus (+) character. You can also use plus (+) character in VB.NET to concatenate strings together, but with a few caveats as you’ll learn in Chapter 5. The other difference is that in C# all code lines must be terminated with a semicolon (;) to indicate the end of a unit of code, but Visual Basic uses the line break. If you want to split a long line of code over multiple lines in Visual Basic, you need to use the underscore (_) character. In earlier versions, VB.NET required the underscore in a lot of different places. However, in Visual Basic 10, the version that ships with Visual Web Developer 2010, the designers of the language have greatly reduced the number of places where you need the underscore. One place where you do need the underscore if you want to split code over multiple lines is right before the Handles keyword, as you saw in this code snippet from step 6 of the exercise: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Label1.Text = “Hello World; the time is now “ & DateTime.Now.ToString() End Sub
Working with Web Forms ❘ 53
Note that in your page, you won’t see the underscore at the end of the first line of the Visual Basic example. It was added here because the book’s pages are not wide enough to show the entire code statement on a single line. You’ll see more of these underscores in other Visual Basic examples in the remainder of this book. If you decide to manually type the underscore to make your own code more readable, don’t forget to type an additional space before the actual underscore or your code won’t work. In C#, you don’t need this character because the language itself allows you to break long lines simply by pressing Enter. This is because C# uses a semicolon to denote the end of a line instead of a line break in the source. You opened the page in your browser using the right-click View in Browser option or by pressing Ctrl+F5. With the View in Browser option, you always open the page you right-click. With the Ctrl+F5 shortcut, you open the page that is currently the active document in the Document Window, the page that is currently selected in the Solution Explorer, or the file that has been set as the start page for the web site. Additionally, all open files are saved automatically, and the site is checked for errors before the requested page is opened in the browser. You can assign a page as the start page by right-clicking it in the Solution Explorer and choosing Set As Start Page. If you want to control this behavior at a later stage, right-click the web site in the Solution Explorer and choose Property Pages. In the Start Options category, you can indicate that you want the currently active page to open, or you can assign a specific page, as show in Figure 2-16.
Figure 2-16
In the previous exercise, you learned how to add a page that contains a simple Label control. Additionally, you saw how to write some code that updates the label with today’s date and time. You can ignore this code for now; it only served to demonstrate the differences between Code Behind and inline code. In Chapter 5, you learn more about programming in Visual Basic and C#.
54 ❘ Chapter 2 Building an ASP.NET Web Site
To make compelling pages, you obviously need a lot more content than just a simple Label control that shows today’s date and time. The next section shows you how to add content and HTML to your pages and how to style and format it.
Adding Markup to Your Page You have a number of ways to add HTML and other markup to your pages. First of all, you can simply type it in the Markup View window. However, this isn’t always the best option, because it forces you to type a lot of code by hand. To make it easier to insert new HTML in the page and to apply formatting to it, the Design View window offers a number of helpful tools. These tools include the Formatting toolbar and the menu items Format and Table. For these tools to be active, you need to have the document in Design View. If you’re working in Split View mode, you have to make sure that the Design View part has the focus, or you’ll find that most of the tools are not available.
Inserting and Formatting Text You can type text in both Design View and in Markup View. Simply place the cursor at the desired location and start typing. When you switch to Design View, the Formatting toolbar becomes available, with the options shown in Figure 2-17. Background Color
Foreground Color Target Rule Reuse Existing Style
Block Format Show Overlay
Font Size Font Name
Numbered Lists Bold Italic Underline
Align Text
Hyperlink
Bullets Generate Local Resource
Figure 2-17
The drop-down list labeled Block Format enables you to insert HTML elements like
for paragraphs, through for headings, and
, , and
for lists. You can choose an item from the drop-down list directly to have it inserted in your page, or you can select some text first and choose the appropriate block element from the list to wrap the selected text inside the tags. The drop-down list labeled Font Name enables you to change the font family, and the Font Size drop-down list enables you to change the font size. Many of the buttons on the toolbar function exactly the same as in other editing environments. For example, the B button formats your text with a bold font. Similarly, the I and the U buttons italicize and underline your font, respectively. In the next Try It Out, you see how to work with these tools to create the home page of the Planet Wrox web site.
Working with Web Forms ❘ 55
Try It Out
Adding Formatted Text
In this Try It Out, you create a Web Form called Default.aspx and add some basic content to it. 1.
Add a new Web Form through the Add New Item to the root of the site dialog and call it Default.aspx. Make sure you check off the Place Code in Separate File option and click Add.
Switch to Design View using the Design button at the bottom of the Document Window. . 2
Click inside the dashed rectangle until you see the glyph showing that the element is currently active. At the same time, the tag navigator at the bottom of the code window should highlight the last block with the text on it, as shown in Figure 2-18.
Figure 2-18
3.
Type Hi there visitor and welcome to Planet Wrox and highlight the text using the mouse. From the Block Format drop-down list (visible in Figure 2-17) choose Heading 1 . Note that a small glyph with the text h1 appears right above the text, to indicate that VWD created a heading for you automatically. Figure 2-19 shows the Design View with the element.
Figure 2-19
4.
Position your cursor at the end of the heading after the word Wrox and press Enter. A new paragraph (indicated by a small glyph with the letter p on it) is inserted for you so you can directly start typing.
. 5
Type the text shown in Figure 2-20 (or make up your own) to welcome the visitor to Planet Wrox. Notice how the text www.PlanetWrox.com turns blue as soon as you type the comma to indicate VWD has recognized it as a web address and has turned it into a link. You can press Enter to start a new paragraph. Select the text “paying a visit,” click the Foreground Color button on the Formatting toolbar, and select a different color in the dialog box that appears. Then select some other text, such as “reviews and concert pictures,” and click the Bold button. When you’re done, your Design View should show something similar to Figure 2-20.
56 ❘ Chapter 2 Building an ASP.NET Web Site
Figure 2-20
The code for the home page should now look more or less similar to the following (the code has been reformatted a bit to fit the space in the book): Hi there visitor and welcome to Planet Wrox
We're glad you're <span class=”style1”>paying a visit to www.PlanetWrox.com, the coolest music community site on the Internet.
Feel free to have a look around; there are lots of interesting <strong>reviews and concert pictures to be found here.
6.
Open the page in your browser by pressing Ctrl+F5, or by right-clicking the page in the Solution Explorer and then choosing View in Browser.
How It Works When you use the various Formatting toolbar buttons, like Foreground Color, VWD inserts the appropriate HTML and CSS code for you. For example, when you click the B button, VWD inserts a pair of <strong> tags around the selected text. When you click the I button, it adds a pair of <em> tags to italicize the text. In this exercise, VWD also inserted a class attribute (shown in the previous code example) that points to a class called style1 when you changed the text color. The code for this style has been added to the top of your file and looks similar to this: <style type=”text/css”> .style1 { color: #FF0000; }
Working with Web Forms ❘ 57
Your code may look slightly different if you chose a different color. The code you see here is explained in the next chapter. For now, just remember that this code sets the color of the text it is applied to as red. Note that VWD replaced the apostrophe character (’) in “we’re” in the welcome message with its HTML-compliant variant: '. Using this kind of code enables you to insert characters in your page that a browser may have trouble displaying, or that have special meaning within HTML itself, like the ampersand character (&), which is written as &. When you type text in Design View, VWD automatically inserts the coded equivalents of relevant characters for you; however, if you type in Markup View directly, you’ll have to do this yourself. Don’t worry if your code looks different from what is shown here. Many settings in VWD influence the code that is generated for you.
So far, the exercises have been concerned with adding and styling text in your page. However, VWD enables you to insert other HTML elements as well, like tables and bullets. The next section shows you how this works.
Adding Tables and Other Markup HTML tables are great if you need to present structured or repeating data, like a list of products in a shopping cart, photos in a photo album, or input controls in a form. There is a lot of debate on the Internet about whether you should use tables to lay out your page as well. For example, if your page contains a header with a logo, a main content area, and a footer at the bottom, you could use a table with three rows to accomplish this. In general, it’s considered bad practice to use tables for this purpose because they add a lot of extraneous markup to the page and are often difficult to maintain. Besides, quite often the same result can be accomplished using CSS, which you learn about in the next chapter. Despite the disadvantages that tables may bring, they are still an invaluable asset in your HTML toolbox when it comes to displaying tabular or otherwise structured information.
Try It Out
Using the Format and Table Menus
In this exercise, you learn how to add tables to your page using the Table menu and how to add rows and columns. Additionally, you learn how to add other structured elements such as bulleted lists. 1. 2.
In the Demos folder, create a new Web Form called TableDemo.aspx. Make sure it uses Code Behind by checking the Place Code in Separate File option. Switch the page to Design View, click inside the dashed rectangle that represents the standard tag in the page, and choose Table ➪ Insert Table. The Insert Table dialog box appears, as
shown in Figure 2-21.
58 ❘ Chapter 2 Building an ASP.NET Web Site
Figure 2-21
3.
Set Rows to 3 and leave Columns set to 2. Leave all other settings set to their defaults and click OK. The table gets inserted in the page.
. 4
If you see only a single table cell, and not the entire table with three rows and two columns, you need to enable Visual Aid for tables. To do this, choose View ➪ Visual Aids ➪ Visible Borders from the main menu to turn the borders on. Your Design View should now look like Figure 2-22.
Figure 2-22
5.
Drag the right border of the very first cell in the table to the left. You’ll see a visual indicator showing the width of the cell. Keep dragging it to the left until it has a width of 200 pixels, as in Figure 2-23.
Figure 2-23
Working with Web Forms ❘ 59
6.
To add more rows or columns to the table, you can right-click an existing cell. From the popup menu that appears, choose Insert to add additional rows or columns at different locations. Similarly, you can use the Delete, Select, and Modify options to delete rows or columns, merge cells, and make selections. For this exercise, you don’t need to add additional rows or columns, although it’s okay if you had already done so.
. 7 8.
Place your cursor in the first cell of the first row and type the words Bulleted List.
. 9
Switch to the Plain Bullets tab, click the picture with the round, solid bullets (see Figure 2-24), and click OK.
Place your cursor in the second cell of the first row and choose Bullets and Numbering from the Format menu.
Figure 2-24
10. Type some text, like your favorite musical genre (Punk, Rock, Techno, and so on), and press Enter. VWD inserts a new bullet for you automatically, so you can continue to add new items to the list. Add two more genres, so you end up with three bullets.
1 1.
Repeat steps 7 through 10, but now create a numbered list. First, type Numbered List in the first cell of the second row, then position your cursor in the second cell of the same row, and choose Format ➪ Bullets and Numbering. Switch to the Numbers tab (visible in Figure 2-24 behind the Plain Bullets tab) and click the second picture in the first row, which shows a standard numbered list, and click OK. Type a few items for the list, pressing Enter after each item.
1 2.
Open the page in the browser by pressing Ctrl+F5. You should see a screen similar to Figure 2-25.
60 ❘ Chapter 2 Building an ASP.NET Web Site
Figure 2-25
How It Works When you visually insert page elements like tables or lists through the available menus, VWD inserts the required markup for you in Markup View. When you insert a table, VWD adds a
element and a number of
and
elements to define rows and cells, respectively. It also applies a style attribute to the table to control the table’s width. It created another style for the
elements when you dragged the column width to be 200 pixels. Similarly, when you insert a list, VWD inserts a element for numbered or ordered lists and a
element for bulleted or unordered lists. Within these elements,
elements are used to define each item in the list.
Besides the HTML tags you have seen thus far, there is another important tag you need to look at: the tag, which is used to create links between pages.
Connecting Pages An important part of any web site is the links that connect the pages in your site. Links enable your visitors to go from one page to another, in the same site or to a completely different site on the Internet. There are a few ways to create links between pages, including: ➤➤
The HTML element, explained in this chapter.
➤➤
Using the control, discussed in Chapter 7.
➤➤
Programmatically through code. This is discussed later in the book.
The following exercise shows you how easy it is to link from one page to another.
Working with Web Forms ❘ 61
Try It Out
Linking Pages
In this Try It Out, you modify the page TableDemo.aspx you created earlier by adding text that links to another page. Once you run the page in the browser and click that link, the new page will replace the old one. 1.
Open the page TableDemo.aspx from the Demos folder.
. 2 . 3 . 4
If necessary, switch to Design View. In the first cell of the third row, type the text Link. In the second cell of the same row, type the text Go to the Homepage of Planet Wrox and highlight it with your mouse.
. 5
On the Formatting toolbar, click the Convert to HyperLink button. It’s located near the end of the toolbar and has a green globe on it. If you don’t see this button because it’s obscured by other toolbars, either drag the Formatting toolbar to a new location, or choose Format ➪ Convert to Hyperlink to bring up the same dialog box.
. 6
In the dialog box that appears, click the Browse button, browse to the Default.aspx page in the root of your site and click OK. Next, click OK again to dismiss the Hyperlink dialog box. The Design View of your page should look similar to the one displayed in Figure 2-26.
Figure 2-26
7.
Switch to Markup View and notice how the HTML for the link has been inserted: Go to the Homepage of Planet Wrox
Note that the href attribute points to the page you want to link to. 8.
If you want to change the page being linked to from Markup View, click somewhere between the opening and closing quote of the href attribute and press Ctrl+Spacebar. A dialog box pops up that enables you to select another page. Alternatively, you can click the Pick URL option and browse for the new page somewhere in your site.
62 ❘ Chapter 2 Building an ASP.NET Web Site
9.
Right-click the page TableDemo.aspx in the Solution Explorer and choose View in Browser. When the page has finished loading, click the Go to the Homepage of Planet Wrox link. The request is sent to the web server and, as a response, you now get the home page of the web site.
How It Works Links between pages are likely one of the most important elements in a web page, because they enable you to create a connection between a page in your site and another page, whether that page lives in your own site or on a completely different server somewhere on the Internet. For simple links that should appear somewhere in your page, the HTML tag with a href attribute set is the easiest to set up. When the user clicks such a link, the browser requests the new page from the server and displays it. The double dots (..) in the href‘s value are a way to refer to the parent directory. The full href attribute means “go up one level in the folder hierarchy and then select the file Default.aspx.” You see a lot more about links and how they work in Chapter 7. You’re not limited to linking to pages in your own site. If you want to link to external pages instead, simply replace the href attribute value with the full address of the page as shown in the following example: Go to the Wrox Homepage
It’s important to include the http:// prefix; otherwise, the browser goes out looking for a file or folder called www.wrox.com on your web site.
You’ll use the things you learned in this chapter about page creation and formatting in the next chapter, which deals with designing your web pages using CSS. Besides the visual tools like the Formatting toolbar and the Table menu, Visual Web Developer has another great way to quickly insert code in your pages: code snippets. Code snippets enable you to insert large chunks of code with just a few keystrokes. You see code snippets at work in the next chapter.
Practical Tips on Working with Web Forms Here are some tips for working with Web Forms: ➤➤
Always try to favor Web Forms with Code Behind over those with inline code. Although at first you may not notice a big difference in working with them, as your site and pages start to grow, you’ll find that it’s easier to work with a page where the code is separated from the markup.
➤➤
Spend some time familiarizing yourself with the different menu items of the Format and Table menus. Most of them generate HTML elements that are inserted into your page. Take a look at the HTML elements and attributes that have been generated for you, and try to change them directly in the code, and through the menus and toolbars. This way, you get a good feel for the various tags available and how they behave.
Summary ❘ 63
➤➤
Experiment with links to connect pages in your site. Notice how VWD creates different links depending on the location of the page you are linking to. Chapter 7 deals with linking and the various ways to address pages in your site in much more detail.
Summary This chapter introduced you to some important topics that help you build maintainable and structured ASP.NET web sites. Understanding the differences between the various project types and templates enables you to kick-start a web project with just the files you need. The same applies to the different file types you can add to your site. Because each file type serves a specific purpose, it’s important to realize what that purpose is and how you can use the file. One common activity that you’ll perform when building ASP.NET web pages is adding markup to the page. As you saw in this and the previous chapter, markup comes in a few flavors, including plain HTML and ASP.NET Server Controls. Knowing how to add this markup to your page using the numerous menu options and toolbars that VWD offers is critical in building good-looking web pages. Now that you have a solid understanding of creating and modifying Web Forms, it’s time to look at how you can turn those dull looking black-and-white pages with a few controls into attractive web pages. The next chapter shows you how to work with the many CSS tools found in VWD to create the desired effect.
Exercises
1.
Name three important files in the Web Files category that you can add to your site. Describe the purpose of each file.
2.
What do you need to do to make a piece of text both bold and italicized in your web page? What will the resulting HTML look like?
3.
Name three different ways to add existing files to an ASP.NET web site in VWD.
4.
What are the different views that VWD offers you for your ASPX pages? Does VWD offer other views as well? Answers to Exercises can be found in Appendix A.
64 ❘ Chapter 2 Building an ASP.NET Web Site
⊲⊲ What You Learned in this Chapter Code Behind
A page model where server side code is stored in a separate code file
Design View
Gives you a graphical representation of your page
File Types
ASP.NET supports many different file types, including Web Forms (.aspx), master pages (.master), CSS files (.css), JavaScript (.js), and SQL Server databases (.mdf)
Inline Code
A page model where server side code is stored in the same file as the markup
Markup View
Enables you to look at the markup of your page
Project Templates
Jump-start your web development by setting up a site targeting a specific scenario
Project Types
VWD offers two project types: Web Application Projects and Web Site Projects
Split View
Enables you to look at Markup View and Design View at the same time
Web Form
Presents the User Interface of your web site at the client
YOU CAN DOWNLOAD THE CODE FOUND IN THIS BOOK. VISIT WROX.COM AND SEARCH FOR ISBN 9780470502211
3
Designing Your Web Pages What you will learn in this chapter: ➤➤
What CSS is and why you need it
➤➤
How CSS looks and how to write it
➤➤
The different ways to add CSS code to your ASP.NET pages and to external files
➤➤
The numerous tools that VWD offers you to quickly write CSS
The pages you created in the previous two chapters look pretty plain and dull. That’s because they lack styling information and therefore default to the standard layout that the browser applies. To spruce up your pages, you need a way to change their presentation in the browser. The most common way to do this is by using the Cascading Style Sheets (CSS) language. CSS is the de facto language for formatting and designing information on the Web, including ASP.NET web pages. With CSS you can quickly change the appearance of your web pages, giving them that great look that your design or corporate identity dictates. Solid support for working with CSS has been added in VWD 2008, the previous version of Visual Web Developer. The new VWD 2010 builds on top of this CSS support and improves it in a number of ways, including the ability to render pages much closer to how they’ll eventually end up in the browser. The CSS tools enable you to visually create your CSS code, making it much easier to style your pages without the need to know or remember every little detail of CSS. To understand the relevance of and need for CSS in your ASP.NET web sites, you need to understand the shortcomings of HTML first. The next section looks at the problems that plain HTML presents, and how CSS is able to overcome these issues.
66 ❘ Chapter 3 Designing Your Web Pages
Why Do You Need CSS? In the early days of the Internet, web pages consisted mostly of text and images. The text was formatted using plain HTML, using tags like <strong> to make the text bold, and the tag to influence the font family, size, and color. Web developers soon realized that they needed more power to format their pages, so CSS was created to address some of HTML’s styling shortcomings.
Problems of HTML Formatting One of the problems with using HTML for formatting is that it offers only a limited set of options to style your pages. You can use tags like <em>, <strong>, and to change the appearance of text and use attributes like bgcolor to change the background color of HTML elements. You also have a number of other attributes at your disposal for changing the way links appear in your page. Obviously, this feature set isn’t rich enough to create the attractive web pages that your users expect and demand. Another problem of HTML with a lot more impact on how you build your web pages is the way the styling information is applied to the page. By design, HTML forces you to embed your formatting in your HTML document, making it harder to reuse or change the design later. Consider the following example:
This is red text in an Arial type face and slightly larger than the default text.
The problem with this code snippet is that the actual data (the text in the
element) is mixed with the presentation (the formatting of the text with the tag in this example). Ideally, the two should be separated, so each of them is easier to change without affecting the other. Imagine you used the
and tags to mark up the first paragraph of every page in your site. Clearly, this code is difficult to maintain. What happens when you decide to change the color of the font from red to dark blue? Or what if your corporate identity dictates a Verdana font instead of Arial? You would need to visit each and every page in your site, making the required changes. Besides maintainability, another problem with HTML formatting is the fact that you can’t easily change the formatting at runtime in the user’s browser. With the HTML from the previous code snippet, there is no way to let your visitor change things like the font size or color, a common request to help people who are visually impaired. If you want to offer your visitors an alternative version of the page with a larger font size or a different color, you’d need to create a copy of the original page and make the necessary changes. The final problem with HTML formatting is that the additional markup in your page adds considerably to the size of the page. This makes it slower to download and display because the information
Why Do You Need CSS? ❘ 67
needs to be downloaded with each page in your web site. It also makes it harder to maintain your pages because you’d need to scroll through large HTML files to find the content you need. To summarize, formatting with HTML suffers from the following problems: ➤➤
Its feature set severely limits the formatting possibilities that your pages require.
➤➤
Data and presentation are mixed within the same file.
➤➤
HTML doesn’t allow you to easily switch formatting at runtime in the browser.
➤➤
The required formatting tags and attributes make your pages larger and thus slower to load and display.
Fortunately, CSS enables you to overcome all of these problems.
How CSS Fixes Formatting Problems CSS is designed to format your web pages in almost every possible way. It offers a rich set of options to change every little aspect of your web page, including fonts (size, color, family, and so on), colors and background colors, borders around HTML elements, positioning of elements in your page, and much more. CSS is widely understood by all major browsers today, so it’s the language for visual presentation of web pages and very popular among web developers. CSS overcomes the problem of mixed data and presentation by enabling you to define all formatting information in external files. Your ASPX or HTML pages can then reference these files and the browser will apply the correct styles for you. With this separation, the HTML document contains what you want to display, and the CSS file defines how you want to display it, enabling you to change or replace one of the two documents, leaving the other unmodified. In addition, CSS can be placed directly in an HTML or ASPX page, which gives you a chance to add small snippets of CSS exactly where you need them. You should be cautious when placing CSS directly in an HTML or ASPX page, because you can then no longer control style information from a single, central location. Because all CSS code can be placed in a separate file, it’s easy to offer the user a choice between different styles — for example, one with a larger font size. You can create a copy of the external style sheet, make the necessary changes, and then offer this alternative style sheet to the user. You see how this works in Chapter 6 when ASP.NET Themes are discussed. Another benefit of a separate style sheet file is the decrease in bandwidth that is required for your site. Style sheets don’t change with each request, so a browser saves a local copy of the style sheet the first time it downloads it. From then on, it uses this cached copy instead of requesting it from the server over and over again. Sometimes this caching can work against you when the browser doesn’t download the latest CSS files with your changes. If you find that the browser is not picking up the changes you made to a CSS file, use Ctrl+F5 or Ctl+R in the browser (not VWD) to get a fresh copy from the server. Now that you have seen why CSS is so important, it’s time to find out how it looks and how to use it.
68 ❘ Chapter 3 Designing Your Web Pages
An Introduction to CSS In terms of syntax, CSS is an easy language to learn. Its “grammar” consists of only a few concepts. That makes it relatively easy to get started with. What makes CSS a bit more difficult is the way all major browsers render a page. Although virtually every modern desktop browser understands CSS, they all have their quirks when it comes to displaying a page according to the CSS standard. This standard, maintained by the same organization that maintains the HTML standard, the World Wide Web Consortium, or W3C for short, comes in three different versions: 1.0, 2.1, and 3.0. From these three versions, 2.1 is the most applicable today. It contains everything that version 1.0 contains but also adds a lot of possibilities on top of that. It’s also the version that VWD uses and generates by default. Version 3.0 is currently under development and it’s expected to take some time before the major browsers have solid support for it. Before you look at the actual syntax of CSS, it’s a good idea to see an example first. In the next exercise, you write a simple ASPX page that contains some CSS to format the contents of the page. This helps in understanding the CSS language, which is discussed in full detail in the section that follows.
Try It Out
Writing Your First CSS
In this Try It Out you write some CSS that changes the appearance of a header and two paragraphs. You’ll hand code the page for now; the second half of this chapter shows you how to use the CSS tools available in VWD. 1.
In the Demos folder of the Planet Wrox project, create a new Web Form called CssDemo.aspx. For this exercise, it doesn’t matter if you choose inline code or Code Behind.
. 2
Make sure the page is in Markup View and then locate the closing tag in the source. Position your cursor at the end of the line and press Enter to create an empty line between the title and head tags. On this new line type the word style and then press Tab. Visual Web Developer completes the <style> element for you. Press Enter twice to create some room between the tags. You end up with the following bolded code: <style type=”text/css”>
NOTE This code completion feature uses code snippets that enable you to associate a piece of code (like the <style> element) with an identifier (like style in this example). Code snippets are very useful to quickly insert pieces of code by typing only the short identifier. Many more code snippets are available, and where appropriate I’ll point them out throughout this book.
An Introduction to CSS ❘ 69
Instead of using the style code snippet, you can also type the full code yourself. Note that as soon as you type the opening angle bracket ( tag. The same applies to the type attribute; simply type the letters ty and the type attribute is preselected in the list. All you need to do to complete the word is press the Tab or Enter key. And, once more, the same help is available for the attribute value text/css. Simply select it in the list and press Tab or Enter, and the value is inserted for you automatically, nicely surrounded by the double quotes. . 3
Next, between the opening and closing <style> tags, type the following bolded CSS code: <style type=”text/css”> h1 { font-size: 20px; color: Green; } p { color: Blue; font-style: italic; } .RightAligned { text-align: right; }
Take great care when typing this code, because CSS is rather picky about syntax. The first item in the list is an h1 tag to style a heading at the first level so it gets a size of 20 pixels and is displayed in a green font. Notice the colon between font-size and 20px and that the line is closed with a semicolon. The second item in the list simply contains the letter p and defines the look and feel for all
elements in the page. The last item is prefixed with a period (.) followed by the text RightAligned. This item is used to right-align some text in the page. . 4
Scroll down in the page a bit until you see the opening tag. Right after this tag, type the following bolded code: Welcome to this CSS Demo page
CSS makes it super easy to style your pages.
With very little code, you can quickly change the looks of a page.
70 ❘ Chapter 3 Designing Your Web Pages
Instead of typing in this code directly, you can also use the Formatting toolbar while in Design View to create elements like and
. For now, you’ll need to switch to Markup View to add class=”RightAligned”, but in later exercises in this chapter you see how you can have the IDE write this code for you. 5.
If you switch to Design View (or Split View), you’ll see that the designer shows your text with the formatting defined in the <style> element of the page. Figure 3-1 shows the page in Split View so you can see the code and the design at the same time.
Figure 3-1
Although this black and white book makes it difficult to see different font colors, in Figure 3-1 you can clearly see that the has a larger font size. The figure also shows that all paragraphs (both the plain paragraph and the one with class=”RightAligned”) are now displayed with an italic font. Finally, you can see that the final paragraph is aligned to the right of the window, because the class attribute on the tag is set to RightAligned. If you don’t see the last paragraph glued to the right border of the Document Window, make sure you typed RightAligned exactly the same in the <style> tag and in the class attribute. Because CSS is case sensitive, there’s a big difference between RightAligned and rightaligned. 6.
Press Ctrl+F5 to view CssDemo.aspx in your browser. The page you see in the browser is identical to the preview you got in the Design View of Visual Web Developer.
How It Works Although the code you typed in this exercise is relatively simple, there’s a lot going on under the hood of the browser (and the Design View of VWD 2010) to make this possible. You started by adding some styles to the section of the page: <style type=”text/css”> h1 {
An Introduction to CSS ❘ 71
font-size: 20px; color: Green; } ...
The <style> tag is used to wrap a style sheet that is embedded in the page with its type attribute set to text/css. The code block from h1 until the closing curly brace (}) between the <style> tags is called a rule set or simply a rule. The rule in this code snippet defines the appearance for all elements in your page. The h1 at the top of the code block is called a selector and is used to indicate to what element the formatting should be applied. In this case, the selector maps directly to an HTML element, but many other selectors are available, as you’ll see in the next section. Figure 3-2 shows how the elements are related to each other.
Selector Declaration hl ( Rule Set )
font-size: 20px; color: Green;
Property
Value
Figure 3-2
Between the curly braces you see the style information that should be applied to the heading. Each line between the curly braces is called a declaration. A declaration consists of a property, followed by a colon and then followed by a value. The semicolon (;) at the end of a declaration separates it from the next declaration and is required on all declarations except for the last one in the rule set. However, for consistency, it’s a good idea to add it to all declarations, which is what I’ll do in the remainder of this book. When the browser loads this page, it also reads in the styles you defined between the <style> tags. Then, whenever it comes across an HTML element that matches the selector, it applies the CSS rules to that element. So, for the and
elements, their respective rules are applied. This causes the heading to turn green with a large font, while the paragraphs turn blue with an italic font. But why does the last paragraph turn blue and get right-aligned? In CSS, you can have rules coming from different sources. The last
tag gets its style information from the standard p selector in the style definition. So, the p rule gives the paragraph a blue and italic font. However, it also has a class defined. This class, called RightAligned, causes the text to be aligned to the right of the window. In the end, the last
element gets its rules from two selectors at the same time. You can make up and assign your own class names (as shown with the RightAligned class), giving you the flexibility to design your pages and elements exactly how you want them.
The next section digs a lot deeper in the syntax of CSS, giving you a much more detailed view on selectors, properties, and values.
CSS—The Language As you saw in the previous Try It Out exercise, a cascading style sheet is actually a collection of rules. A rule is a combination of a selector and one or more declarations, which in turn can be broken down to a property and a value. You’re probably getting a little dizzy from all the new terms that
72 ❘ Chapter 3 Designing Your Web Pages
were introduced in the past few paragraphs, so in the next section, you see most of them again, with a detailed explanation and code examples that show you what they are used for and how they work.
The Style Sheet The style sheet contains all the relevant style information that should be applied to page elements. In its simplest form, a style sheet looks like this: h1 { color: Green; }
A style sheet can also contain more than one rule as you saw in the previous exercise. At the same time, each rule can contain multiple declarations, enabling you to group them under a single selector: h1 { font-size: 20px; color: Green; }
The code you just saw is functionally identical to this: h1 { font-size: 20px; } h1 { color: Green; }
The condensed form, where the two declarations are grouped under the same selector, is much easier to read, understand, and maintain, so it’s advisable to use this syntax as much as possible. To be able to style an element on a page, a browser has to know three things: ➤➤
What element of the page must be styled?
➤➤
What part of that element must be styled?
➤➤
How do you want that part of the selected element to look?
The answers to these questions are given by selectors, properties, and values.
Selectors As its name implies, a selector is used to select or point to one or more specific elements within your page. A number of different selectors are available, giving you fine control over what elements you want to style. The selector answers the first question: What element of the page must be styled? The next section shows you the four most important types of selectors.
An Introduction to CSS ❘ 73
The Universal Selector The Universal selector, indicated by an asterisk (*), applies to all elements in your page. The Universal selector can be used to set global settings like a font family. The following rule set changes the font for all elements in your page to Arial: * { font-family: Arial; }
The Type Selector The Type selector enables you to point to an HTML element of a specific type. With a Type selector, all HTML elements of that type will be styled accordingly. h1 { color: Green; }
This Type selector now applies to all elements in your code and gives them a green color. Type selectors are not case sensitive, so you can use both h1 and H1 to refer to the same heading.
The ID Selector The ID selector is always prefixed by a hash symbol (#) and enables you to refer to a single element in the page. Within an HTML or ASPX page, you can give an element a unique ID using the id attribute. With the ID selector, you can change the behavior for that single element, like this: #IntroText { font-style: italic; }
Because you can reuse this ID across multiple pages in your site (it only has to be unique within a single page), you can use this rule to quickly change the appearance of an element that you use once per page, but more than once in your site, for example with the following HTML code:
I am italic because I have the right ID.
I am NOT italic because I have a different ID.
In this example, the #IntroText selector changes the font of the first paragraph — which has the matching id attribute — but leaves the other paragraph unmodified. ID selectors are case sensitive, so make sure that the id attribute and the selector always use the same casing.
The Class Selector The Class selector enables you to style multiple HTML elements through the class attribute. This is handy when you want to give the same type of formatting to a number of unrelated HTML
74 ❘ Chapter 3 Designing Your Web Pages
elements. The following rule changes the text to red and bold for all HTML elements that have their class attributes set to Highlight: .Highlight { font-weight: bold; color: Red; }
The following code snippet uses the Highlight class to make the contents of a <span> element and a link () appear with a bold typeface: This is normal text but <span class=”Highlight”>this is Red and Bold. This is also normal text but this link is Red and Bold as well.
Notice that the selector uses a period in its name, but you don’t use this period when referring to the selector in the class attribute. The class attribute is very useful because it enables you to reuse a piece of CSS for many different purposes, regardless of the HTML element that uses the class. CSS supports more types of selectors, giving you even more control over the elements you want to target, but the four different types you just saw are the most widely used.
Grouping and Combining Selectors CSS also enables you to group multiple selectors by separating them with a comma. This is handy if you want to apply the same styles to different elements. The following rule turns all headings in the page to red: h1, h2, h3, h4, h5, h6 { color: Red; }
Moreover, with CSS you can also combine selectors, enabling you to hierarchically point to a specific element in a page. You can do this by separating the selectors with a space. The following example targets all
elements that fall within an element with an id of MainContent, leaving all other paragraphs unmodified. #MainContent p { font-size: 18px; }
Note that combining is very different from grouping. Grouping is just a shortcut to avoid typing the same declarations over and over again, whereas combining enables you to target specific elements in your document. With combining, you’re not limited to ID and Type selectors; you can also use it with the other selectors, as is demonstrated with the following example:
An Introduction to CSS ❘ 75
#MainContent p.Attention { font-weight: bold; }
This rule changes all paragraphs with the class Attention within the element with its id set to MainContent and leaves all others untouched. The following HTML snippet uses this rule to show the effect:
My class is Attention, so my text is bold.
My text is not bold, as it lacks the Attention class.
I am NOT bold because I don’t fall within MainContent.
The second question that needs to be answered to apply a certain style in your page is about what part of the element must be styled. This is done with properties.
Properties Properties are the part of the element that you want to change with your style sheet. The CSS specification defines a long list of properties (VWD’s IntelliSense list shows more than 100 items), although you won’t use all of them in most web sites. The following table lists some of the most common CSS properties and describes where they are used. Property
Description
Ex ample
background-color background-image
Specifies the background color or image of an element.
This causes the element to be hidden, and not take up any screen space.
This setting causes other content following a float to be placed at the top-right corner of the element. You see how this works later in the chapter.
continues
76 ❘ Chapter 3 Designing Your Web Pages
(continued) Property
Description
Ex ample
height width
Sets the height or width of elements in your page.
height: 100px; width: 200px;
margin padding
Sets the amount of free space inside (padding) and outside (margin) of an element.
padding: 0; margin: 20px;
visibility
Controls whether an element is visible in the page. Invisible elements still take up screen space; you just don’t see them.
visibility: hidden;
This causes the element to be invisible. However, it still takes up its original space in the page. It’s as if the element is still there, but completely transparent.
Fortunately, VWD helps you to find the right property with its many CSS tools, so you don’t have to remember them all.
NOTE Many more selectors and properties are available in CSS than I have described here. For more detail on CSS, consult VWD's IntelliSense lists or take a look at www.w3schools.com/css/css_reference.asp.
For a property to be useful, you need to give it a value, which answers the third question: How do you want the part of the selected element to look?
Values Just as with properties, values come in many flavors. The values you have available depend on the property. For example, the color attribute takes values that represent a color. This can be a named color (such as White), or a hexadecimal number representing a red, green, and blue (RGB) component (such as #FF0000), or it can be set using the CSS rgb notation. The following examples are all functionally equivalent: h1 { color: Red; } h1 {
The first declaration uses the named color Red, whereas the other two examples use an RGB value to specify the red color. Using named colors can increase the readability of your CSS code, but because you’re limited to a relatively short list of named colors, you often need the hexadecimal notation to get the exact color you want. When you type a color property in VWD, it presents you with a list of known colors as shown in Figure 3-3. If you want to type in a different color, simply ignore the list (or close it by pressing the Escape key) and then type your own value. In a later exercise in this chapter you see how to use a color picker to select non-standard colors. Many other values are possible as well, including size units (px, em, and so on), font families, images (which take the form Figure 3-3 of url(SomeImage.jpg)), or so-called enumerations like the border-style, which allows you to set a border style to solid, dashed, double, and so on.
Using Shorthand Many of the CSS properties enable you to write a shorthand version as well as a more expanded version. Take, for example, the border property. In its shortest form, the border property can be set like this: border: 1px solid Black;
This border property applies a border to all four sides of an HTML element. The border size will be 1px, the style will be solid (some of the other options include dashed, dotted, and double), and the border color will be set to Black. This is an easy way to quickly set all four borders of the HTML to the same values. However, if you want more control over the individual borders and their properties, you can use the expanded version: border-top-width: 1px; border-top-style: solid; border-top-color: Black; border-right-width: 1px; border-right-style: solid; border-right-color: Black; border-bottom-width: 1px;
This long version causes the exact same style to be applied: a solid black border on all four sides with a thickness of 1 pixel. In most cases, you should favor shorthand notation over its expanded counterpart, because it’s much easier to read and maintain. However, if you need absolute control over the border — for example, if you want a 2-pixel dashed border on the left and top sides, and a green, solid border on the right and bottom sides of the HTML element — it’s good to know that you can set each border property of all four directions individually. Other CSS properties that support shorthand include font, background, list-style, margin, and padding. If you’re unsure whether a property supports shorthand, consult the IntelliSense pop-up list that appears by pressing Ctrl+Space when you’re entering a property in a CSS file or a <style> block. Although at times it seems you need to write CSS by trial and error, and just hope for the right result, there’s actually a quite accurate model behind CSS that determines how items should be laid out on the page. This model is called the CSS Box Model.
The CSS Box Model The CSS Box Model describes the way three important CSS properties are applied to HTML elements: padding, border, and margin. Figure 3-4 shows a graphical representation of the box model. In the middle there is an HTML element like a
or a with a certain height and width. Just around it there is padding; the whitespace that surrounds the element within its border. Immediately after the padding you can see the border and finally on the outside there is margin, which defines the room between an element (including its padding and border) and its surrounding elements. The three outer properties of an element — padding, border, and margin — add up to the space that an element takes up in page. To see how this works, consider the following CSS and HTML: .MyDiv { width: 200px; padding: 10px; border: 2px solid black; }
Top Margin Border Padding
Left
Element
Bottom Figure 3-4
Right
An Introduction to CSS ❘ 79
... Element
This renders a rectangle in the browser with the element surrounded by a black border of two pixels shown in Figure 3-5. Before you read on, try answering the question: How wide is the arrow below the element?
Element How wide is this arrow? Figure 3-5
If you guessed 224 pixels, you are correct. The width of the arrow is the sum of three values: the width of the actual element (200 pixels), plus the width of the padding surrounding it on both sides (two times 10 pixels), plus the width of the borders on both sides (two times two pixels), resulting in a total width of 224 pixels. So, if you wanted the entire box to be 200 pixels wide instead, you’d need to set the width property of the MyDiv selector to 176px. The example shows the effect on the width only, but the same principles apply to the height of elements. Keep this box model in mind when laying out your pages. When things end up wider or taller than you anticipated, check the width, height, padding, border, and margin properties in the CSS style sheet. In the next exercise, you modify the site’s home page that you created in the previous chapter. You add the basic layout for the site, which is then styled using a style sheet. In Chapter 6 you use this page again when you upgrade it to a master page.
Try It Out
Styling the Planet Wrox Home Page
In this exercise you modify two files: First, you add the basic layout elements to the Default.aspx page to create room for a header, a menu, the main content area, a sidebar, and a footer. Then you modify the Styles.css file from the Styles folder to change the size and location of these elements. Finally, you attach the style sheet to the page, so the style information is applied when the page is viewed in the designer or in a browser. 1. 2.
Open the file Default.aspx from the root of your web site and if necessary switch to Markup View. Modify the code within the element so it ends up like this: Header Goes Here Menu Goes Here Hi there visitor and welcome to Planet Wrox ... Sidebar Goes Here Footer Goes Here
80 ❘ Chapter 3 Designing Your Web Pages
Make sure that the welcome message you added in the previous chapter ends up between the opening and closing tag of the MainContent . . 3
Open the file Styles.css from the Styles folder. If you added some code to this file earlier, remove that code first.
. 4
At the top of the page, type the following code that uses an ID selector to select the Header : #Header { }
5.
Position your mouse between the curly braces and then choose Styles ➪ Build Style from the main menu. Alternatively, you can choose the same item by right-clicking the ID selector, or clicking the Build Style button on the Styles toolbar. The Modify Style dialog box shown in Figure 3-6 appears.
Figure 3-6
6.
In the Category list on the left, click Background and then open the drop-down list for the background color. From the color picker that appears, click the Silver color, as shown in Figure 3-7. Alternatively, you can type the hexadecimal color code for Silver (#C0C0C0) in the backgroundcolor text box directly.
An Introduction to CSS ❘ 81
Figure 3-7
7.
Switch to the Position category by clicking it in the list on the left. The panel that appears enables you to set position-related information, including the height and width. Under width, enter 844 and make sure that px is selected in the drop-down list at the right. For the height, enter 86. Click OK to dismiss the dialog box and to insert the declarations into your code, which now looks like this: #Header { background-color: #C0C0C0; width: 844px; height: 86px; }
8.
Repeat steps 4 through 7, this time creating the following rules: * { font-family: Arial; } h1 { font-size: 20px; } #PageWrapper { width: 844px; } #MenuWrapper {
The float and clear properties are found in the layout category of the Modify Style dialog box. . 9
When you’re done creating the rules, save and close the Styles.css file, because you’re done with it for now.
1 0.
Open the file Default.aspx again and switch to Design View. From the Solution Explorer, drag the file Styles.css from the Styles folder onto the page. You should immediately see the Design View change to reflect the code you wrote in the style sheet. When you dropped the style sheet on the page, VWD inserted code in the section of the page in Markup View that attaches the style sheet to the document: <style type=”text/css”> .style1 { color: #FF0000; }
You can also drag an existing style sheet from the Solution Explorer directly in the section of a page in Markup View. When you do that, VWD adds the same element. 11. Finally, save the changes to all open documents (press Ctrl+Shift+S) and then request Default.aspx in your browser. Your screen should look similar to Figure 3-8, which shows the page in Mozilla Firefox.
An Introduction to CSS ❘ 83
Figure 3-8
How It Works The Style Builder makes it easy to select CSS properties and change their values. You don’t need to memorize every little detail about CSS, but instead you can visually create your CSS code. Although the tool can do most of the work for you, it’s still useful if you can read and understand the CSS code. When you need to make tweaks to your code, it’s often quicker to do it directly in the Document Window, instead of opening the Style Builder. Note that the Header, PageWrapper, MenuWrapper, and Footer have an exact width of 844 pixels. This way, the site fits nicely on screens with a size of 1024 × 768 pixels, a common screen size for many of today’s computers, without being squeezed between the Windows borders. Systems with bigger screens will simply expand the white background at the right of the page. Note also that the MainContent area and the Sidebar are positioned next to each other. This is done with the CSS float property: #MainContent { width: 664px; float: left; } #Sidebar { background-color: Gray; width: 180px; float: left; }
84 ❘ Chapter 3 Designing Your Web Pages
This tells the MainContent to “float” on the left side of the Sidebar, effectively placing the Sidebar to the right of it. You need to tell the Sidebar to float as well; if you leave it out, it will be placed at the left of the page, right where it was before you applied the CSS. The combined width of the two elements adds up to 844 pixels, which is exactly the width of their parent element: the PageWrapper. To end the float and tell the Footer element to be placed directly under the MainContent and Sidebar elements, the clear property is used to clear any float (left or right) that may be in effect: #Footer { background-color: #C0C0C0; width: 844px; clear: both; }
The gray backgrounds are just temporarily added to the code, so it’s easier to see what ends up where. In future exercises, you modify the CSS file again to fit the scheme of the Planet Wrox web site. To tell the browser what styles to apply, you link the style sheet in the head of the page:
This tells the browser to look in the Styles folder for a file called Styles.css and apply all rules in that file to the current document. Once the browser has downloaded the CSS file, it applies all the styles it finds in there to your HTML elements, resulting in the layout shown in Figure 3-8.
In this exercise, you saw how to link a style sheet to a page using the tag. There are, however, more ways to include style sheets in your web pages.
Adding CSS to Your Pages The first way to add CSS style sheets to your web pages is through the element that points to an external CSS file, as you saw in the previous exercise. Take a look at the following to see what options you have when embedding a style sheet in your page:
The href property points to a file within your site, just as you saw in the previous chapter when you created links between two pages. The rel and type attributes tell the browser that the linked file is in fact a cascading style sheet. The media attribute is quite interesting: it enables you to target different devices, including the screen, printer, handheld devices, and even Braille and aural support tools for visually impaired visitors. The default for the media attribute is screen, so it’s OK to omit the attribute if you’re targeting standard desktop browsers. You briefly saw the second way to include style sheets at the beginning of this chapter: using embedded <style> elements. The <style> element should be placed at the top of your ASPX or HTML page, between the tags. Within the <style> tags, you can write the exact same CSS you saw
An Introduction to CSS ❘ 85
earlier. For example, to change the appearance of an element in the current page alone, you can add the following code to the of your page: <style type=”text/css”> h1 { color: Blue; }
The third way to apply CSS to your HTML elements is to use inline styles with the style attribute that you saw in the previous chapter. Because the style attribute is already applied to a specific HTML element, you don’t need a selector and you can write the declaration in the attribute directly: <span style=”color: White; background-color: Black;”> This is white text on a black background.
Choosing among External, Embedded, and Inline Style Sheets Because you have so many options to add style sheets to your site, what’s the best method to use? In general, you should give preference to external style sheets over embedded styles, which in turn are preferred over inline styles. External style sheets enable you to change the appearance of the entire site through a single file. Make one change to your external style sheet file, and all pages that use this style sheet pick up the change automatically. However, it’s perfectly acceptable to use embedded and inline styles as well in certain circumstances. If you want to change the look of a single page, without affecting other pages in your site, an embedded style sheet is your best choice. The same applies to inline styles: if you only want to change the behavior of a single element in a single page, and you’re pretty sure you’re not going to need the same declaration for other HTML elements, use an inline style. An important thing to consider is the way that the various types of style sheets override each other. If you have multiple identical selectors with different property values, the one defined last takes precedence. For example, consider a rule defined in an external style sheet called Styles.css that sets the color of all tags to green: h1 { color: Green; }
Now imagine you’re attaching this style sheet in a page that also has an embedded rule for the same h1 but that sets a different color: <style type=”text/css”> h1 {
86 ❘ Chapter 3 Designing Your Web Pages
color: Blue; }
With this code, the color of the actual tag in the page will be blue. This is because the embedded style sheet that sets the color to blue is defined later in the page and thus overrides the setting in the external file. If you turn the styles around like this: <style type=”text/css”> h1 { color: Blue; }
the heading will be green, because the setting in the external style sheet now overrules that of the embedded style. The same principle applies to inline style sheets. Because they’re defined directly on the HTML elements, their settings take precedence over embedded and external style sheets. It’s also good to know that CSS generally overrules attributes on HTML elements. For example, if you have a CSS rule that sets the width and height of an image, the height and width attributes on the img element are ignored and the image in this example ends up as a 100-pixel square: img { height: 100px; width: 100px; } ...
NOTE There’s a lot more to CSS than what is shown here. To learn more about CSS, pick up a copy of Professional ASP.NET 2.0 Design: CSS, Themes, and Master Pages by Jacob J. Sanford (ISBN: 978-0-470-12448-2) or a copy of Beginning CSS: Cascading Style Sheets for Web Design, Second Edition by Richard York (ISBN: 978-0-470-09697-0).
In general, it’s recommended that you attach external files at the top of the section, followed by embedded style sheets. That way, the external file defines the global look of elements, and you can use embedded styles to overrule the external settings. VWD makes it easy to move embedded style sheets to an external CSS file, something you learn how to do in the next section, which discusses the remainder of the CSS tools in VWD.
Working with CSS in Visual Web Developer ❘ 87
Working with CSS in Visual Web Developer VWD has the following handy tools on board for working with CSS: ➤➤
The Style Sheet toolbar, giving you quick access to creating new rules and styles.
➤➤
The CSS Properties Grid, which enables you to change property values.
➤➤
The Manage Styles window, enabling you to organize styles in your site, changing them from embedded to external style sheets and vice versa; reorder them; link existing style sheets to a document; and create new inline, embedded, or external style sheets.
➤➤
The Apply Styles window, which you can use to choose from all available styles in your site and quickly apply them to elements in your page.
➤➤
The Style Builder, which you can use to visually create declarations.
➤➤
The Add Style Rule window, which helps in building more complex selectors.
The next sections give you a detailed look at these six tools.
Creating New Styles in External Style Sheets In an earlier Try It Out, you manually added selectors to the CSS file and then used the Style Builder to write the rules. However, you can also use the VWD tools to write the selectors for you. In the next Try It Out, you see how to use the Add Style Rule window to create a new rule in an external file. You then use the Style Builder to modify the rule.
Try It Out
Creating New Styles in an Existing Style Sheet
In this exercise, you create a new style that affects all the links in the MainContent area. By using combined selectors, you can target the links in the content area only, leaving the others unmodified.
1 . 2. 3.
Start by opening the file Styles.css from the Styles folder. Scroll down in the file and position your cursor at the end, right below the #Footer rule. Make sure the Style Sheet toolbar is visible and click the first button, labeled Add Style Rule, or choose Styles ➪ Add Style Rule from the main menu. With the dialog that appears you can visually create a combined selector. In the left section of the dialog box, you can choose among Element, Class, and ID selectors. Select the last option, labeled Element ID, and in its text box type MainContent. Your dialog now looks like Figure 3-9. Figure 3-9
88 ❘ Chapter 3 Designing Your Web Pages
If you don’t see the hash symbol (#) in front of MainContent in the Style Rule Preview box, make sure you selected Element ID and not Element. Click the button with the right arrow in the middle of the screen to add the selector to the Style Rule Hierarchy list. 4.
. 5
Next, select the Element radio button at the top of the dialog box and from its drop-down list, choose a (for links) and click the arrow button once more. Your screen should now show a preview of the selector in the Style Rule Preview box, as in Figure 3-10.
Figure 3-10
Click OK to add the selector to your style sheet file. You should end up with the following empty rule: #MainContent a { }
6.
Right-click between the curly braces of the rule you just inserted and choose Build Style.
. 7
In the Font category, change the color property to #008000 by clicking the arrow of the drop-down list box, and then clicking the green square on the top row.
8.
In the text-decoration section at the right of the same dialog box, place a check mark for the underline option. The Modify Style dialog box should now look like the one shown in Figure 3-11.
Figure 3-11
Working with CSS in Visual Web Developer ❘ 89
9.
Click OK to dismiss the dialog box. Back in the CSS file, select the entire rule set you just created (including #MainContent a and both curly braces), copy it to the clipboard, and then paste it again twice below the original rule set.
10. Rename the first selector you just pasted from #MainContent a to #MainContent a:visited. This style is used for links that the user has already visited.
1 1.
Right-click the new rule you just created and choose Build Style. In the Font category, change the color from green to red by typing #FF0000 in the color text box and click OK.
12. Change the third selector in the file from #MainContent a to #MainContent a:hover. This style is applied to links when the user hovers over them with the mouse.
1 3.
Once again, right-click the new selector you just created and choose Build Style. In the Font category, change the color from green to orange by typing #FFA500 in the color text box. Click OK to close the Modify Style dialog box. You should end up with the following three rules in your CSS file below the styles that were already present: #MainContent a { color: #008000; text-decoration: underline; } #MainContent a:visited { color: #FF0000; text-decoration: underline; } #MainContent a:hover { color: #FFA500; text-decoration: underline; }
14. Save and close the Styles.css file because you’re done with it for now.
How It Works You started off by creating a new rule using the Add Style Rule dialog box. Quite often, you’ll find it easier and quicker to type the rule directly in the code editor. However, when you’re creating complex combined rules, the Add Style Rule dialog box can help you understand and create the hierarchy of the rule. The Modify Style dialog box is an excellent tool for creating new CSS declarations. Instead of memorizing all the different CSS properties and values, you can simply point and click them together in an organized dialog box. All the different CSS properties are grouped under logical categories, making it easy to find and change them.
90 ❘ Chapter 3 Designing Your Web Pages
The :hover and :visited parts on the a selector are probably new to you. These selectors are called pseudo class selectors. The a:visited selector is only applied to links that you have already visited in your browser. The a:hover selector is only applied to the tag when the user hovers the mouse over the link. In the next Try It Out you see the effect of these two selectors in the browser.
With the style sheet created, the next thing you need to do is attach this style sheet to your document. You have a number of ways to do this, including typing in the code by hand or dropping the file in Markup or Design View. The next Try It Out exercise shows you a third option: using the Manage Styles window.
Try It Out
Attaching Your New Style Sheet to Your Document
In this exercise, you remove and reattach the style sheet called Styles.css to your Default.aspx page so you see another alternative to attaching a style sheet file to an ASPX page. You then add some text and links to this page so you can see the behavior of rule sets you created earlier. 1.
Switch the page Default.aspx into Markup View and remove the element from the section that you added earlier in this chapter. Then switch to Design View and make sure the Manage Styles window is open. If it isn’t, click somewhere in the Document Window to activate the Design View and then choose View ➪ Manage Styles from the main menu. The window shown in Figure 3-12 appears. Recall from Chapter 1 that if you don’t see this item in the View menu, choose Tools ➪ Settings ➪ Expert Settings first.
Figure 3-12
The Manage Styles window gives you an overview of all external and embedded style sheets that apply to the current document. Notice how VWD sees that the current document already contains an embedded style: style1 that you created in Chapter 2.
2 .
Click the Attach Style Sheet button on the Manage Styles window (it’s the second button in Figure 3-12), browse to your Styles folder in the root of the site, and select the Styles.css file. Click OK and the style sheet is inserted in your page again.
. 3
When you attach the style sheet, the Manage Styles window (shown in Figure 3-13) is updated and now shows your newly attached style sheet.
. 4
With the page Default.aspx still open in Design View, select the text “look around” in the paragraph. If you typed something else in an earlier Try It Out, select that text instead. At this stage, all that’s important is that you have some text to turn into a link.
. 5
On the Formatting toolbar, click the Convert to Hyperlink button (with the globe and link symbol on it), click the Browse button in the dialog box that appears, and select Default.aspx in the root of the site. This way, the link points to the same page it’s defined in, which is fine for this exercise. Click OK twice to dismiss the dialog boxes.
Figure 3-13
Working with CSS in Visual Web Developer ❘ 91
6.
Save the changes to all open documents (choose File ➪ Save All from the main menu or press Ctrl+Shift+S) and then request Default.aspx in your browser by pressing Ctrl+F5. You should see the page appear with the “look around” link underlined, as shown in Figure 3-14.
Figure 3-14
7.
Hover your mouse over the “look around” link; note that it turns to orange.
. 8
Click the “look around” link, and the page will reload. The link has now turned to red. If the link was already red the first time you visited it, don’t worry. You opened the page in your browser before, which caused the browser to mark the link as visited. The browser keeps track of the pages you visit and then applies the correct style to new and visited links. If you want to see the desired behavior in Internet Explorer, open up the Internet Options by choosing Tools ➪ Internet Options. Then on the General tab, click the Delete button. In the dialog box that pops up, clear the history by following the on-screen instructions. When you now reload the page by pressing Ctrl+F5 in your browser, the link should turn to green. Other browsers have similar options to clear the browser’s history. For example, Firefox enables you to clear the history using the Tools ➪ Clear Recent History menu option. If you don’t have this menu item, choose Tools ➪ Options and then switch to the Privacy tab, where you can delete the history as well. Alternatively, you can open the page in a different browser. To select an alternate browser, rightclick Default.aspx in the Solution Explorer and choose Browse With from the context menu. If your alternate browser is listed there already, select it from the list and then click Browse. Optionally you can make this browser your default, by clicking the Set as Default button. If your browser is not listed, click the Add button and then the ellipsis next to the Program Name box to search for your favorite browser. When the browser is displayed in the list, click it to select it and then click Browse to open the page in that browser. The page should now appear in your alternate browser.
92 ❘ Chapter 3 Designing Your Web Pages
How It Works The Manage Styles window gives you a quick overview of style sheets that are active for the current page, either as an external and attached style sheet, or as an embedded style sheet in the section of the page. It’s a very useful window to attach new styles to the current document, and to move styles from one location to another, which you see how to do in the next section. When you open the page in the browser, the updated style sheet is downloaded and the browser then applies the #MainContent a:visited selector to all links to pages you visited before. When you hover your mouse over a link, the selector #MainContent a:hover is applied, causing the link to turn orange. Viewing your pages in different browsers is a good thing to do. Although modern browsers tend to render a page more and more similarly, there are subtle differences that you need to be aware of and handle in your HTML and CSS code. Installing a few different browsers on your system (Internet Explorer, Firefox, Safari, Opera, and Chrome, for example), assigning them to the Browse With dialog as shown in this Try It Out, and testing your pages in these browsers as often as you can will help to ensure your pages look exactly right in the majority of the browsers.
Useful as external style sheets may be, there are times when you really want to use embedded or inline styles instead. Creating and managing those styles, explained in the next section, is just as easy.
Creating Embedded and Inline Style Sheets When you’re working with a page in Design View, you often need to make minor tweaks to part of the page, like styling a piece of text, aligning an image, or applying a border to an element. At this stage, you need to make a decision about whether to create an inline, embedded, or external style sheet. As you saw earlier, you should opt for external or embedded style sheets if you envision you’re going to reuse a style later. VWD doesn’t care much, though. It enables you to create styles at all three levels. Even better, it enables you to easily upgrade an embedded style to an external one, or copy inline style information to a different location, giving you great flexibility and the option to change your mind later. In the next exercise, you see how to create inline and embedded style sheets. You see later how to move those styles to an external style sheet, enabling other pages to reuse the same styles.
Try It Out
Creating Embedded and Inline Styles in a Page
In this Try It Out, you add a style rule to the element of the page, to remove the default margin that a browser draws around the heading. In addition, you style the first paragraph using a class, giving it a different look to make it stand out from the other paragraphs on the page.
1 . . 2
Go back to VWD and make sure that the page Default.aspx is open in Design View. Click once on the h1 element in the Document Window to select it and then choose Format ➪ New Style. The New Style dialog box appears (visible in Figure 3-15), which is pretty similar to the Modify Style dialog box you saw earlier.
3.
At the top of the screen, open the Selector drop-down list and choose (inline style). It’s the first item in the list. This ensures that the new style is applied as an inline style to the element.
. 4
Switch to the Box category, shown in Figure 3-16.
Working with CSS in Visual Web Developer ❘ 93
Figure 3-15
Figure 3-16
94 ❘ Chapter 3 Designing Your Web Pages
This dialog box has a handy diagram that serves as a refresher on the CSS Box Model, showing you where the properties padding, border, and margin end up. By default, browsers draw some white space above or below an element, but the actual amount differs between browsers. To give each browser the same consistent settings, you can reset the padding to 0 and then apply a little bit of margin at the bottom of the heading, which creates some distance to the elements following it. To do this, set padding to 0 in the top box. By leaving the Same for All option selected, VWD creates a shorthand declaration for you. Then uncheck Same for All for the margin section, enter 0 for the top, right, and left boxes and enter 10 for the bottom text box. Leave all drop-down lists set to px and click OK. You end up with the following element with an inline style in Markup View: Hi there visitor and welcome to Planet Wrox
5.
Next, in Design View, select the first paragraph by clicking it. A small glyph appears to indicate you selected a
element, as visible in Figure 3-17. The Tag Selector at the bottom of the Document Window should highlight the
element.
Figure 3-17
6.
With the paragraph still selected, choose Format ➪ New Style from the main menu. This time, instead of creating an inline style, type the text .Introduction in the Selector box that is visible in Figure 3-18. Don’t forget the dot (.) in front of the selector’s name.
7.
At the top of the screen, select the check box for Apply New Style to Document Selection. With this setting on, the new class you’re about to create is applied to the
tag that you have selected.
8.
From the font-style drop-down list, choose italic. Your New Style dialog box should now look like Figure 3-18.
. 9
Finally, click OK. Note that the entire paragraph is now displayed with an italic font.
Working with CSS in Visual Web Developer ❘ 95
Figure 3-18
10. With the
tag still selected, open the CSS Properties Grid (see Figure 3-19) by choosing View ➪ CSS Properties. This grid gives you an overview of all the CSS properties and shows which ones are currently active for your page. This grid shows a list of applied rules in the top half of the window. The bottom half of the window is used to show the CSS properties for those rules. In Figure 3-19 you see the rules that are applicable to the Figure 3-19 .Introduction selector. Properties that appear in blue and bold have their value set, whereas others appear in a normal font. If you don’t see these styles, click the third button on the toolbar of the CSS Properties Grid, which moves the properties that are set up in the list.
1 1.
In the CSS Properties list in the bottom half, locate the color property and set it to a dark blue color, like #003399. To achieve this, open the drop-down list for the property value and choose a color from the color picker. If the color you’re looking for is not available, click the More Colors button to bring up the extended color picker, shown in Figure 3-20.
96 ❘ Chapter 3 Designing Your Web Pages
Figure 3-20
Instead of using the color picker, you can also type in a value in the Properties Grid directly. This is how all properties work in the CSS Properties Grid: they let you enter values directly or enable you to visually change the value using a drop-down list or a button with ellipses at the end of the property’s value box. Figure 3-21 shows the different options you have for the font-style property in a convenient drop-down list.
Figure 3-21
Take special note of the three buttons at the top of the window, because they house some useful functionality. The first two buttons enable you to switch between categorized mode and alphabetical mode, making it easier to find the right property. The third button enables you to display the applied properties at the top of the list (as is the case in Figure 3-21) or at their default location in the list.
1 2.
Finally, save all changes and open Default.aspx in your browser (see Figure 3-22). You’ll see that the first paragraph is now displayed with a blue and italic font except for the link in the text, which should be green. Additionally, if you followed all the instructions from the previous chapter, the text “paying a visit” is red, set by the embedded CSS class.
Working with CSS in Visual Web Developer ❘ 97
Figure 3-22
13. Switch back to VWD and look at your page in Markup View. In the section of the page, you should see the following embedded style sheet: .Introduction { font-style: italic; color: #003399; }
How It Works The numerous tools that VWD offers make it easy to write CSS for your web site. You don’t need to hand code anything, or remember all the different properties that the CSS standard supports. Instead, you can simply choose them from different lists on the CSS Properties Grid. This grid enables you to enter values manually but also offers handy tools to select colors, files, and items from drop-down lists. All changes you make in the Properties Grid are applied to the relevant style sheet, whether you’re working with an inline, embedded, or external style sheet. At the same time, the Design View is updated to reflect the new CSS options you have set. When you look at the element, you can see that VWD created an inline style with a padding set to 0px to affect all four sides at once and a margin set to 0px 0px 10px 0px to control all four sides individually.
Once you have created a bunch of useful and reusable styles, you need a way to apply your existing styles to other pages or HTML elements. You see how this works next.
98 ❘ Chapter 3 Designing Your Web Pages
Applying Styles If you have some experience with Microsoft Word, you may be familiar with the Styles dialog box, which lists all available styles and enables you to apply them to selected portions of text. This way, you can quickly apply identical formatting to blocks of text. This works similarly in VWD. With the Apply Styles window — accessible by choosing View ➪ Apply Styles from the main menu — you can easily apply style rules to elements in the page.
Try It Out
Using the Apply Styles Window
In this exercise, you reuse the .Introduction class and apply it to the second paragraph of the page as well. That way, both paragraphs end up looking the same.
1 .
Still in Default.aspx, make sure you’re in Design View and then select the second paragraph of the page by clicking it. Ensure that the Tag Selector at the bottom of the Document Window shows that the
tag is selected, and not another tag like <strong> that may be part of the
element. If you have only one paragraph of text, create a new one first (by pressing Enter after the first paragraph in Design View), enter some text, and then select that paragraph.
2 .
Open the Apply Styles window by choosing View ➪ Apply Styles. This window shows all the selectors it finds in the current page and any attached style sheet. If you don’t see all the styles shown in Figure 3-23, click the Options button and choose Show All Styles. To help you find the right style, VWD uses a number of different visual cues. First of all, the Apply Styles window uses red, green, blue, and yellow dots to represent ID selectors, class selectors, element selectors, and inline styles, respectively. Figure 3-23 only shows red and green dots (you’ll just have to trust me) because the
element doesn’t have any inline styles applied. However, if you select the element, an inline style and an element selector appear. If Figure 3-23 you do try this out, make sure you select the
element again afterward. Furthermore, styles that are currently used in the page are surrounded by an additional circle, as is the case with all selectors in Figure 3-23.
3.
Click the Introduction class in the CSS Styles list. VWD adds a class attribute to the
tag:
Feel free to have a look around; there are lots of interesting <strong>reviews and concert pictures to be found here.
If you want to apply multiple classes, hold down the Ctrl key while clicking one of the other classes in the list. This applies a list of classes separated by a space to the element’s class attribute. You can follow the same steps to apply the selected style in Markup View as well. . 4
Using the Clear Styles button, you can quickly remove existing classes and inline styles from a tag. Consider the HTML fragment you saw in the previous chapter when you used the Formatting
Working with CSS in Visual Web Developer ❘ 99
toolbar to format text in the page. If you used the Foreground Color button, you ended up with code similar to this: We're glad you're <span class=”style1”>paying a visit
To remove the class attribute, select the <span> tag in the tag selector, or simply click the <span> tag in Markup View and then click Clear Styles in the Apply Styles window, which you can see at the top of Figure 3-23. You’ll end up with this HTML: We're glad you're paying a visit
Because an empty <span> around the text has no use, VWD removes it for you as well. Removing style attributes from HTML elements works the same way.
How It Works Once again, VWD is able to keep all relevant windows in sync: the Design View, Markup View, and the various CSS design tools. When you apply a class from the Apply Styles window, VWD adds the requested class to the selected HTML element in Markup View. It then also updates the Design View window. Similarly, when you remove a selector or a declaration from an embedded style in Design View, both the Design View and the CSS Tools windows are updated.
The final CSS functionality you need to look at in this chapter is located on the Manage Styles and Apply Styles windows. Besides helping you attach CSS files to your documents, these windows enable you to easily manage your styles.
Managing Styles Because it’s so easy to add new inline and embedded styles, your pages may quickly become a mess with styles all over the place. To achieve reusability, you should move as much of your inline and embedded styles as possible to an external style sheet. This is exactly what the Apply Styles and Manage Styles windows enable you to do.
Try It Out
Managing Styles With the Manage Styles and Apply Styles Windows
Earlier in this chapter, you modified the element and applied padding and margin to the heading. However, Default.aspx is not the only page that could benefit from this style for a heading, so it makes sense to move it to the Styles.css file. Similarly, the Introduction class seems reusable enough to include it in the Styles.css file so other pages can access it. This Try It Out shows you how to move styles around in your site.
1 . . 2
Make sure that Default.aspx is still open and switch to Markup View if necessary. Locate the element and click it once. VWD highlights the tag in the Tag Selector at the bottom of the Document Window to indicate it’s the active tag, as shown in Figure 3-24.
100 ❘ Chapter 3 Designing Your Web Pages
Figure 3-24
3.
Open the Apply Styles window by choosing View ➪ Apply Styles from the main menu. Alternatively, if you have the window docked with other windows, simply click its tab to make it active. Make sure the window is not accidentally docked in the main Document Window, but either floats or is placed at the side of the Document Window. At the bottom of the Apply Styles window, you’ll see an inline style appear (see Figure 3-25).
. 4
Right-click Inline Style and choose New Style Copy. The New Style dialog box appears, enabling you to create a new style based on the current selection. At the top of the window, choose h1 from the Selector drop-down list, and from the Define In drop-down list Figure 3-25 choose Existing style sheet. From the URL drop-down list, choose Styles/Styles.css. If that item isn’t available, click the Browse button to locate and select it. Your dialog box should end up like Figure 3-26.
Figure 3-26
Working with CSS in Visual Web Developer ❘ 101
5.
Click OK to close the dialog box. VWD creates a copy of the h1 style and places it in the Styles.css file. Notice that VWD creates a new selector for h1 in the Styles.css file instead of adding the padding and margin info to the existing rule set. If you want, you could combine the two selectors into one manually.
. 6
In the Apply Styles window, right-click Inline Style again, and this time choose Remove Inline Style from the context menu. This removes the style attribute from the h1 element.
7.
Switch to the Manage Styles window. Again, make sure the window is placed besides the Document Window and not docked in the Document Window. Under the Current Page item, locate the .Introduction selector, visible in Figure 3-27.
8.
Click the .Introduction selector once, and then drag it into the area for Styles.css, for example dropping it after the h1 selector. Note that VWD draws lines between the selectors as you hover over them to indicate the point where the selector will end up. Figure 3-27 shows how the .Introduction selector is dragged from the current page into Styles.css, between the h1 and #PageWrapper selectors.
9.
Once you drop the selector in the Styles.css section of the Manage Styles Figure 3-27 window, the associated style is removed from your current page, and then inserted in Styles.css. Because that CSS file is included in your current page using the element, you won’t see a difference in Design View. You can now remove the empty <style> element from Default.aspx, because it’s not needed anymore.
10. Save any pending changes you may have and then open Default.aspx in your browser by pressing Ctrl+F5. Note that the paragraphs haven’t changed and still use the same blue and italic font.
How It Works Unfortunately, VWD doesn’t allow you to move inline styles to external style sheet files. However, by creating a copy of the existing style, and then deleting the original inline style, you can achieve the same effect. Moving embedded or external style sheets between files is a lot easier. You can simply drag a style from one file to another, and VWD will automatically move the code for you. This makes it extremely easy to organize your CSS. Instead of leaving all your embedded CSS in your page because you’re afraid to touch it, you can now simply drag and drop it into an external file. This makes it a lot easier to reuse those styles in other pages, decreasing page size and page bloat, and making your site a lot easier to manage. Obviously, it’s important that the file you are moving your CSS to is attached to the pages you’re working with.
102 ❘ Chapter 3 Designing Your Web Pages
Practical Tips on Working with CSS Follow these tips to make the most of CSS: ➤➤
Take some time to familiarize yourself with the many properties that CSS supports. The best way to do this is to create a brand new page in your Demos folder, create a few HTML elements like and
tags, and then simply experiment with all the different properties. By trying out many of the properties on the CSS Properties Grid, you get a feel for what options you have available. This makes it easier later if you want to apply a certain effect to some piece of content.
➤➤
When creating custom CSS classes, try to come up with names that describe the behavior of the rule, rather than the look and feel. For example, a class called .Introduction to style the first paragraph of a page is a good description. It enables you to change the underlying values without affecting the actual meaning of the name. But classes with names like .BlueAndItalic are guaranteed to give you problems later. What if you decide to change the blue to black later? You either end up with a very odd class name not describing its own behavior, or you’ll need to rename the class and then update the entire site, changing references to the old class to .BlackAndItalic.
➤➤
Try to create smaller and reusable rule sets that you can combine if required, rather than creating large, monolithic rules that can only be used on a single UI element. For example, instead of creating a style like this: .ImportantHeading { font-size: 20px; font-weight: bold; color: red; }
You’re better off creating a few lightweight rules that are easier to reuse: h1 { font-size: 20px; } .Attention { font-weight: bold; color: red; }
When you apply the .Attention class to a heading like this: you get the exact same behavior you got when you gave it the ImportantHeading class. However, with the separate Attention class, you have created a reusable rule that you can apply to other elements that need the user’s attention, like
or <span> elements.
Summary ❘ 103
Summary This chapter gave you a good look at CSS, the most important language for styling your ASPX and HTML web pages. CSS enables you to overcome the limitations of HTML with respect to styling your web pages because it is designed to minimize page bloat, give you greater control over the looks of your page, and generally help you create web sites that load quicker and that are easier to maintain. Once you have a good understanding of the CSS terminology, you’ll find it’s easy to work with the many CSS tools that VWD has on board. Tools like the Manage Styles and Apply Styles windows, the Style Builder, and the smart IntelliSense in the code editor make writing and managing CSS very easy. CSS can be applied not only to HTML as you’ve seen in this chapter, but also to ASP.NET Server Controls. The CSS you apply to those controls eventually ends up in the browser as plain HTML where the same principles apply as those you’ve seen in this chapter. The next chapter gives you a detailed look at the many available ASP.NET Server Controls.
Exercises
1.
What is the main benefit of using an external style over embedded style sheets?
2.
Write a CSS rule that changes the appearance of all headings at level one (h1) in your page to the following: ➤➤
The heading uses an Arial font face.
➤➤
The heading should be blue.
➤➤
The heading must have a font size of 18 pixels.
➤➤
The heading has a blue, thin border at the top and left sides.
For the last requirement, check out VWD’s IntelliSense list in a CSS file to discover another shorthand version for the border property.
3.
Which of the two following rules is easier to reuse across pages in your web site? Can you explain why? #MainContent { border: 1px solid blue; } .BoxWithBorders { border: 1px solid blue; }
4.
VWD enables you to attach an external style sheet to a page in a number of different ways. Can you name three different options to do this? Answers to Exercises can be found in Appendix A.
104 ❘ Chapter 3 Designing Your Web Pages
⊲⊲ What You Learned in this Chapter CSS
Cascading Style Sheets: the language to layout web pages in the browser
CSS Box Model
The model on which the dimensions of elements are calculated with regard to height, width, padding, border, and margin
Declaration
A combination of a property and a value that determines the styling for the element to which the declaration applies
Embedded style sheets
CSS code that is defined in a page in a <style /> element
External style sheets
CSS code that is defined in a separate file and then included in a page using the element
In-line style sheets
CSS defined directly on an element using the style attribute
Rule set
A combination of a selector and one or more declarations wrapped in a pair of curly braces
Selector
A CSS construct to point to one or more elements in the page. Different selectors exist, including the universal selector, the ID and class selectors and the element selector
YOU CAN DOWNLOAD THE CODE FOUND IN THIS BOOK. VISIT WROX.COM AND SEARCH FOR ISBN 9780470502211
4
Working with ASP.NET Server Controls What You Will Learn in This Chapter: ➤➤
What ASP.NET Server Controls are
➤➤
The different kinds of server controls you have at your disposal
➤➤
The common behavior shared among most of the server controls
➤➤
How the ASP.NET runtime processes the server controls on your page
➤➤
How server controls are able to maintain their state across postbacks
ASP.NET Server Controls are the workhorses of ASP.NET. Almost all the pages you build in VWD will contain one or more server controls. These controls come in all sorts and sizes, ranging from simple controls like a Button and a Label to complex controls like the TreeView and the GridView that are capable of displaying data from a data source (like a database or an XML file). You see these controls in Chapters 7 and 13. The architecture of ASP.NET Server Controls is deeply integrated into ASP.NET, giving the controls a feature set that is quite unique in today’s technologies for building web sites. This chapter shows you what server controls are, how they work, and which ones are available out of the box when you install Visual Web Developer. The chapter starts off with a general discussion of server controls. You see how to define them in your code by adding them to Design or Markup View. The section that follows gives you a thorough look at the many controls that are available in the VWD Toolbox.
106 ❘ Chapter 4 Working with ASP.NET Server Controls
Introduction to Server Controls It’s important to understand how server controls operate and how they are completely different from the way you define controls in other languages like classic ASP or PHP — another popular programming language creating dynamic web sites. For example, to influence the text in a text box in these languages, you would use plain HTML and mix it with server-side code. This works similarly to the example in Chapter 2 where the current date and time are displayed on the page. To create a text box with a message and the current time in it in classic ASP, you can use the following code:
As you can see, this code contains plain HTML, mixed with a server-side block, delimited by that outputs the current time using the equals (=) symbol. This type of coding has a major disadvantage: the HTML and server-side code is mixed, making it difficult to write and manage your pages. Although this is a trivial example in which it’s still easy to understand the code, this type of programming can quickly result in very messy and complex pages. Server controls work differently. In ASP.NET, the controls “live” on the server inside an ASPX page. When the page is requested in the browser, the server-side controls are processed by the ASP.NET runtime — the engine that is responsible for receiving and processing requests for ASPX pages. The controls then emit client-side HTML code that is appended to the final page output. It’s this HTML code that eventually ends up in the browser, where it’s used to build up the page. So, instead of defining HTML controls in your pages directly, you define an ASP.NET Server Control with the following syntax, where the italicized parts differ for each control.
For the controls that ship with ASP.NET 4 you always use the asp: prefix followed by the name of the control. For example, to create a TextBox that can hold the same welcome message and current time, you can use the following syntax:
Note that the control has two attributes: ID and Runat. The ID attribute is used to uniquely identify a control on the page, so you can program against it. It’s important that each control on the page has a unique ID; otherwise the ASP.NET runtime won’t understand what control you’re referring to. If you accidentally type a duplicate control ID, VWD will signal the problem in the error list. The mandatory Runat attribute is used to indicate that this is a control that lives on the server. Without this attribute, the controls won’t be processed and will end up directly in the HTML source. If you ever feel you’re missing a control in the final output in the HTML of the browser, ensure that the control has this required attribute. Note that for non-server elements, like plain HTML elements, the Runat attribute is optional. With this attribute on non-server controls, they can be reached by your programming code. You learn more about this later in the book. You can easily add the Runat attribute to an existing element by typing runat and pressing the Tab key.
Introduction to Server Controls ❘ 107
The preceding example of the TextBox is using a self-closing tag where the closing slash (/) is embedded in the opening tag. This is quite common for controls that don’t need to contain child content such as text or other controls. However, the long version, using a separate closing tag is acceptable as well:
You can control the default behavior of closing tags per tag using Tools ➪ Options ➪ Text Editor ➪ HTML ➪ Formatting ➪ Tag Specific Options. You can program against this text box from code that is either placed inline with the page or in a separate Code Behind file, as you saw in Chapter 2. To set the welcome message and the time, you can use the following code: VB.NET
Message.Text = “Hello World, the time is “ & DateTime.Now.TimeOfDay.ToString() C#
Message.Text = “Hello World, the time is “ + DateTime.Now.TimeOfDay.ToString();
The definition of the control in the markup section of the page is now separated from the actual code that defines the text displayed in the text box, making it easier to define and program the text box (or any other control) because it enables you to focus on one task at a time: either declaring the control and its visual appearance in the markup section of the page, or programming its behavior from a code block. You see how server controls send their underlying HTML to the client in the next exercise.
Try It Out
Working with Server Controls
In this exercise, you add a TextBox, a Label, and a Button control to a page. When you request the page in the browser, these server controls are transformed into HTML, which is then sent to the client. By looking at the final HTML for the page in the browser, you’ll see how the HTML is completely different from the initial ASP.NET markup. 1.
Open the Planet Wrox project in Visual Web Developer.
. 2
In the Demos folder in the Solution Explorer, create a new Web Form called ControlsDemo.aspx. Choose your programming language and make sure the Web Form uses Code Behind.
. 3
Switch to Design View. From the Toolbox, drag a TextBox, a Button, and a Label control onto the design surface within the dashed lines of the tag that was added for you when you created the page. Type the text Your name in front of the TextBox and add a line break between the Button and the Label by positioning your cursor between the two controls in Design View and then pressing Enter. If you’re having trouble positioning the cursor between the controls, place it after the Label control and then press the left arrow key twice. The first time you press it, the Label will be selected; the second time, the cursor is placed between the two controls, enabling you to press Enter. Your Design View should now look like Figure 4-1.
108 ❘ Chapter 4 Working with ASP.NET Server Controls
Figure 4-1
4.
Right-click the Button and choose Properties to open up the Properties Grid for the control. Pressing F4 after selecting the Button does the same thing. The window that appears, shown in Figure 4-2, enables you to change the properties for the control, which in turn influence the way the control behaves at runtime.
. 5
Set the control’s Text property to Submit Information and set its ID (which you’ll find all the way down at the bottom of the list wrapped in parentheses) to SubmitButton.
. 6 . 7
Change the ID of the TextBox to YourName using the Properties Grid.
. 8
Figure 4-2
Clear the Text property of the Label using the Properties Grid. You can right-click the property’s label in the grid and choose Reset, or you can manually remove the text. You can leave its ID set to Label1. Still in Design View, double-click the button to have VWD add some code to the Code Behind of the page that will be fired when the button is clicked in the browser. Add the bolded line of code to the code block that VWD inserted for you: VB.NET
Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SubmitButton.Click Label1.Text = “Your name is “ & YourName.Text End Sub C#
protected void SubmitButton_Click(object sender, EventArgs e) { Label1.Text = “Your name is “ + YourName.Text; }
Note that the VB.NET example doesn’t need an underscore here to split the code over two lines. In previous versions of VB.NET the underscore was required to split this code over two lines. . 9
Save the changes to the page and then open it in the browser by pressing Ctrl+F5. When it appears in the browser, don’t click the button yet, but open up the source of the page by right-clicking the
Introduction to Server Controls ❘ 109
page in the browser and choosing View Source or View Page Source. You should see the following HTML code (I changed the formatting slightly so the HTML fits on the page): Your name <span id=”Label1”>
10. Switch back to your browser, fill in your name in the text box, and click the button. When the page is done reloading, open up the source for the page in the browser again using the browser’s rightclick menu. The code should now look like this: Your name <span id=”Label1”>Your name is Imar
Note that the two bold lines have changed, and now show the name you entered in the text box. You can ignore the other HTML in the page for now.
How It Works As its name implies, an ASP.NET Server Control lives on the server in your ASPX page where it can be processed by the ASP.NET runtime. When you request a page in the browser, the runtime creates an in-memory representation of the ASPX file with the controls you created. Once the runtime is about to send the HTML to the browser, it asks each of the controls in the page for their HTML, which is then injected in the final response. For example, when the Label control is asked for its HTML the first time it loads, it returns the following: <span id=”Label1”>
Although you defined the Label control with the syntax, it ends up as a simple <span> element in the browser. Because the Text property of the Label control is empty, you don’t see any text between the two <span> tags. The same applies to other controls; an ends up as , whereas the ends up as . When you click the button, the control causes a postback, which sends the information for the controls in the page to the server, where the page is loaded again. Additionally, the code that you wrote to handle the button’s Click event is executed. This code takes the name you entered in the text box and then assigns it to the Label control: Label1.Text = “Your name is “ + YourName.Text;
Don’t worry about the syntax for the code that handles the button’s Click event for now. In Chapter 5, you see how this works, and why you need this code.
110 ❘ Chapter 4 Working with ASP.NET Server Controls
At this stage, the Label control contains the text you entered in the text box, so when it is asked for its HTML, it now returns this: <span id=”Label1”>Your name is Imar
You get a good look at postbacks later in this chapter when the ASP.NET state engine is discussed.
A Closer Look at ASP.NET Server Controls Because you’ll be working with server controls for most of the time when building your ASP.NET web pages, you need to know in detail how they work and how to use them. In the next section, you see how to add the controls to your pages and change the way they behave in the browser. In the section that follows, you get an overview of the behavior that all server controls have in common. Once you understand this shared behavior, it’s easy to apply this knowledge to other, new controls as well, enabling you to get up to speed with them very quickly.
Defining Controls in Your Pages As demonstrated in the previous Try It Out, you can simply drag controls from the Toolbox onto the design surface of the page. This makes it very easy to add a bunch of controls to a page to get you started. However, because of the way the design surface works, it’s sometimes difficult to add them exactly where you want them. For example, it can be difficult to drag a control between the opening and closing tags of an HTML element. Fortunately, you can just as easily drag a control from the Toolbox in Markup View. Additionally, you can also type the control’s markup directly in Markup View, letting IntelliSense and Code Snippets help you with the different tags and attributes. You’ll also find that the Properties Grid works in Markup View. Simply click the relevant markup, and the Properties Grid will be updated to reflect the tag you clicked. This makes it easy to change the properties of the control, while you can still see exactly what markup gets generated for you. If you look at the Properties Grid for some of the controls in a page, you’ll notice that many of them have similar properties. In the next section, you see exactly what these properties are and what they are used for.
Common Properties for All Controls Most of the server controls you find in the VWD Toolbox share some common behavior. Part of this behavior includes the so-called properties that define the data a control can contain and expose. You learn more about properties and other behavior types in the next chapter. Each server control has an ID to uniquely identify it in the page, a Runat attribute that is always set to Server to indicate the control should be processed on the server, and a ClientID that contains the client-side ID attribute that will be assigned to the element in the final HTML. In versions of ASP.NET up to 3.5 this ClientID was always generated for you automatically. However, in ASP.NET 4 a new ClientIDMode property has been introduced that gives you more control over the ID of an element
A Closer Look at ASP.NET Server Controls ❘ 111
at the client. You see how this works in later chapters. The Runat attribute does not really belong to the server control, but is necessary to indicate that the markup for the control should be processed as a server control and not end up as plain text or HTML in the browser. Besides these properties, many of the server controls share more properties. The following table lists the most common ones and describes what they are used for. Property
Description
AccessKey
Enables you to set a key with which a control can be accessed at the client by pressing the associated letter.
BackColor ForeColor
Enables you to change the color of the background (BackColor) and text (ForeColor) of the control.
BorderColor BorderStyle BorderWidth
Changes the border of the control in the browser. The similarities with the CSS border properties you saw in the previous chapter are no coincidence. Each of these three ASP.NET properties maps directly to its CSS counterpart.
CssClass
Lets you define the HTML class attribute for the control in the browser. This class name then points to a CSS class you defined in the page or an external CSS file.
Enabled
Determines whether the user can interact with the control in the browser. For example, with a disabled text box (Enabled=”False”) you cannot change its text.
Font
Enables you to define different font-related settings, such as Font-Size, FontNames, and Font-Bold.
Height Width
Determines the height and width of the control in the browser.
TabIndex
Sets the client-side HTML tabindex attribute that determines the order in which users can move through the controls in the page by pressing the Tab key.
ToolTip
Enables you to set a tooltip for the control in the browser. This tooltip, rendered as a title attribute in the HTML, is shown when the user hovers the mouse over the relevant HTML element.
Visible
Determines whether or not the control is sent to the browser. You should really see this as a server-side visibility setting because an invisible control is never sent to the browser at all. This means it’s quite different from the CSS display and visibility properties you saw in the previous chapter that hide the element at the client.
To see how all these attributes end up in the browser, consider the following markup for a TextBox server control:
112 ❘ Chapter 4 Working with ASP.NET Server Controls
When you request the page with this control in the browser, you end up with the following HTML:
This results in the text box shown in Figure 4-3. Note that most of the server-side control properties have been moved to CSS inline styles with the style attribute. When building web sites, it’s quite uncommon to define a TextBox in this manner. As you learned in the previous chapter, you should avoid inline styles as much as possible, and opt for external CSS style sheets instead. You can accomplish the exact same behavior with this server-side control:
Figure 4-3
And the following CSS class: .TextBox { background-color: Black; color: White; font-size: 30px; border-color: Yellow; border-style: Dashed; border-width: 4px; height: 40px; width: 200px; }
Obviously, the second example is much easier to read, reuse, and maintain. If you want another text box with the exact same look, you simply assign TextBox to the CssClass of that control. Also, notice I left out the Enabled and Visible properties. Both default to True so there’s no need to explicitly state that in the control declaration. Although it’s recommended to use CSS classes instead of these inline styles, it’s good to know about the server-side control properties in case you need fine control over them. If you programmatically change the control’s properties (as you learn how to do later) they still end up as inline styles, and thus possibly override settings in embedded or external style sheets. Now that you have seen the generic behavior that all server controls share, it’s time to look at the large number of controls that ship with ASP.NET 4.
Types of Controls ❘ 113
Types of Controls Out of the box, ASP.NET 4 comes with a large number of server controls, supporting most of your web development needs. To make it easy for you to find the right controls, they have been placed in separate control categories in the Visual Web Developer Toolbox (accessible by pressing Ctrl+Alt+X). Figure 4-4 shows the Toolbox with all the available categories. In the following sections, you see the controls in each category and the tasks for which they are designed. With the discussion of the various controls, you see a mention of the properties of a control. For example, a TextBox has a Text property (among many others), and a ListBox has a SelectedItem property. Some properties can only be set programmatically and not with the Properties Grid. Programmatically reading and changing control properties is discussed in detail in the next chapter.
Figure 4-4
Standard Controls The Standard category contains many of the basic controls that almost any web page needs. You’ve already seen some of them, like the TextBox, Button, and Label controls earlier in this chapter. Figure 4-5 shows all the controls in the Standard category. Many of the controls probably speak for themselves, so instead of giving you a detailed description of them all, the following sections briefly highlight a few important ones.
Simple Controls The Toolbox contains a number of simple and straightforward controls, including TextBox, Button, Label, HyperLink, RadioButton, and CheckBox. Their icons in the Toolbox give you a good clue as to how they end up in the browser. In the remainder of this book, you see these controls used many times.
List Controls The standard category also contains a number of controls that present themselves as lists in the browser. These controls include ListBox, DropDownList, CheckBoxList, RadioButtonList, and BulletedList. To add items to the list, you define elements between the opening and closing tags of the control, as shown in the following example: C# Visual Basic CSS
Figure 4-5
114 ❘ Chapter 4 Working with ASP.NET Server Controls
The DropDownList enables a user to select only one item at a time. To see the currently active and selected item of a list control programmatically, you can look at its SelectedValue, SelectedItem, or SelectedIndex properties. SelectedValue returns a string that contains the value for the selected item, like C# or Visual Basic in the preceding example. SelectedIndex returns the zero-based index of the item in the list. With the preceding example, if the user had chosen C#, SelectedIndex would be 0. Similarly, when the user has chosen CSS, the index would be 2 (the third item in the list). The BulletedList control doesn’t allow a user to make selections, and as such doesn’t support these properties. For controls that allow multiple selections (like CheckBoxList and ListBox), you can loop through the Items collection and see what items are selected. In this case, SelectedItem returns only the first selected item in the list; not all of them. You learn how to access all the selected items in the next exercise. To see how to add list items to your list control, and how to read the selected values, the following exercise guides you through creating a simple Web Form with two list controls that ask users for their favorite programming language.
Try It Out
Working with List Controls
In this exercise you add two list controls to a page. Additionally, you add a button that, when clicked, displays the selected items as text in a Label control. 1.
In the Demos folder, create a new Web Form called ListControls.aspx. Make sure you create a Code Behind file by checking the Place Code in Separate File option.
. 2
Switch to Design View and drag a DropDownList from the Toolbox onto the design surface of the page within the dashed border of the element that is already present in your page.
. 3
Notice that as soon as you drop the DropDownList control on the page, a pop-up menu appears that is labeled DropDownList Tasks, as shown in Figure 4-6.
Figure 4-6
This pop-up menu is called the Smart Tasks panel. When it appears, it gives you access to the most common tasks of the control it belongs to. In the case of the DropDownList, you get three options. The first option enables you to bind the control to a data source, which is demonstrated in Chapter 13. The second item enables you to manually add items to the list, whereas the last option sets the AutoPostBack property of the control. With this option checked, the control will
Types of Controls ❘ 115
submit the page it is contained in back to the server as soon as the user chooses a new item from the list. The Smart Tasks panel only appears for the more complex controls that have a lot of features. You won’t see it for simple controls like Button or Label. To reopen the Smart Tasks panel, rightclick the control in the designer and choose Show Smart Tag. Alternatively, click the small arrow at the top-right corner of the control, visible in Figure 4-6. On the Smart Tasks panel of the DropDownList, click the Edit Items link to bring up the ListItem Collection Editor, shown in Figure 4-7.
Figure 4-7
This dialog box enables you to add new items to the list control. The items you add through this window will be added as elements between the tags for the control. . 4
Click the Add button on the left side of the screen to insert a new list item. Then in the Properties Grid on the right, enter C# for the Text property and press Tab. As soon as you tab away from the Text property, the value is copied to the Value property as well. This is convenient if you want both the Text and the Value property to be the same. However, it’s perfectly OK (and quite common) to assign a different value to the Value property.
. 5
Repeat step 4 twice, this time creating list items for Visual Basic and CSS. You can use the up and down arrows in the middle of the dialog box to change the order of the items in the list. Finally, click OK to insert the items in the page. You should end up with the following code: C# Visual Basic CSS
6.
Switch to Markup View and drag a CheckBoxList control from the Toolbox directly into the code window, right after the DropDownList.
116 ❘ Chapter 4 Working with ASP.NET Server Controls
7.
Copy the three elements from the DropDownList you created in steps 4 and 5 and paste them between the opening and closing tags of the CheckBoxList. You should end up with this code: CSS C# Visual Basic CSS
8.
Switch to Design View and drag a Button from the Toolbox in Design View to the right of the CheckBoxList control. The Button will be placed below the CheckBoxList. Next, drag a Label control and drop it to the right of the Button. Create some room between the Button and the Label by positioning your cursor between the controls and then pressing Enter twice. Double-click the Button to open the Code Behind of the page.
. 9
In the code block that VWD added for you, add the following bolded code, which will be executed when the user clicks the button: VB.NET
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Button1.Click Label1.Text = “In the DDL you selected “ & DropDownList1.SelectedValue & “ ” For Each item As ListItem In CheckBoxList1.Items If item.Selected Then Label1.Text &= “In the CBL you selected “ & item.Value & “ ” End If Next End Sub C#
protected void Button1_Click(object sender, EventArgs e) { Label1.Text = “In the DDL you selected “ + DropDownList1.SelectedValue + “ ”; foreach (ListItem item in CheckBoxList1.Items) { if (item.Selected == true) { Label1.Text += “In the CBL you selected “ + item.Value + “ ”; } } }
Notice how in the VB.NET code the underscore is needed again to split the code over two lines. VB.NET requires the underscore if you want to move the Handles keyword to its own line.
Types of Controls ❘ 117
10. Save the changes to the page and then request it in the browser. Choose an item from the DropDownList, check one or more items in the CheckBoxList, and click the button. You should see something similar to Figure 4-8, which shows the page in Firefox.
Figure 4-8
How It Works The various list controls all use elements. That makes it easy to reuse them by copying them from one control to another. Because the DropDownList supports only one selected item at a time, it’s pretty easy to get its selected value. All it takes is a single line of code (shown in C#): Label1.Text = “In the DDL you selected “ + DropDownList1.SelectedValue + “ ”;
The CheckBoxList control enables a user to select multiple items at once. Therefore, you need a bit more code to loop over the collection of items, checking the Selected property of each item (again shown in C#): foreach (ListItem item in CheckBoxList1.Items) { if (item.Selected == true) { Label1.Text += “In the CBL you selected “ + item.Value + “ ”; } }
The CheckBoxList and the other list controls have an Items collection that contains all the items you defined in the code. So, given the code from this Try It Out, CheckBoxList1 contains three items, for C#, Visual Basic, and CSS, respectively. Each ListItem in turn contains a Selected property that determines whether or not the user has checked the item in the list. Using a foreach loop (For Each in VB.NET), you can iterate over the collection of ListItem elements, testing the Selected property one by one. If the item was selected in the list, its Selected property is true and its Value is appended to the text of the Label. Notice the use of += (&= in VB.NET) in the
118 ❘ Chapter 4 Working with ASP.NET Server Controls
last code example to assign the Value of the list item together with the text. The += and &= syntax is shorthand for this: Label1.Text = Label1.Text + “In the CBL you selected “ + item.Value + “ ”;
This code takes the current text from the Label control, appends “In the CBL you selected “ + item.Value + “ ” to it, and then reassigns the entire string back to the Text property of the label. Using the += syntax is often a bit easier to write and understand, but the longer version is common as well. Both VB.NET and C# have support for a For Each loop, although both languages use a slightly different syntax. In the next chapter you learn a lot more about looping and other language constructs. Also of note is the way the ListItems are set up. In the first example, before the Try It Out, you saw ListItem elements with both a value and text: C# Visual Basic CSS
When you add items to the list yourself with the ListItem Collection Editor, you don’t get the Value attributes: C# Visual Basic CSS
You didn’t get the Value attribute, because you didn’t supply an explicit value for the item in the ListItem Collection Editor. If you omit the Value, the text between the opening and closing tags of the ListItem is used implicitly as the value, which is fine in many cases. However, it’s also quite common to have a different Value and Text property in the list. For example, when you have a list with countries, you could use the full name of the country as the Text (like Netherlands) and use the official country code (nl) as the Value for the drop-down list. You see the list controls at work in other chapters in this book.
Container Controls Quite often it’s desirable to have the ability to group related content and controls. This grouping can be done by putting the controls (and other markup) in one of the container controls, like the Panel, the PlaceHolder, the MultiView, or the Wizard. For example, you can use the PlaceHolder or the Panel control to hide or show a number of controls at once. Instead of hiding each control separately, you simply hide the entire container that contains all the individual controls and markup. Both of these controls have their own advantages and disadvantages. The good thing about the PlaceHolder control is that it emits no HTML of its own into the page, so you can use it as a container without any side effects in the final page. However, it lacks design-time support, making it hard to manage the controls inside the PlaceHolder at design time in VWD. In contrast, the Panel enables you to easily access all controls and other content it contains but renders itself as a tag. In many cases this isn’t a problem, so usually you’re best off with the Panel control because of its design-time support.
Types of Controls ❘ 119
The MultiView (which can contain one or more controls) and the Wizard are similar in that they enables you to split up a long page into multiple areas, making it easy to fill in a long form, for example. The Wizard has built-in support for moving from page to page using Previous, Next, and Finish buttons, whereas the MultiView must be controlled programmatically.
A Closer Look at the Panel Control In the following exercise, you use a Panel control to create a container for other controls and markup. You only add some text for now, but in a subsequent Try It Out exercise you add ASP.NET controls to the panel.
Try It Out
Using the Panel Control
In this exercise you see how to use the Panel control as a container for some simple text. In addition, you use a CheckBox to control the visibility of the Panel at the server.
1 . 2.
Start by creating a new Web Form with Code Behind called Containers.aspx in the Demos folder. Switch the page into Design View and drag a CheckBox and a Panel control from the Toolbox on the design surface into the dashed element.
. 3
Give the CheckBox control a meaningful description by setting its Text property to Show Panel and set its AutoPostBack property to True using the Properties Grid. Rather than choosing True from the drop-down list for the property, you can also double-click the AutoPostBack property or its value to toggle between False and True.
4.
Set the Visible property of the Panel control to False using the Properties Grid. This hides the Panel control when the page first loads.
. 5
Inside the Panel control, type some text (for example, I am visible now). Note that the panel behaves like the rest of VWD’s design surface. You can simply add text to it, select and format it, and even add new controls to it by dragging them from the Toolbox. The code for the panel should end up like this: I am visible now
6.
Double-click the CheckBox control in Design View and, inside the code that VWD added for you, enter the following bolded line of code: VB.NET
Protected Sub CheckBox1_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged Panel1.Visible = CheckBox1.Checked End Sub C#
120 ❘ Chapter 4 Working with ASP.NET Server Controls
7.
Save all your changes and then request the page in the browser by pressing Ctrl+F5.
. 8
When the page first loads, all you see is the CheckBox and the text beside it. When you click the CheckBox control to place a checkmark in it, the page reloads and now shows the text you entered in step 5.
COMMON MISTAKES If nothing happens, go back to the source of the page in VWD and ensure that AutoPostBack is set to True on the CheckBox control.
If you look at the HTML in the browser (right-click the page and choose View Source or View Page Source), you’ll see that the text you typed in step 5 is surrounded with a tag with an id of Panel1: I am visible now
How It Works In step 4 of this exercise you set the Visible property of the Panel control to False. This means that when the page loads, the control is not visible on the server and thus its HTML never makes it to the browser. When you then check the CheckBox, a postback occurs, which sends the information contained in the form to the server. At the server, some code is run that is fired whenever the checkbox changes its state from checked to unchecked or vice versa. Inside that code block, the following code is executed (shown in C#): Panel1.Visible = CheckBox1.Checked;
This means that the Panel is only visible when the checkbox is checked. When it isn’t, the Panel is hidden automatically. In the next chapter you learn much more about the code that makes this happen, and why you need code like the line with CheckBox1_CheckedChanged.
As you can see, it’s easy to add text and other markup to the Panel control in Visual Web Developer. Right now, you only added some plain text but in the next section you see how to add a Wizard control and how to use it.
Magic with the Wizard Control The Wizard control is a great tool for breaking apart large Web Forms and presenting them as bitesized chunks of information to the user. Instead of confusing your user with one page with many controls and text on it, you can break the page apart and present each part on a separate wizard page. The Wizard control then handles all navigation issues by creating Next, Previous, and Finish buttons automatically. In the following exercise you use a wizard to ask a user for her name and favorite programming language. Although the example itself is pretty trivial, and you could have
Types of Controls ❘ 121
placed both questions on the same page without confusing the user, the example shows how the wizard works and why it’s useful. You can easily apply the same techniques to your own, possibly larger, Web Forms.
Try It Out
Using the Wizard to Create Easy-to-Use Forms
In this Try It Out, you place a Wizard inside the panel you created in the previous exercise that enables a user to fill in a form that is spread over a couple of pages. The wizard will have two steps where a user can enter details and a results page that shows the data the user has provided. 1.
Make sure you still have Containers.aspx page open in Design View. Remove the text “I am visible now” that you entered in the previous Try It Out, and then drag a Wizard control from the Toolbox inside the Panel. Drag its right edge further to the right, increasing the total width of the control to 500px. Your page now looks similar to Figure 4-9.
Figure 4-9
2.
Open the Wizard’s Smart Tasks panel (click the arrow in its upper right-hand corner) and choose Add/Remove WizardSteps. In the dialog box that follows click the Add button to insert a third wizard step, shown in Figure 4-10.
Figure 4-10
3.
Click the first WizardStep labeled Step 1 in the Members list on the left and change its Title from Step 1 to About You. Set the Title of the other two steps to Favorite Language and Ready, respectively.
122 ❘ Chapter 4 Working with ASP.NET Server Controls
4.
Change the StepType of the second step (now labeled Favorite Language) to Finish and of the last step to Complete. You can leave the StepType of the first step set to Auto. Click OK to close the WizardStep Collection Editor.
. 5
In Design View, click About You in the list at the left to make it the active step and drag a Label and a TextBox to the right side of the Wizard. You need to drag them inside the grey rectangle that’s in the upper-right corner of the Wizard, or the controls won’t end up inside the Wizard. Set the Text property of the Label to Type your name and change the ID of the TextBox to YourName. When you’re done, your Wizard looks like Figure 4-11.
Figure 4-11
6.
Click the Favorite Language item in the list on the left to make it the active step. If you don’t see Favorite Language turn bold, open the Wizard’s Properties Grid and set the ActiveStepIndex to 1. Add a DropDownList to the rectangle with the grey border on the right part of the wizard step. Rename the DropDownList by setting its ID to FavoriteLanguage. Open the Smart Tasks panel of the DropDownList control and choose Edit Items. Add the same three items you added in an earlier Try It Out: for C#, Visual Basic, and CSS, respectively. If you want, you can copy the three items from the page ListControls.aspx and paste them between the tags inside the second step. You should end up with the following code for the second step: C# Visual Basic CSS
7.
Switch to Markup View, and inside the last WizardStep labeled Ready, drag a label control from the Toolbox and rename it by setting its ID to Result. If you try to switch to the last step in Design View, you may notice that the wizard disappears. If that happens, switch to Markup View and set ActiveStepIndex to 0 again on the opening tag of the <Wizard> control.
. 8
Double-click the wizard in Design View and add the following bolded code, which will be executed when the user clicks the Finish button on the last step of the wizard. If you’re having problems getting VWD to create the correct code for you, as you see it in the next snippet, select the Wizard,
Types of Controls ❘ 123
press F4 to open up the control’s Properties Grid, and then click the button with the lightning bolt on it (the fourth button from the left on the toolbar of the Properties Grid), as shown in Figure 4-12.
Figure 4-12
This part of the Properties Grid is often referred to as the Events Tab of the Properties Grid. Locate and double-click FinishButtonClick in the Action category. With both methods, you should end up with some code for Wizard1_FinishButtonClick that you need to extend with the following code (the next chapter shows you what the lightning bolt does, exactly, and what it is used for): VB.NET
Protected Sub Wizard1_FinishButtonClick(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.WizardNavigationEventArgs) _ Handles Wizard1.FinishButtonClick Result.Text = “Your name is “ & YourName.Text Result.Text &= “ Your favorite language is “ & FavoriteLanguage.SelectedValue End Sub C#
protected void Wizard1_FinishButtonClick(object sender, WizardNavigationEventArgs e) { Result.Text = “Your name is “ + YourName.Text; Result.Text += “ Your favorite language is “ + FavoriteLanguage.SelectedValue; }
9.
Switch back to Design View and open the Properties Grid for the Wizard and make sure its ActiveStepIndex is set to 0. The designer remembers the last step you designed and stores the value in the ActiveStepIndex of the Wizard in Markup View. To make sure the wizard starts on the first page, you should always set the ActiveStepIndex back to 0 (or click the first step in the Wizard control in Design View) before you save your changes and run the page.
1 0.
Press Ctrl+F5 to open the page in the browser. Select the CheckBox to make the Panel visible and enter your name on the first wizard page. Click Next and choose your favorite programming language. Notice how there’s now a Previous button available that enables you to go back to the first
124 ❘ Chapter 4 Working with ASP.NET Server Controls
step of the wizard if you wanted to change your name. Instead of clicking the Next and Previous buttons, you can also click the links on the left of the wizard in the browser. When you click the Finish button, you’ll see the results of the information you entered in the wizard (see Figure 4-13).
Figure 4-13
How It Works The Wizard control takes care of most of the hard work for you. It handles the navigation, determines when to show the correct buttons (Next, Previous, and Finish), and ensures that in the resulting page the values for the controls you added in the wizard steps are still available so you can show them in the results label. It does this with a concept called View State, something you learn more about toward the end of this chapter. All you have to do is define the steps and set their StepType. The StepType of the first step was set to Auto. With this setting, the wizard figures out what buttons to show. Because it’s the first step in the Wizard and there’s no previous step to go to, it leaves out the Previous button. You set the StepType of the second step to Finish, which tells the wizard to draw a Previous button and a Finish button. When you click the Finish button, the wizard arrives at the last step with its StepType set to Complete. On this step, the navigation buttons are hidden, and all you see is the label with the result, which was assigned with the code in the Code Behind of the page. In Chapter 5, you learn more about the code in the Code Behind that makes this possible.
In addition to the controls you have seen in the previous sections, a few other controls are worth examining. Not all of them are discussed here in detail because they aren’t used any further in this book. Consult the documentation that comes with VWD for a detailed description of these controls. Another good source of information is the Microsoft Developer Network (MSDN) site at http://msdn.microsoft.com. To find information about controls on this site, use your favorite search engine and then search for ControlName Control MSDN. For example, to find more information on the Wizard control you’d search for Wizard Control MSDN. Typically, the MSDN site is at the top of the search results.
Other Standard Controls This section briefly discusses the remainder of the controls in the Standard category of the Toolbox. You see many of them used in the sample application in the rest of the book.
Types of Controls ❘ 125
LinkButton and ImageButton The LinkButton and the ImageButton controls operate similarly to an ordinary Button control. Both of them cause a postback to the server when they are clicked. The LinkButton presents itself as a simple element but posts back (using JavaScript) instead of requesting a new page. The ImageButton does the same, but displays an image that the user can click to trigger the postback.
Image and ImageMap These controls are pretty similar in that they display an image in the browser. The ImageMap enables you to define hotspots on the image that when clicked either cause a postback to the server or navigate to a different page.
Calendar The Calendar control presents a rich interface that enables a user to select a date. You see more of it toward the end of this chapter when the ASP.NET state engine is discussed.
FileUpload The FileUpload control enables a user to upload files that can be stored on the server. You see more of this control later in this book.
Literal, Localize, and Substitute All three controls look a little like the Label control because they can all display static text or HTML. The biggest advantage of the Literal is that it renders no additional tag itself; it displays only what you assign to its Text property and is thus very useful to display HTML or JavaScript that you build up in the Code Behind or that you retrieve from a database. The Localize control is used in multilingual web sites and is able to retrieve its contents from translated resource files. The Substitute control is used in advanced caching scenarios and enables you to update only parts of a page that is otherwise cached completely. Both of these controls fall outside the scope of this book, but for a good discussion of them you may want to get a copy of Wrox’s Professional ASP.NET 4 in C# and VB (ISBN: 978-0-470-50220-4).
AdRotator The AdRotator control enables you to display random advertisements on your web site. The ads come from an XML file that you create on your server. Because it lacks advanced features like click tracking and logging that are required in most but the simplest scenarios, this control isn’t used much in today’s web sites.
HiddenField The HiddenField control enables you to store data in the page that is submitted with each request. This is useful if you want the page to remember specific data without the user seeing it on the page. Because the field does show up in the HTML source of the page and is thus accessible to the end user, you should never store any sensitive data in it.
126 ❘ Chapter 4 Working with ASP.NET Server Controls
XML The XML control enables you to transform data from an XML format to another format (like XHTML) for display on a page. Check out Professional ASP.NET 4 for a detailed description.
Table The control is in many respects identical to its HTML
counterpart. However, because the control lives at the server, you can program against it, dynamically creating new columns and rows and adding dynamic data to it. This concludes the discussion of the controls in the Standard category of the Toolbox. In most web pages, you’ll be working with at least a few of these controls. The remainder of this section discusses the other categories of the Toolbox. Because most of them are used in some form in the remainder of this book, this chapter just briefly describes their purpose so you get a rough idea what they are used for. You’ll find cross-references to the other chapters where you can find out more information about them.
HTML Controls The HTML category of the Toolbox contains a number of HTML controls that look similar to the ones found in the Standard category. For example, you find the Input (Button) that looks like the . Similarly, there is a Select control that has the and as its counterparts. In contrast to the ASP.NET Server Controls, the HTML controls are client-side controls and end up directly in the final HTML in the browser. You can expose them to server-side code by adding a Runat=”Server” attribute to them. This enables you to program against them from the Code Behind of a Web Form, to influence things like their visibility. The HTML controls have a lot less functionality than the ones in the Standard category. For example, the Select control lacks design-time support for adding new items to the list with the ListItem Collection Editor. This forces you to write the items by hand in the Markup View of VWD. Because the controls in the Standard and HTML category look quite like each other, the next section discusses their differences and gives you some idea when to favor one category over the other.
How to Choose between Standard and HTML Controls There seems to be some overlap between the controls in the Standard and HTML categories of the Toolbox. So which ones should you choose and when? Generally, the true server controls in the Standard category offer you a lot more functionality, both in terms of design-time support in VWD and what they can do at runtime. But this functionality comes at a price. Because of their increased complexity, the server controls take a little more time to process. However, on most web sites you probably won’t notice the difference. Only when you have a high-traffic web site with lots of controls on the page do the HTML controls give you a slightly better performance and consume less memory on the server when compared to the server controls. In most scenarios, favor the server controls over their HTML counterparts. Because server controls offer more functionality, they give you more flexibility in your pages, allowing you to create a richer user experience. Also the better design-time support makes it worth choosing these controls.
Types of Controls ❘ 127
Choose the HTML controls if you’re really sure that you don’t need the functionality that the server controls offer you. The remainder of this section quickly guides you through the other categories in the Toolbox.
Data Controls Data controls were introduced in ASP.NET 2.0, and offer an easy way to access various data sources like databases, XML files, and objects. Instead of writing lots of code to access the data source as you had to do in earlier versions of ASP.NET, you simply point your data control to an appropriate data source, and the ASP.NET runtime takes care of most of the difficult issues for you. You see a lot more about these controls in Chapter 13 and onward.
Validation Controls Validation controls enable you to rapidly create Web Forms with validation rules that prohibit users from entering invalid data. For example, you can force users to enter values for required fields and check whether the entered data matches a specific format like a valid date or a number between 1 and 10. They even allow you to write custom code to create validation routines that are not covered by the standard controls. The beauty of the validation controls is that they can execute both on the client and the server, enabling you to create responsive and secure web applications. Chapter 9 digs much deeper into these controls.
Navigation Controls The controls you find under the Navigation category of the Toolbox are used to let users find their way through your site. The TreeView control presents a hierarchical display of data and can be used to show the structure of your site, giving easy access to all the pages in the site. The Menu control does a similar thing and provides options for horizontal and vertical fold-out menus. The SiteMapPath control creates a “breadcrumb trail” in your web pages that enables your users to easily find their way up in the hierarchy of pages in your site. You see all of these controls in action in Chapter 7, which deals with navigation in web sites exclusively.
Login Controls Just like the Data and Navigation controls, the Login controls were introduced in ASP.NET 2.0 and are still strongly present in ASP.NET 4. With very little effort, login controls enable you to create secure web sites where users need to sign up and log in before they can access specific parts of the web site (or even the entire web site). In addition, they provide the tools for users to change their password, or request a new password if they forget the old one, and enable you to display different data depending on the logged-in status and role of the user. Chapter 16 provides more details about the security features and login controls of ASP.NET.
Ajax Extensions More than a year after the official release of ASP.NET 2.0 in November 2005, Microsoft released the ASP.NET 2.0 AJAX Extensions 1.0 as an add-on for ASP.NET 2.0. These extensions enable you
128 ❘ Chapter 4 Working with ASP.NET Server Controls
to create flicker-free web applications that are able to retrieve data from the server from client-side JavaScript without a full postback. Ever since Ajax became a hot technology in 2005, Microsoft has been working hard to get to the top of the Ajax implementers. The Ajax extensions were fully integrated in the VWD 2008 IDE and have been upgraded to AJAX 4 in VWD 2010. Chapter 10 focuses on Ajax.
WebParts ASP.NET WebParts are a set of controls that enables an end user of a web page to change the appearance and behavior of a web site. With a few simple actions, users can change the entire appearance of a web site by rearranging content, hiding or showing parts of the web page, and adding other content fragments to the page. The ASP.NET WebParts are outside the scope of this book because they deserve an entire book. If you want to learn more about WebParts, check out Professional Web Parts and Custom Controls with ASP.NET 2.0 by Peter Vogel (ISBN: 978-0-7645-7860-1). Although the book is targeted at ASP.NET 2.0, you’ll find that many of the concepts presented in the book still apply to ASP.NET 4.
Dynamic Data The controls in this category are used in Dynamic Data Web Sites. Dynamic Data sites enable you to quickly build a user interface to manage data in a database. These controls are not discussed further in this book.
The ASP.NET State Engine In the previous chapter, you created a page with a TextBox and a Button control. In the Try It Out, you ran this page in the browser, typed some text, and clicked the button. The button caused a postback to the server, and when the page reloaded, the text was still present in the text box. You pretty much did the same thing with the Wizard control in this chapter, where the values from the text box and the drop-down list were maintained as well. If you’re familiar with other web technologies like ASP or PHP, this probably surprised you. In those languages, you often need to write lots of code to make this happen. So why and how does this work automatically in ASP.NET? The text in the text box is maintained by the ASP.NET state engine, a feature that is deeply integrated into the ASP.NET runtime. It enables controls to maintain their state across postbacks, so their values and settings remain available after every postback of the page.
What Is State and Why Is It Important? To understand state, it’s important to realize that by design, HTTP — the protocol used to request and serve pages in a web browser — is stateless. What this means is that the web server does not keep track of requests that have been made from a specific browser. As far as the web server is concerned, each request you make to the server by browsing to a page and clicking links to other pages stands on its own. The web server has no recollection of pages you requested previously.
The ASP.NET State Engine ❘ 129
This poses some interesting problems. Consider, for example, a simple login page that enables you to log in to a web site, like your favorite web mail program. You can see a sample of the login box in Figure 4-14. Now imagine that you try to log in with a correct user name but with Figure 4-14 an incorrect password. The page will then inform you that your login attempt failed. Ideally, you would also want your user name to be filled in for you automatically, and you’d want the Remember Me Next Time check box to retain its selection as well. That way, it’s easy for the user to enter the correct password and click the Log In button again. This is just a trivial example, but it’s easy to come up with many more scenarios where it’s useful if controls are able to maintain their own state. However, by default, a web page or a control cannot do this on its own. Because each request is a standalone request, the server won’t fill in the text boxes again after a postback, but will simply serve the page the same way it did when it first loaded it. In other web technologies, like classic ASP or PHP, you could work around this by manually writing code that prepopulates controls after a postback. Fortunately, ASP.NET makes this much easier for you by integrating this functionality in the ASP.NET feature set.
How the State Engine Works The state engine in ASP.NET is capable of storing state for many controls. It can store state not only for user input controls like a TextBox and a CheckBox but for other controls like a Label and even a Calendar. This is best demonstrated by a demo. The following exercise shows you how to create a page with controls that are capable of maintaining their state. The sections that follow then explain how ASP.NET is able to do this.
Try It Out
Examining the ASP.NET State Engine
In this exercise you add Label, Button, and Calendar controls to the page. These controls are used to demonstrate some of the inner workings of ASP.NET, including postbacks and the way ASP.NET maintains state. 1.
Under the Demos folder, create a new page called State.aspx. Make sure it uses Code Behind, and don’t forget to choose your preferred programming language.
. 2
Switch the page to Design View, click inside the dashed to put the focus on it, and then choose Table ➪ Insert Table from the main menu and click OK to insert a table with two rows and two columns.
. 3
In the first cell of the first row, drag a Label control from the Toolbox. In the first cell of the second row, drag a Calendar control.
4.
Note that as soon as you drop the calendar in the cell, the Smart Tasks panel for the Calendar pops up as shown in Figure 4-15. In the case of the Calendar, you only get one option on this panel, Auto Format, which enables you to change the appearance of the calendar. Click the link, choose from one of the predefined color schemes, like Simple, and click OK.
130 ❘ Chapter 4 Working with ASP.NET Server Controls
Figure 4-15
5.
Next, drag a Button control into each of the two cells in the right column of the table.
. 6
Click the Button in the first row and press F4 to open the Properties Grid. Set the Button’s ID to SetDate and set its Text property to Set Date. You’ll find the ID property all the way at the end of the list with properties or at the beginning if you have the list with properties sorted alphabetically.
. 7
Repeat the previous step for the other button but call it PlainPostBack and set its Text property to Plain PostBack. When you’re done, the page should look like Figure 4-16 in Design View.
Figure 4-16
8.
Double-click the Set Date button in Design View and add the following bolded code on the empty line between the code lines that VWD inserted for you: VB.NET
Protected Sub SetDate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SetDate.Click Label1.Text = DateTime.Now.ToString() End Sub
Open the page in the browser by pressing Ctrl+F5. Select a date on the calendar by clicking one of the days. Notice that as soon as you click the date, the page seems to reload, caused by a postback. You learn more about this in the How It Works section that follows this exercise.
1 0.
Click the Set Date button a few times. Again, the page is posted back to the server and the Label is updated with today’s date and time each time you click the button. Wait a few seconds, and then click the Plain PostBack button. Once again, a postback occurs, and then the page reloads. Now take a look at the text for the Label. It still contains the date and time that was displayed when you last clicked the Set Date button. Click the Plain PostBack button a few more times and notice that the Label doesn’t change.
1 1.
Go back to VWD and open the Properties Grid for the Label control in Design View. Locate the EnableViewState property and set it to False by selecting that value from the drop-down list or by double-clicking the property name or its value.
12. Repeat steps 9 and 10 by reopening the page in the browser and clicking the calendar and the buttons. This time, when you click the Plain PostBack button, you’ll see that the Label control defaults to its initial text: Label.
How It Works To understand how this all works, there are a few important elements to look at. First, open up the page in the browser again and view its HTML source. You can do this by right-clicking the page in the browser and choosing the View Source or View Page Source menu item. Near the top of the window, you see the following element: ...
The HTML element is used to enable a user to submit information from the browser to the server. A user can enter information using controls like text boxes, drop-down lists, check boxes, and so on. A form can be submitted in two ways: with POST (as shown in the previous element) or with GET. In the former case, all data from the form is added to the body of the request and then sent to the server. In the case of the GET method, all the data is appended to the actual address of the request. The intricacies of the differences are not that important right now; what’s important to understand is what the element is used for: it encapsulates form controls whose values are submitted back to the server en masse. When a control like a Button is clicked, it causes a postback to the server. During this postback, all the relevant information in the form is submitted back to the server where it can be used to rebuild the page. By default, all your ASP.NET Web Forms always use the POST method to send data to the server. Also, by default, an entire ASP.NET page always contains exactly one form. Because this is so common, a new
132 ❘ Chapter 4 Working with ASP.NET Server Controls
page (or Master Page as you learn in Chapter 6) created in VWD already contains the element, so you don’t have to add it yourself. Finally, it’s important to understand that an ASP.NET Web Form by default always submits back to itself. In other web environments, like classic ASP and PHP, it’s not uncommon to set the action attribute of the page to a second page that then processes the data the user has submitted. However, with an ASP.NET page, you’ll find that even if you set the action attribute in the code explicitly, the ASP.NET runtime will revert it to the name of the current page.
NOTE ASP.NET supports a feature called Cross Page PostBacks that enables you to submit from one page to another. To learn more about this concept, search the MSDN site for Cross Page PostBacks or get yourself a copy of Professional ASP.NET 4 from Wrox.
The next thing to look at is the hidden __VIEWSTATE field that you see in the HTML source bolded in the following snippet: ...
Although at first the text appears to contain nothing more than random characters, it actually contains useful information. To protect the information stored in this field, and to decrease the size of it, ASP.NET has converted the page state in the preceding string. If you were able to look inside the value of the fields, you’d find a value for the Label1 control with the current date and time. When your ASP.NET page loads, the ASP.NET runtime fills this hidden field with information about the page. For example, it added the value for the Text property of the Label1 control when you caused a postback by clicking the Set Date button. Similarly, it contains the selected date for the Calendar. When the page is then submitted back by a postback, the value in this hidden __VIEWSTATE field is sent with the request. Then, when ASP.NET creates the new page on the server, the information from the __VIEWSTATE field is read and applied to the controls in the page. This way, a control like the Label is able to maintain its text even after the page has been posted back to the server. To reiterate, here’s a rundown of the process that took place in the preceding Try It Out.
1 . . 2 . 3 4.
You requested the page in the browser by opening it from within VWD. The browser got the page from the server by making a request for it. The ASP.NET runtime read the page file from disk, processed it, and sent the resulting HTML to the browser. At this stage, all the controls were set to their default values that were defined in the markup of the page. For example, the Text of the Label control is set to Label. After the page got displayed in the browser, you clicked the Set Date button. This caused a postback to the server.
The ASP.NET State Engine ❘ 133
5.
At the server, the page was reconstructed again, similar to the first time it loaded, described in step 3. At this stage, all the controls contain their default values. So, again, the Label1 control had its Text property set to Label. Shortly after the defaults have been set, the runtime overrides these defaults for controls it finds in View State. However, because this was the first postback and the Label control’s Text property hadn’t changed yet, its value was not present in View State. So the Text property just contained the default word Label.
6.
While still processing the same request, the ASP.NET runtime fired the code in SetDate_Click. As you have seen, this code changed the Text property of the Label control to the current date and time. The ASP.NET runtime sees this change and stores this new value in View State as well, so it stays available for subsequent postbacks.
. 7
Next, you clicked the Plain PostBack button. Just as with the other button, this caused a postback to occur. The page was constructed again, and all defaults are set. Again, this means that the Text property of Label1 control simply contains the word Label. However, shortly after that, during the same processing cycle, the ASP.NET runtime processes the View State, restoring control values it finds in the hidden __VIEWSTATE field. In this example, it found the Text property with the current date and time, and assigned it again to the Label control. Because the Plain PostBack button doesn’t change the Text of the Label anymore, the Text property doesn’t change: it contains the date and time from the previous postback. At the end, the entire page is sent to the browser, where the label correctly displays its previous value.
8.
Finally, you turned off the View State for the Label control by setting EnableViewState to False. With this setting turned off, the ASP.NET runtime doesn’t track the Label control anymore. So when you click the Plain PostBack button, the ASP.NET runtime doesn’t find any information for the label in View State, which eventually results in the label displaying its own default text: the word Label.
Not All Controls Rely on View State You have to understand that not all controls rely on View State all the time. A number of controls are able to maintain some of their own state. These controls include, among others, the TextBox, CheckBox, RadioButton, and DropDownList controls. They are able to maintain their values because they are rendered as standard HTML form controls in the browser. For example, a TextBox server control looks like this in the browser:
When a page with such a TextBox in it is posted back, the browser also sends the value of the control back to the server. The ASP.NET runtime can then simply look at that value to prepopulate the text box again, instead of getting the value from View State. Obviously, this is more efficient than storing the value in View State too. If that were the case, the value would get sent to the server twice: once in the text box and once in View State. Especially with large values, this could quickly increase the page’s size, and thus its load time. For some features — such as tracking changes made at the client — these controls still need to store their values in View State as well, and will do so on a need-to-have basis.
134 ❘ Chapter 4 Working with ASP.NET Server Controls
A Note about View State and Performance Because the View State engine adds a considerable amount of information to the page, it’s often a good idea to turn it off when you don’t need it. This way, you can minimize the size of the hidden __VIEWSTATE field, which means the page becomes smaller and thus loads faster in the browser. Turning off View State is easy, and can be done at three different places: ➤➤
At the web site level. You can do this in the web.config file in the root of the site by modifying the <pages> element under <system.web>, setting the enableViewState attribute to false: <pages enableViewState=”false”> ...
Turning off View State at the site level is problematic, as you can’t turn it on later for specific controls. Fortunately, ASP.NET 4 offers a new ViewStateMode property that gives you more control over how View State is used. ➤➤
At the page level. At the top of each page you find the page directive, a series of instructions that tell the ASP.NET runtime how the page should behave. In the page directive you can set EnableViewState to False:
This option is useful for pages where you’re sure you don’t need View State at all. ➤➤
At the control level. Each ASP.NET Server Control enables you to set EnableViewState individually, giving you the option to turn it off for some controls, while leaving it on for others.
Once you’ve turned off View State at a higher level (web.config or page level) you can’t turn it on again at a lower level (the page or a specific control). However, using the new ViewStateMode property you can still accomplish this as follows: ➤➤
Do not turn off View State in the web.config file.
➤➤
At the page level, set EnableViewState to True and ViewStateMode to Disabled like this:
This turns off View State for all controls in the page except for those that explicitly enable it again by setting the ViewStateMode to Enabled. ➤➤
For the controls you want to give View State support, set the ViewStateMode to Enabled, like this:
Practical Tips on Working with Controls ❘ 135
If you want to see this at work in your demo page, modify the page directive of State.aspx as in the previous example by setting EnableViewState to True and ViewStateMode to Disabled. Then create a second Label in the page and set ViewStateMode for the first to Enabled:
In the code behind of the page, assign today’s date and time to the second label as well: VB.NET
Finally, run steps 9 and 10 of the last Try It Out exercise. You’ll notice the first Label maintains its text, whereas the second defaults back to the text Label.
Practical Tips on Working with Controls The following list presents some practical tips on working with controls: ➤➤
Spend some time trying out the different controls in the Standard category. Although many of them are used and discussed throughout the book, it’s good to know how you should use them and how they operate. By experimenting with them now in a few sample pages, you have a head start when the controls reappear in later chapters.
➤➤
Consider turning off View State for controls that don’t need it. In many cases, you hardly notice the difference, but especially with the data-driven controls discussed in Chapter 13 and onward, disabling View State can seriously decrease the size of your web page, resulting in shorter load times and improved user experience.
➤➤
Before you design a complex Web Form with multiple controls to accept user input, step back from your computer and take a piece of paper and a pen to draw out the required functionality. By thinking about the (technical) design of your application before you start coding, it’s much easier to create a consistent and well-thought-out user interface. Making considerable changes later in the page if you’ve taken a wrong route will always take more time than doing it (almost) right the first time.
➤➤
Experiment with the View State mechanism to get a better understanding of how it works. Create a couple of pages similar to the one you created in the last exercise. Then turn off View State at the page or control level and see how the page behaves. Take note of the controls, such as TextBox, that are capable of maintaining their value even with View State off.
136 ❘ Chapter 4 Working with ASP.NET Server Controls
Summary This chapter gave you a good look at the large set of ASP.NET Server Controls. Because these controls are so important and used throughout every ASP.NET application, it’s really critical that you understand what controls you have available in the Toolbox, what they are used for, how they work and how they maintain their own state. One of the biggest inventions in ASP.NET is the state engine that enables controls to maintain their state across postbacks. The state engine is a real time saver and frees you from writing loads of tedious and boring code in every single web page to replicate this behavior. However, you should turn View State off when possible because enabling it means a performance hit. This chapter also introduced you to some trivial server-side code in Visual Basic and in C#. The next chapter gives you a much better understanding of programming ASP.NET pages. You see how a programming language looks, what elements it contains, and how to write code yourself to use in your ASP.NET pages. And best of all, the examples are presented in Visual Basic and C#, so you’re not stuck with a language you may not like.
Exercises
1.
Name the mechanism that enables server controls to maintain their state.
2.
How is the ASP.NET runtime able to keep track of control state between postbacks?
3.
Name a difference between an and an .
4.
What property do you need to cause a postback to the server when you change the checked state of a CheckBox in the browser?
5.
Many server controls have a common set of properties that affects their looks at runtime. Name three properties that change styling elements such as color, borders, and size.
6.
Instead of setting individual control properties like BackColor and ForeColor, it’s better to set a single CSS-related property. What’s the name of this property and what benefit does it give you? Answers to Exercises can be found in Appendix A.
Summary ❘ 137
⊲⊲ What You Learned in this Chapter __VIEWSTATE
The hidden form field that is used to transfer the state from the server to the client and back
Container controls
Server controls that serve as a container by wrapping other content and controls
Events tab
The part of the Properties Grid that lets you set up handlers for control events such as Click for a Button
List controls
Server controls that present a list of items to the user. Controls include the DropDownList, CheckBoxList and more
Post and Get methods
Different methods to submit data from the client to the server. With Post the data is added to the body of the request whereas with Get the data is appended to the address of the requested page
Postback
The process of sending form data from a client browser back to the server
Server Controls
The work horses of ASP.NET, used to build up the User Interface of a web page in the browser
Smart Tasks panel
The action panel that appears for some controls to help you accomplish common tasks
View State
The mechanism that enables the ASP.NET controls to store state at the client
5
Programming Your ASP.NET Web Pages What You Will Learn in This Chapter: ➤➤
How to work with data types, variables, objects, and collections in a programming environment
➤➤
Different ways to make decisions in your code
➤➤
The options available for creating blocks of functionality that can easily be reused
➤➤
Different ways to write well-organized and documented code
➤➤
What object orientation is, and how you can use it in your applications
In the previous four chapters, you created a number of Web Forms that contained mostly ASP.NET Server Controls and plain HTML. Only a few of the examples contained actual programming code, written in either C# or Visual Basic (VB.NET), and most of that code was pretty straightforward. However, not all of your pages will always be so simple. Although the many smart Server Controls you have at your disposal minimize the amount of code you need to write compared to the older 1.x family of the .NET Framework or other web technologies like classic ASP or PHP, the ability to read, understand, and write code is a critical asset in your web development toolkit. This chapter teaches you the basics and beyond of programming for web applications. Just as with all the other samples in the book, this entire chapter covers both VB.NET and C#. For every concept or piece of theory introduced in this chapter, you see an example in both VB.NET and C# at the same time. Which language you prefer is entirely your decision.
140 ❘ Chapter 5 Programming Your ASP.NET Web Pages
NOTE To get the most out of this chapter, it’s recommended to actually try out the code. Most of the examples can be tested with a simple ASPX page. Drag a Label and a Button on your page and double-click the Button in Design View. Then type the sample code on the open line of the code block that VWD added for you and press Ctrl+F5. After the page has finished loading, click the button and the code will be executed. Some of the examples call fictitious code and won’t run correctly. They only serve to illustrate the topic being discussed.
Introduction to Programming To get started with programming, it’s critical to understand a common set of terms shared by programmers in all types of languages and applications. The remainder of this chapter introduces you to a relatively large number of terms and concepts. Most of the terminology comes with code examples so you can see how they are used in real code. It’s also important to realize this is not a complete introduction to programming. Not every detail of a programming language is covered. Instead, this chapter focuses on the key concepts that you need to understand to successfully build day-to-day web sites. Once you get the hang of that you’ll find it’s easier to deepen your knowledge about programming by learning the more exotic features of your favorite programming language.
NOTE If you’re interested in learning a lot more about programming in VB.NET or C#, find Beginning Microsoft Visual Basic 2010 (ISBN: 978-0-470-50222-8) or Beginning Microsoft Visual C# 2010 (ISBN: 978-0-470-50226-6), both published by Wrox.
The code you are going to write in this and coming chapters is either added to the Code Behind of a web page, or in a separate class file placed in the special App_Code folder. When the ASP.NET runtime processes the request for a page containing code, it compiles any code it finds in the page, Code Behind, or class file first. When code is compiled, it is being transformed from a human-readable programming language (like C# or VB.NET) into Intermediate Language (IL), the language that the .NET Framework runtime can understand and execute. The result of the compilation process of an ASP.NET web site is one or more files with a DLL extension in a temporary folder on your system. This compilation process only takes place the first time the page is requested after it has been changed. Subsequent requests to the same page result in the same DLL being reused for the request. Fortunately, in ASP.NET web sites, compilation takes place behind the scenes, so you usually don’t have to worry about it. To get started with programming, the first concepts that you need to look at are data types and variables, because they are the building blocks of any programming language.
Data Types and Variables ❘ 141
NOTE The .NET Framework used by ASP.NET is huge and contains thousands of types with hundreds of thousands of methods, properties and so on. Clearly, you cannot memorize all the types in the framework, so you need to make good use of resources like IntelliSense and the on-line help. Navigating the MSDN site ( http://msdn.microsoft.com/en-us/library/) can sometimes be a daunting task. However, I often find that searching for something like typeName type .NET MSDN brings up exactly what I need. So, if I wanted to learn more about the string class, I’d type string class .NET MSDN in my favorite search engine. Nine out of ten times the first result is a link to the relevant page on the MSDN web site, where I can learn more about the class, where it’s defined and located and how to use it.
Data Types and Variables At first when you think about data that is used in some programming environment, you may not realize that each piece of data has a data type. You may think that a computer would store the text Hello World in exactly the same way as today’s date or the number 26, as a series of characters, for example. However, to be able to effectively work with data, many programming languages have different data types, where each data type is constrained to a specific type of information. Out of the box, the .NET Framework comes with a long list of data types that enables you to work with numbers (like Integer, Short, and Double), text strings (Char and String), dates (DateTime), true/ false constructs (the Boolean), and more. A list with the most common types is supplied later in this section. For each major type of data there is a special data type. To work with that data, you can store it in a variable that you need to declare first using the required data type. In VB.NET you use Dim myVariable As DataType whereas in C# you use DataType myVariable to declare a variable. The following example shows you how to declare two variables: an Integer (int in C#) to hold a number and a String (string in C#) to hold a piece of text: VB.NET
‘ Declare a variable of type Integer to hold medium sized whole numbers. Dim distanceInMiles As Integer ‘ Declare a variable to hold some text like a first name. Dim firstName As String C#
// Declare a variable of type int to hold medium sized whole numbers. int distanceInMiles; // Declare a variable to hold some text like a first name. string firstName;
142 ❘ Chapter 5 Programming Your ASP.NET Web Pages
These two code examples also contain comments, prefixed with a tick (‘) in VB.NET and two forwards slashes (//) in C#. You learn more about commenting your code later in this chapter. After you have declared a variable, you can assign it a value. You can assign types like numbers and Booleans directly to a variable. To assign a String to a variable you need to enclose it in double quotes: VB.NET
Dim distanceInMiles As Integer distanceInMiles = 437 Dim firstName As String firstName = “Imar” C#
int distanceInMiles; distanceInMiles = 437; string firstName; firstName = “Imar”;
In addition to separate declarations and assignments, you can also declare a variable and assign it a value in one fell swoop: VB.NET
Dim distanceInMiles As Integer = 437 C#
int distanceInMiles = 437;
Although a variable name can be nearly anything you like, it’s advised to give each variable a meaningful name that describes its purpose. For example, a string to hold a first name would be called firstName and a variable that holds someone’s age would simply be called age. To help you find the type of the variable Figure 5-1 later in the code, VWD and all other products in Visual Studio show a useful tooltip when you hover over a variable in the code editor, making it super easy to find a variable’s type. Figure 5-1 shows that the distanceInMiles variable in the C# example is of type int. You’re advised not to prefix your variables with a few letters to indicate the type. For example, write firstName and not sFirstName for a String holding someone’s name. This type of notation, called Hungarian Notation, is considered outdated. IDEs like VWD, with their smart IntelliSense and other programming tools, don’t really require this anymore. Without Hungarian Notation, your code becomes easier to read (age is more readable than iAge) and easier to maintain because you can change a variable’s type without renaming it everywhere it’s used.
Data Types and Variables ❘ 143
Microsoft .NET (and thus the ASP.NET environment) supports a large number of different programming languages, including VB.NET, C#, and others. All these languages are able to communicate with each other. For example, you can write some code in C#, use Visual C# Express Edition to compile it to a .dll file (a file with reusable code that can be consumed by other .NET applications), and then use it in a web application that uses VB.NET as the primary language. Because of this interoperability, it’s necessary to agree on some system that enables all .NET programming languages to understand each other. This system is called the Common Type System (CTS). It’s the CTS that defines the data types that are accessible to all CTS-compliant languages. Each language is then free to define a set of primitive types, which are essentially shortcuts or aliases for the more complex type descriptions in the .NET Framework. So, even if the CTS defines a type called System.Int32, a language like C# is free to alias this type as int to make it easier for a developer to work with it. The following table lists the most common CTS types in the .NET Framework and their C# and VB.NET aliases. The table also lists the ranges of the variables and what they are used for. .NET
C#
VB.NET
Description
System.Byte
byte
Byte
Used to store small, positive whole numbers from 0 to 255. Defaults to 0 when no value is assigned explicitly.
System.Int16
short
Short
Capable of storing whole numbers between –32,768 and 32,767. Defaults to 0.
System.Int32
int
Integer
Capable of storing whole numbers between –2,147,483,648 and 2,147,483,647. Defaults to 0.
System.Int64
long
Long
Holds whole large numbers between –9,223,372,036,854,775,808 and 9,223,372,036,854,775,807. Defaults to 0.
System.Single
float
Single
Stores large numbers with decimals between –3.4028235E+38 and 3.4028235E+38. Defaults to 0.0.
System.Double
double
Double
Can hold large fractional numbers. It’s not as accurate as the Decimal when it comes to the fractional numbers but when extreme accuracy is not a requirement, you should prefer the Double over the Decimal, because the Double is a little faster. Defaults to 0.0.
System.Decimal
decimal
Decimal
Stores extremely large fractional numbers with a high accuracy. Defaults to 0. This data type is often used to store monetary values.
System.Boolean
bool
Boolean
Used to hold a simple boolean value: either True (in VB), true (in C#), False (in VB), or false (in C#). Defaults to False.
System. DateTime
n/a
Date
VB.NET has an alias for the System.DateTime data type to store date and time values. C# doesn’t define an alias for this type. Defaults to 1/1/0001: 12:00 am. continues
144 ❘ Chapter 5 Programming Your ASP.NET Web Pages
(continued) .NET
C#
VB.NET
Description
System.Char
char
Char
Holds a single character. Defaults to Nothing (null in C#).
System.String
string
String
Can hold text with a length of up to 2 billion characters. Defaults to Nothing (null in C#).
System.SByte
sbyte
SByte
Used to store small numbers from –128 to 127. Defaults to 0.
System.UInt16
ushort
UShort
Similar to a System.Int16, but this data type can only store unsigned whole numbers, between 0 and 65,535. Defaults to 0. The other data types prefixed with a U are all unsigned as well.
System.UInt32
uint
UInteger
Capable of storing whole numbers between 0 and 4,294,967,295. Defaults to 0.
System.UInt64
ulong
ULong
Capable of storing whole numbers between 0 and 18,446,744,073,709,551,615. Defaults to 0.
System.Object
object
Object
The parent of all data types in .NET, including the CTS types and types you define yourself. Each data type is also an object, as you learn later in the book. Defaults to Nothing (null in C#).
The standard .NET types are all prefixed with System followed by a period. This System part is the namespace for this data type. You learn what namespaces are and what they are used for later in this chapter. Sometimes you need to convert data from one type to another. For example, you may have an Integer that you need to treat as a Double. You can do this in a number of different ways.
Converting and Casting Data Types The most common way to convert a type is converting it into a String. Web applications use string types in many places. For example, the Text returned from a TextBox is a String, and so is the SelectedValue of a DropDownList. To convert any Object to a String, you can simply call its ToString() method. Every object in the .NET world supports this method, although the exact behavior may differ from object to object. For now, it’s important to understand that ToString is a method — or an operation — on an object, like a String or a Double and even the parent Object itself. You learn more about methods and objects later in this chapter when object-oriented programming is discussed. Using ToString() is easy, as the following example that outputs today’s date and time on a Label control demonstrates: VB.NET
Label1.Text = System.DateTime.Now.ToString()
Data Types and Variables ❘ 145
C#
Label1.Text = System.DateTime.Now.ToString();
Another way to convert data types is by using the Convert class.
NOTE Classes are an important concept in .NET so they are discussed in their own section later in this chapter. For now it’s important to understand that a class is like a blueprint for objects that are used in .NET. You can create your own classes, but you will also use many of the standard classes that are part of the .NET Framework.
The Convert class contains functionality to convert a number of data types into another type. The following is a simple example of converting a String containing a value that looks like a Boolean into a true Boolean type: VB.NET
Dim myBoolean1 As Boolean = Convert.ToBoolean(“True”) Dim myBoolean2 As Boolean = Convert.ToBoolean(“False”)
Besides the ToBoolean method, Convert offers you a host of other conversion methods including ToInt32 (for numbers), ToDateTime (for dates), and ToString. Another way to convert one type into another is by using casting. With casting you actually force one type into another, which is different from converting, in which the underlying value of a data type is transformed into a new value. Casting only works for compatible types. You can’t, for example, cast a DateTime into an Integer. You can, however, cast similar types, like a Double to an Integer or a String to an Object. The reverse of the latter example isn’t always true. Earlier I said that every data type in the .NET Framework is based on the Object data type, meaning that, for example, a String is an Object. However, not every Object is also a String. When you try to cast one type into another and get a compilation error, keep this in mind. Later chapters in this book show you more examples of how to cast compatible types into each other. To cast one type into another using VB.NET, you have a few options. First, you can use CType and DirectCast. CType is a bit more flexible in that it allows you to cast between two objects that look similar. DirectCast, on the other hand, only allows you to cast between compatible types but performs slightly faster. The following VB.NET example shows how this works: Dim o1 As Object = 1 Dim i1 As Integer = DirectCast(o1, Integer) Dim i2 As Integer = CType(o1, Integer) Dim o2 As Double = 1
‘ Works, because o1 is an Integer ‘ Works, because o1 is an Integer
146 ❘ Chapter 5 Programming Your ASP.NET Web Pages
Dim i3 As Integer = DirectCast(o2, Integer) Dim i4 As Integer = CType(o2, Integer)
‘ ‘ ‘ ‘
Fails, because o2 is not an Integer Works, because o2 looks like an Integer
In the first part of the example, an object called o1 is declared and assigned the Integer value of 1. Although o1 exposes itself to the outside world as an Object, its underlying value is still an Integer. When DirectCast is called, the cast succeeds because o1 is, under the hood, an Integer. In the second example, o2 is declared as a Double, a numeric type that looks somewhat like an Integer, but isn’t really one. Therefore, the call to DirectCast fails because a Double cannot be cast to an Integer. CType, on the other hand, works fine, because the underlying value of the variable o2 looks like an Integer and can therefore be cast to one. The third option to cast in VB.NET is using the keyword TryCast, which is somewhat similar to the other two options. When an object cannot be cast correctly, TryCast returns Nothing, whereas DirectCast and CType result in a crash of the code. In C# you have two options to cast objects. The most common way is to put the data type in parentheses in front of the expression you want to cast: object o1 = 1; int i1 = (int)o1;
// Works
double o2 = 1; int i2 = (int)o2;
// Works
Alternatively, you can use the as keyword, which works similar to TryCast in VB.NET in that the code doesn’t crash if the cast doesn’t succeed. The following sample code shows that you cannot cast an Integer to an ArrayList (which you meet later in this chapter). Instead of crashing, the variable myList simply contains null to indicate that the cast operation didn’t succeed. object o1 = 1; ArrayList myList = o1 as ArrayList;
// Doesn’t cast, but doesn’t crash either.
You see more about casting and converting in the remaining chapters in this book.
Using Arrays and Collections So far the data types you have seen are relatively straightforward and singular objects. For example, you store a value of True or False in a Boolean type, and you store a number like 123 in an Integer. But what if you have the need to store lots of integers? You may have the need to do so if you want to store the points of a complex shape like a polygon. Or you may have the need to store all the roles that your application supports in a single variable so you can show them on a web page in the Management section, for example. Here’s where arrays and collections come to the rescue.
Defining and Working with Arrays You can see an array as a big bag or list of the same type of things. You define the data type of the things in the array when you declare it. Each item in the array is identified by a sequential number
Data Types and Variables ❘ 147
(its so-called index) starting at 0, making arrays zero-based. When declaring and accessing an array in VB.NET you use parentheses, whereas in C# you use square brackets. After you have defined the array and populated its elements, you can access the elements by their zero-based element index (0, 1, 2, and so on). The following code snippet defines an array called roles that can hold up to two roles at the same time: VB.NET
Dim roles(1) As String C#
string[] roles = new string[2];
See the difference between the VB.NET and C# examples? That’s not a typo. In VB.NET you define an array’s size by specifying the upper bound. The upper bound is the last element in the array that you can access. Because arrays are zero-based (that is, you address the first item in the array with an index of 0) it means that if you need room for two items, the upper bound is 1, giving you the items 0 and 1. In C#, on the other hand, you don’t define the upper bound but instead you define the size. So in C#, you simply specify 2 to get an array with two elements. Additionally, C# requires you to use the keyword new, which instantiates a new array for you. VB.NET does that for you automatically and raises an error if you add the New keyword as in the C# example. You see the new (New in VB.NET) keyword again later in this chapter. To enter the role names into the array you use the following syntax: VB.NET
Just as with the array’s declaration, you use parentheses in VB.NET and square brackets in C# to address the elements in the array. Note that roles[0] refers to the first element in the array and roles[1] refers to the second. By design, arrays have a fixed size. So, given the previous example that defines an array with room for two elements, the following code will throw an error: VB.NET
roles(2) = “Members”
‘ Throws an error
C#
roles[2] = “Members”;
// Throws an error
148 ❘ Chapter 5 Programming Your ASP.NET Web Pages
This code tries to squeeze a third role into an array that has room for only two. Obviously, that doesn’t fit and you’ll get an error stating that the “Index was outside the bounds of the array.” But what if you need to create more room in the array at a later stage in your code at runtime? In VB.NET this is pretty easy. You can use the ReDim statement: ReDim Preserve roles(2) roles(2) = “Members”
‘ Works fine now
This line of code redimensions the array to its new size: an upper bound of two, thus creating room for a third element. The Preserve keyword is necessary to leave the current items in the array intact. Without it, the resized array will be empty. In C#, you need to do a little more work. In that language, you need to create a new array of the desired size, and then copy over the elements from the old array into the new one. Then you can point your old variable to the new one and add the element: string[] tempRoles = new string[3]; Array.Copy(roles, tempRoles, roles.Length); roles = tempRoles; roles[2] = “Members”;
// // // // //
Create new array with required size Use Copy to copy the elements from the old to the new array Assign the new array to the old variable
// Works fine now
Instead of creating a new array and copying the elements, you can also use Resize with a concept called generics, which you learn about later in this chapter. For the brave of heart, here’s some code that uses Resize and has the same effect as the previous code snippet: Array.Resize<string>(ref roles, 3);
// Resize the array so it can // hold three elements
roles[2] = “Members”;
// Works fine now
Don’t worry about this odd-looking generics syntax right now; you probably won’t need it very often, because the .NET Framework offers alternatives to fixed size arrays. When you start working with arrays, you find that they are quick to use at runtime, but lack some useful functionality. For example, it’s not so easy to add new elements or to remove existing items from the array. Fortunately, the .NET Framework offers a range of useful collections that do give you the feature set you need.
Defining and Working with Collections Collections are similar to arrays in that they enable you to store more than one object in a single variable. The same bag analogy works for collections: you can simply drop a number of items in a bag, and it will hold them for you. What’s different with collections is how they enable you to work with the data in the bag. Instead of simply accessing each item by its index, most collections expose an Add method that enables you to add an item to the collection. Similarly, they have Remove and Clear methods to remove one or all items from the collection. Just like arrays, they enable you to iterate, or loop, over them to access the items in the collection.
Data Types and Variables ❘ 149
When collections were first introduced in the .NET Framework 1.0, the ArrayList and Hashtable became popular very quickly because they were so easy to use. The ArrayList enables you to add arbitrary objects that are then stored in the order you add them, whereas the Hashtable enables you to store objects referenced by a custom key. The main benefit of these collections over their array cousins is that they can grow on demand. Unlike the previous example where you needed to resize the array to create room for the third role, the ArrayList grows dynamically when required. The following example shows you how this works: VB.NET
Dim roles As New ArrayList()
‘ Create a new ArrayList. You don’t need ‘ to set its size explicitly
Add the first role Add the second role Keep adding roles and the ArrayList grows as necessary
Because this code now calls a method (Add) rather than assigning an item to a predefined index in an array, you need parentheses (()) in both VB.NET and C#. The usage of methods is discussed later in this chapter. Although collections solve some of the problems that arrays have, they introduce a few problems of their own. The biggest drawback of the ArrayList is that it isn’t strongly typed. What this means is that you can add any object to the list using the Add method. This means that the ArrayList could hold objects that are of different types at the same time. This may not seem to be a big deal at first, but as soon as you start working with an ArrayList that contains multiple types of objects, you’ll quickly see why this is problematic. Take the roles example again. With the array and the ArrayList versions, the code simply added a few strings containing role names. You can then use these three strings to, say, build up a drop-down list in a Web Form to enable a user to pick a role. So far so good. But what if one of the items in the list is not a string? What if another developer accidentally wrote some code that adds a DropDownList control to the ArrayList? Because the ArrayList accepts all objects, it won’t complain. However, your code will crash if it expects a String, but gets a DropDownList control instead. With .NET 2.0, Microsoft introduced a concept called generics. Generics are still strongly present in version 4 of .NET, helping you overcome the problems that weakly typed collections like the ArrayList introduced.
150 ❘ Chapter 5 Programming Your ASP.NET Web Pages
An Introduction to Generics Since their introduction with .NET 2.0, generics pop up in many different locations in the .NET Framework. Although they are used often in situations where collections are used, the use of generics is not limited to collections; you can also use them for singular type of objects. Generics are to code what Microsoft Word templates are to word processing. They enable you to write a code template that can be used in different scenarios with different types. With generics, you can define a generic code template that doesn’t explicitly specify a type. Only when that code is used do you define the type. The main benefit of this is that you can reuse the same template over and over again for multiple data types, without retyping and maintaining multiple versions of the code. Besides using generics in your own code definitions, you find a host of generics-enabled objects and collections in the .NET Framework ready to be used by your code. To understand how you can take advantage of generics, take a look at the following example. It’s essentially the same code you saw earlier where the ArrayList was used, but this time the type of the list is constrained so it accepts strings only: VB.NET
Dim roles As New List(Of String) roles.Add(“Administrators”) roles.Add(“ContentManagers”) roles.Add(“Members”) C#
List<string> roles = new List<string>(); roles.Add(“Administrators”); roles.Add(“ContentManagers”); roles.Add(“Members”);
Not much code has changed to make the roles list type safe. However, with the definition of List (Of String) in VB.NET and List<string> the new list is now set up to only allow strings to be added through its Add method. This compiles fine: roles.Add(“Administrators”);
The following will fail because 33 is not a String: roles.Add(33);
Similar to a generics list of strings, you can also create lists to hold other types. For example: VB.NET
Dim intList As New List(Of Integer) Dim boolList As New List(Of Boolean) Dim buttonList As New List (Of Button)
‘ Can hold Integers only ‘ Can hold Booleans only ‘ Can hold Button controls only
C#
List intList = new List(); List boolList = new List(); List<Button> buttonList = new List<Button>();
// Can hold ints only // Can hold bools only // Can hold Button controls only
Statements ❘ 151
NOTE Because there’s a lot more to generics than what is shown here, they deserve an entire book on their own. Wrox has released such a book: Professional .NET 2.0 Generics by Tod Golding (ISBN: 978-0-7645-5988-4). Although it was originally written for .NET 2.0, you’ll find that all the concepts and examples introduced in that book still apply.
The generics examples you have seen barely scratch the surface of what is possible with generics. However, when building ASP.NET web sites, you often don’t need all the advanced stuff that generics offer you. The List collection is so useful it had to be discussed here. Without a doubt, you’ll use that collection in your own code one way or another. Though the Add method is useful to add items to a collection, it can sometimes be a bit tedious if you need to add multiple items to a collection at once. To make this easier, .NET supports collection initializers. With a collection initializer, you declare the collection, and add some items in one step. You do this by adding the items in a pair of curly braces (prefixed with the keyword From in VB.NET) as shown in the following example: VB.NET
Dim myList As New List(Of Integer) From {1, 2, 3, 4, 5} C#
List myList = new List() { 1, 2, 3, 4, 5 };
Right after this line, the list is populated with the five integers. Collection initializers are not limited to the List class or integers. You can use them with other collection types and data types as well.
Statements To make a program or a web site do something useful, you need to provide it with code statements that it can execute. Statements cover a wide range of actions, such as show this button, send this e‑mail, execute this and that code when a user clicks that button, and so on. However, simply executing these actions is not enough. You often need to execute some code only when a certain condition is true. For example, if a visitor to an e-commerce web site is buying more than $100 of merchandise at one time, she might get a discount of 10 percent. Otherwise, she’ll pay the full price. Conditions or decisions are therefore very important statements in a programming language. Another important set of statements is the loops. Loops enable you to repeat a certain piece of code a number of times. For example, you can have a loop that goes from 1 to 10, performing some action on each iteration. Or you can loop through the products in a shopping cart, summing up their total price, for example. The final important set of statements is the operators. Operators enable you to do something with your values; or, to be more exact, they enable you to operate on them. For example, you use operators to add or subtract values, concatenate (combine) them, or compare them to each other. The following three sections dig deeper into operators, decision making, and loops.
152 ❘ Chapter 5 Programming Your ASP.NET Web Pages
Operators The most important operators can be grouped logically into five different types. Of these five groups, the assignment operators are probably the easiest to understand and use.
Assignment Operators The assignment operators are used to assign a value to a variable. This value can come from many sources: a constant value, like the number 6, the value of another variable, and the result of an expression or a function, which are discussed later. In its simplest form, an assignment looks like this: VB.NET
Dim age As Integer = 38 C#
int age = 38;
What if the person this age variable is referring to just had his birthday? You’d need to add 1 to the age value. That’s where arithmetic operators come into play.
Arithmetic Operators Arithmetic operators enable you to perform most of the familiar calculations on variables and values, like adding, subtracting, and dividing. The following table lists the common arithmetic operators for both VB.NET and C#. VB.NET
C#
+
+
Usage
Adds two values to each other. These values can be numeric types like Int32, but also String, in which case they are concatenated.
-
-
Subtracts one value from another.
*
*
Multiplies two values.
/
/
Divides two values.
\
n/a
Divides two values but always returns a rounded integer.
^
n/a
Raises one value to the power of another.
Mod
%
Divides two whole numbers and returns the remainder.
The first five operators probably look familiar, and their usage is pretty straightforward. The following code snippet shows the basic operations you can perform with these operators: VB.NET
Dim firstNumber As Integer = 100 Dim secondNumber As Single = 23.5 Dim result As Double = 0 result = firstNumber + secondNumber result = firstNumber - secondNumber
‘ Results in 123.5 ‘ Results in 76.5
Statements ❘ 153
result = firstNumber * secondNumber result = firstNumber / secondNumber result = firstNumber \ secondNumber
‘ Results in 2350 ‘ Results in 4.25531914893617 ‘ Results in 4
C#
int firstNumber = 100; float secondNumber = 23.5F; double result = 0; result result result result
VB.NET supports the \ operator, which basically performs the division and then drops the remainder from the value, effectively rounding the return value down to the nearest integer. C# doesn’t have a special operator for this. However, when you try to divide two integers, the result is always an integer as well. This means that 7 (stored as an int) divided by 2 (stored as an int) will be 3. It’s important to realize that this rounding occurs or you may end up with unexpected results. Note that in the C# example you need to add the letter F to the value of 23.5. This tells the compiler you really want it to be a float rather than a double. The final two operators need a bit more explanation. First, the ^ operator — for raising one number to the power of another — is only available in the VB.NET language: VB.NET
Dim result As Double result = 2 ^ 3 result = 3 ^ 2
‘ Results in 8 (2 * 2 * 2) ‘ Results in 9 (3 * 3)
C# doesn’t support this operator, but you can easily replicate its behavior using Math.Pow, which is made available by the .NET Framework. The following code snippet is functionally equivalent to the preceding one: C#
result = Math.Pow(2, 3); result = Math.Pow(3, 2);
// Results in 8 (2 * 2 * 2) // Results in 9 (3 * 3)
Of course Math.Pow is available to VB.NET as well, so if you’re using that language, you have two options to choose from. The final operator is called the mod or the modulus operator. It returns the remainder of the division of two numbers, like this: VB.NET
Dim firstNumber As Integer = 17 Dim secondNumber As Integer = 3 Dim result As Integer = firstNumber Mod secondNumber
‘ Results in 2
154 ❘ Chapter 5 Programming Your ASP.NET Web Pages
C#
int firstNumber = 17; int secondNumber = 3; int result = firstNumber % secondNumber;
// Results in 2
Simply put, the modulus operator tries to subtract the second number from the first as many times as possible and then returns the remainder. In the preceding example this will succeed five times, subtracting a total of fifteen, leaving a remainder of two, which is then returned and stored in the result. The modulus operator is often used to determine if a number is odd or even. When working with operators, it’s important to keep their precedence in mind. To see why this is important, consider the following calculation: 2 + 10 * 4
What is the outcome of this? You may think the answer is 48 if you first add 2 and 10 together, and then multiply the result by 4. However, the right answer is 42; first the multiplication operator is applied on 10 and 4, resulting in 40. Then 2 is added, which leads to 42 as the final result. The following table shows the operator precedence for both VB.NET and C#. VB.NET
C#
^
Exponentiation
*, /, %
Multiplication, division, and modulus
*, /
Multiplication and division
+, -
Addition and subtraction
\
Integer division
Mod
Modulus arithmetic
+, -
Addition and subtraction and string concatenation using the plus (+)
&
String concatenation
To force a different operator order, you can use parentheses around expressions. The contents of the expressions are evaluated first, resulting in a different order. For example: (2 + 10) * 4
This does result in 48 now, because the addition operator is applied before the multiplication operator. Another common set of operators is the comparison operators, which enable you to compare values.
Comparison Operators Just as with the arithmetic operators, VB.NET and C# each have their own set of comparison operators to compare one value to another. A comparison operator always compares two values
Statements ❘ 155
or expressions and then returns a Boolean value as the result. The following table lists the most common comparison operators. VB.NET
C#
Usage
=
==
Checks if two values are equal to each other.
!=
Checks if two values are not equal.
Checks if the first value is greater than the second.
=
Checks if the first value is greater than or equal to the second.
Is
is
In VB.NET: Compares two objects. In C#: Checks if a variable is of a certain type.
The first thing you’ll notice is that C# uses a double equals symbol (==) for the standard comparison operator. This clearly makes it different from the assignment operator. It’s a common mistake in C# to use only a single equals symbol if you intend to compare two values. Consider the following example: if (result = 4) { // Do something here with result }
The intention here is to see if result equals 4. However, because the assignment operator is used instead of a proper comparison operator, you’ll get the compile error that is displayed in Figure 5-2.
Figure 5-2
At first the error message may look a little strange. But if you look at the code a little closer, it starts to make more sense. First, result gets assigned a value of 4. This value is then used for the if statement. However, the if statement needs a Boolean value to determine whether it should run the code inside the if block. Because you can’t convert an integer value to a Boolean like this, you get a compile error. The fix is easy though; just use the proper comparison operator instead: if (result == 4) { // Do something here with result }
156 ❘ Chapter 5 Programming Your ASP.NET Web Pages
Similar to the simple comparison operator, you can use the other operators to compare values: VB.NET
4 > 5 4 5 5 >= 4
‘ 4 is not greater than 5; evaluates to False ‘ 4 is not equal to 5; evaluates to True ‘ 5 is greater than or equal to 4; evaluates to True
C#
4 > 5 4 != 5 5 >= 4
// 4 is not greater than 5; evaluates to false // 4 is not equal to 5; evaluates to true // 5 is greater than or equal to 4; evaluates to true
The Is keyword in VB.NET and is in C# do something completely different. In VB.NET, Is compares two instances of objects, something you learn more about in the second half of this chapter. In C#, you use is to find out if a certain variable is compatible with a certain type. You can accomplish that in VB.NET using the TypeOf operator. The following two examples are functionally equivalent: VB.NET
Dim myTextBox As TextBox = New TextBox() If TypeOf myTextBox Is TextBox Then ‘ Run some code when myTextBox is a TextBox End If C#
TextBox myTextBox = new TextBox(); if (myTextBox is TextBox) { // Run some code when myTextBox is a TextBox }
One of the arithmetic operators enables you to add two values to each other. That is, you use the plus (+) symbol to add two values together. But what if you want to combine two values, rather than add them up? That’s where the concatenation operators are used.
Concatenation Operators To concatenate two strings, you use the + in C# and the & character in VB.NET. Additionally, you can use += and &= to combine the concatenation and assignment operators. Consider this example: VB.NET
Dim firstString As String = “Hello “ Dim secondString As String = “World” Dim result As String ‘ The following three blocks are all functionally equivalent ‘ and result in the value “Hello World” result = firstString & secondString
Statements ❘ 157
result = firstString result = result & secondString result = firstString result &= secondString C#
string firstString = “Hello “; string secondString = “World”; string result; // The following three blocks are all functionally equivalent // and result in the value “Hello World” result = firstString + secondString; result = firstString; result = result + secondString; result = firstString; result += secondString;
In addition to the & and &= concatenation operators in VB.NET, you could use + and += as well. However, depending on the data types of the expressions you’re trying to concatenate, you may not get the result you’d expect. Take a look at this code snippet: Dim firstNumber As String = “4” Dim secondNumber As Integer = 5 Dim result As String = firstNumber + secondNumber
Because firstNumber is a String, you may expect the final result to be 45, a concatenation of 4 and 5. However, by default, the VB.NET compiler will silently convert the string “4” into the number 4, after which addition and not concatenation takes place, giving result a value of 9. To avoid this ambiguity, always use the & and &= operators to concatenate values. Additionally, you can tell VB.NET to stop converting these values for you automatically by adding the following line to the top of your code files: Option Strict On
This forces the compiler to generate errors when an implicit conversion is about to occur, as in the previous example. The final group of operators worth looking into is the logical operators, which are discussed in the next section.
Logical Operators The logical operators are used to combine the results of multiple individual expressions, and to make sure that multiple conditions are true or false, for example. The following table lists the most common logical operators.
158 ❘ Chapter 5 Programming Your ASP.NET Web Pages
VB.NET
C#
Usage
And
&
Returns True when both expressions result in a True value.
Or
|
Returns True if at least one expression results in a True value.
Not
!
Reverses the outcome of an expression.
AndAlso
&&
Enables you to short-circuit your logical And condition checks.
OrElse
||
Enables you to short-circuit your logical Or condition checks.
The And, Or, and Not operators (&, |, and ! in C#) are pretty straightforward in their usage, demonstrated in the following code snippets: VB.NET
Dim num1 As Integer = 3 Dim num2 As Integer = 7 If num1 = 3 And num2 = 7 Then
‘ Evaluates to True because both ‘ expressions are True
If num1 = 2 And num2 = 7 Then
‘ Evaluates to False because num1 is not 2
If num1 = 3 Or num2 = 11 Then
‘ Evaluates to True because num1 is 3
If Not num1 = 5 Then
‘ Evaluates to True because num1 is not 5
C#
int num1 = 3; int num2 = 7; if (num1 == 3 & num2 == 7)
// Evaluates to true because both // expressions are true
if (num1 == 2 & num2 == 7)
// Evaluates to false because num1 is not 2
if (num1 == 3 | num2 == 11)
// Evaluates to true because num1 is 3
if (!(num1 == 5))
// Evaluates to true because num1 is not 5
The AndAlso and OrElse operators in VB.NET and the && and || operators in C# work very similarly to their And and Or counterparts (& and | in C#). The difference is that with these operators the second expression is never evaluated when the first one already determines the outcome of the entire expression. So with a simple And operator: If num1 = 2 And num2 = 7 Then
both expressions are checked. This means that both num1 and num2 are asked for their value to see if they equal 2 and 7 respectively. However, because num1 does not equal 2, there really isn’t a point asking num2 for its value anymore as the result of that expression will never change the final outcome of the combined expressions. This is where the AndAlso (&& in C#) operator enables you to short-circuit your logic:
Statements ❘ 159
VB.NET
If num1 = 2 AndAlso num2 = 7 Then C#
if (num1 == 2 && num2 == 7)
With this code, the expression num2 = 7 (num2 == 7 in C#) is never evaluated because num1 already didn’t meet the required criteria. This may not seem like a big deal with these simple expressions, but it can be a real performance booster if one of the expressions is actually a slow and long-running operation. Consider this fictitious code: VB.NET
If userName = “Administrator” And GetNumberOfRecordsFromDatabase() > 0 Then C#
if (userName == “Administrator” & GetNumberOfRecordsFromDatabase() > 0)
The code for this If block executes only when the current user is called Administrator and the fictitious call to the database returns at least one record. Now, imagine that GetNumberOfRecordsFromDatabase() is a long-running operation. It would be a waste of time to execute it if the current user weren’t Administrator. AndAlso (&& in C#) can fix this problem: VB.NET
If userName = “Administrator” AndAlso GetNumberOfRecordsFromDatabase() > 0 Then C#
if (userName == “Administrator” && GetNumberOfRecordsFromDatabase() > 0)
Now, GetNumberOfRecordsFromDatabase() will only be executed when the current user is Administrator. The code will be ignored for all other users, resulting in increased performance for them. Most of the previous examples used an If statement to demonstrate the logical operators. The If statement itself is a very important language construct as well. The If statement and other ways to make decisions in your code are discussed next.
Making Decisions Making decisions in an application is one of the most common things you do as a developer. For example, you need to hide a button on a Web Form when a user is not an administrator. Or you need to display the even rows in a table with a light grey background while the odd rows get a white background. All these decisions can be made with a few different logic constructs: If, If Else, ElseIf, and switch or Select Case statements.
If, If Else, and ElseIf Constructs The If statement is the simplest of all decision-making statements. The If statement contains two relevant parts: the condition being tested and the code that is executed when the condition evaluates to True. For example, to make a button visible only to administrators you can use code like this:
160 ❘ Chapter 5 Programming Your ASP.NET Web Pages
VB.NET
If User.IsInRole(“Administrators”) Then DeleteButton.Visible = True End If C#
if (User.IsInRole(“Administrators”)) { DeleteButton.Visible = true; }
Note that VB.NET uses the If and End If keywords, whereas C# uses if together with a pair of curly braces to indicate the code block that is being executed. Also, with C#, the parentheses around the condition being tested are required, whereas VB.NET requires you to use the keyword Then after the condition. Often you want to perform a different action if the condition is not True. Using the negation operator Not or ! you could simply write another statement: VB.NET
If User.IsInRole(“Administrators”) Then DeleteButton.Visible = True End If If Not User.IsInRole(“Administrators”) Then DeleteButton.Visible = False End If C#
if (User.IsInRole(“Administrators”)) { DeleteButton.Visible = true; } if (!User.IsInRole(“Administrators”)) { DeleteButton.Visible = false; }
Clearly, this leads to messy code, because you need to repeat each expression evaluation twice: once for the True case and once for the False case. Fortunately, there is an easier solution: the Else block (else in C#): VB.NET
If User.IsInRole(“Administrators”) Then DeleteButton.Visible = True Else DeleteButton.Visible = False End If C#
if (User.IsInRole(“Administrators”)) { DeleteButton.Visible = true; }
Statements ❘ 161
else { DeleteButton.Visible = false; }
For simple conditions, this If Else construct works fine. But consider a scenario where you have more than two options. In those scenarios you can use ElseIf in VB.NET or the else if ladder in C#. Imagine that your site uses three different roles: administrators, content managers, and standard members. Administrators can create and delete content; content managers can only create new content, whereas members can’t do either of the two. To show or hide the relevant buttons, you can use the following code: VB.NET
If User.IsInRole(“Administrators”) Then CreateNewArticleButton.Visible = True DeleteArticleButton.Visible = True ElseIf User.IsInRole(“ContentManagers”) Then CreateNewArticleButton.Visible = True DeleteArticleButton.Visible = False ElseIf User.IsInRole(“Members”) Then CreateNewArticleButton.Visible = False DeleteArticleButton.Visible = False End If C#
Although the ElseIf or else if ladder helps to make the code more readable, you can still end up with difficult code when you have many expressions to test. If that’s the case, you can use the Select Case (VB.NET) or switch (C#) statement.
Switches / Select Case Constructs Imagine you’re building a web site for a concert hall that has shows on Saturday. During the week, visitors can buy tickets online for Saturday’s gig. To encourage visitors to buy tickets as early as possible, you decide to give them an early-bird discount. The earlier in the week they buy their tickets,
162 ❘ Chapter 5 Programming Your ASP.NET Web Pages
the cheaper they are. Your code to calculate the discount rate can look like this, using a Select Case / switch statement: VB.NET
Dim today As DateTime = DateTime.Now Dim discountRate As Double = 0 Select Case today.DayOfWeek Case DayOfWeek.Monday discountRate = 0.4 Case DayOfWeek.Tuesday discountRate = 0.3 Case DayOfWeek.Wednesday discountRate = 0.2 Case DayOfWeek.Thursday discountRate = 0.1 Case Else discountRate = 0 End Select C#
For each day where the discount is applicable (Monday through Thursday) there is a Case block. The differences between VB.NET and C# syntax are quite small: C# uses a lowercase c for case and requires a colon after each case label. Additionally, you need to exit each block with a break statement. At runtime, the condition (today.DayOfWeek) is evaluated and the correct block is executed. It’s important to understand that only the relevant block is executed, and nothing else. When no valid block is found (the code is executed on a day between Friday and Sunday) the code in the Case Else or default block fires. You’re not required to write a Case Else or default block
Statements ❘ 163
although it’s recommended to do so, because it makes your code more explicit and easier to read. The preceding examples could have left it out, because discountRate already gets a default value of 0 at the top of the code block. To get a feel for the statements you have seen so far, the following Try It Out exercise shows you how to use them in a small demo application.
Try It Out
Creating a Simple Web-Based Calculator
In this exercise you create a simple calculator that is able to add, subtract, multiply, and divide values. It shows you how to use some of the logical and assignment operators and demonstrates the If and Select Case / switch constructs.
1 .
Start by creating a new Web Form called CalculatorDemo.aspx in the Demos folder. Make sure you don’t name the page Calculator or you’ll run into troubles later in this chapter where you create a class by that name. Once again, make sure you’re using the Code Behind model and select the correct programming language.
. 2
Switch the page to Design View, and click in the dashed rectangle to put the focus on it. Choose Table ➪ Insert Table from the main menu and add a table with three rows and three columns. Merge all three cells of the first row by selecting them, right-clicking the selection, and choosing Modify ➪ Merge Cells from the menu that appears.
. 3
Add the following controls to the page, set their ID and other properties as in the following table, and arrange the controls as shown in Figure 5-3. Control Type
Control ID
Property Settings
Label
ResultLabel
Clear its Text property. To do this, right-click the property name in the Properties Grid and choose Reset.
TextBox
ValueBox1
DropDownList
OperatorList
Add four ListItems for the following arithmetic operators using the DropDownList’s Smart Tasks panel. + * /
TextBox
ValueBox2
Button
CalculateButton
Set the Text property of the button to Calculate.
When you’re done, your page should look like Figure 5-3 in Design View.
164 ❘ Chapter 5 Programming Your ASP.NET Web Pages
Figure 5-3
4.
Double-click the Calculate button and add the following bolded code in the code placeholder that VWD added for you: VB.NET
Protected Sub CalculateButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles CalculateButton.Click If ValueBox1.Text.Length > 0 AndAlso ValueBox2.Text.Length > 0 Then Dim result As Double = 0 Dim value1 As Double = Convert.ToDouble(ValueBox1.Text) Dim value2 As Double = Convert.ToDouble(ValueBox2.Text) Select Case OperatorList.SelectedValue Case “+” result = value1 + value2 Case “-” result = value1 - value2 Case “*” result = value1 * value2 Case “/” result = value1 / value2 End Select ResultLabel.Text = result.ToString() Else ResultLabel.Text = String.Empty End If End Sub C#
protected void CalculateButton_Click(object sender, EventArgs e) { if (ValueBox1.Text.Length > 0 && ValueBox2.Text.Length > 0) { double result = 0; double value1 = Convert.ToDouble(ValueBox1.Text); double value2 = Convert.ToDouble(ValueBox2.Text); switch (OperatorList.SelectedValue) { case “+”: result = value1 + value2; break; case “-”: result = value1 - value2;
Statements ❘ 165
break; case “*”: result = value1 * value2; break; case “/”: result = value1 / value2; break; } ResultLabel.Text = result.ToString(); } else { ResultLabel.Text = string.Empty; } }
5.
Save all changes and press Ctrl+F5 to open the page in the browser. If you get an error instead of seeing the page, make sure you typed the code exactly as shown here, and that you named all controls according to the table you saw earlier.
. 6
Enter a number in the first and second text boxes, choose an operator from the drop-down list, and click the Calculate button. The code in the Code Behind fires and then — based on the item you selected in the drop-down list — the correct calculation is performed and the label is updated with the result.
. 7
Go ahead and try some other numbers and operators; you’ll see that the calculator carries out the right operation every time you click the Calculate button.
How It Works When you enter two values and click the Calculate button, the following code in the Code Behind fires: VB.NET
If ValueBox1.Text.Length > 0 AndAlso ValueBox2.Text.Length > 0 Then C#
if (ValueBox1.Text.Length > 0 && ValueBox2.Text.Length > 0)
This code is necessary to ensure that both text boxes contain a value. (In Chapter 9 you see a much cleaner way to perform this validation.) The code uses a simple If statement to ensure that both fields have a value. It also uses AndAlso or && to avoid checking the Text property of the second TextBox when the first is empty. The code then declares a Double to hold the result of the calculation and then gets the values from the two text box controls, converts the values to a Double using the ToDouble method of the Convert class, and then sets up a Select Case (switch in C#) block to handle the type of operator you have chosen in the drop-down list: VB.NET
Select Case OperatorList.SelectedValue Case “+” result = value1 + value2
166 ❘ Chapter 5 Programming Your ASP.NET Web Pages
C#
switch (OperatorList.SelectedValue) { case “+”: result = value1 + value2; break;
For each item in the drop-down list, there is a case statement. When you have chosen the + operator from the list, the code in the first case block will fire, and result is assigned the sum of the numbers you entered in the two text boxes. Likewise, when you choose the subtraction operator, for example, the two values are subtracted from each other. At the end, the result is converted to a String and then displayed on the label called ResultLabel.
The Select Case / switch statements close off the discussion about making decisions in your code. There’s one more group of statements left: Loops that enable you to loop over code or over objects in a collection.
Loops Loops are extremely useful in many applications, because they enable you to execute code repetitively, without the need to write that code more than once. For example, if you have a web site that needs to send a newsletter by e‑mail to its 20,000 subscribers, you write the code to send the newsletter once, and then use a loop that sends the newsletter to each subscriber the code finds in a database. Loops come as a few different types, each with their own usage and advantages.
The For Loop The For loop simply repeats its code a predefined number of times. You define the exact number of iterations when you set up the loop. The For loop takes the following format: VB.NET
For counter [ As datatype ] = start To end [ Step stepSize ] ‘ Code that must be executed for each iteration Next [ counter ] C#
for (startCondition; endCondition; step definition) { // Code that must be executed for each iteration }
This looks a little odd, but a concrete example makes this a lot easier to understand: VB.NET
For loopCount As Integer = 1 To 10 Label1.Text &= loopCount.ToString() & “ ” Next
Statements ❘ 167
C#
for (int loopCount = 1; loopCount Sends out an e‑mail to the address specified by emailAddress. <param name=”emailAddress”>The e‑mail address of the recipient. <param name=”mailBody”>The body of the mail message. This method returns True when the message was sent successfully; and False otherwise. Attention: this method assumes a valid mail server is available.
Object Orientation Basics ❘ 181
Public Function SendEmailMessage(ByVal emailAddress As String, ByVal mailBody As String) As Boolean ‘ Implementation goes here End Function C#
/// <summary> /// Sends out an e‑mail to the address specified by emailAddress. /// /// <param name=”emailAddress”>The e‑mail address of the recipient. /// <param name=”mailBody”>The body of the mail message. /// This method returns true when the message was sent successfully; /// and false otherwise. /// Attention: this method assumes a valid mail server is /// available. bool SendEmailMessage(string emailAddress, string mailBody) { // Implementation goes here }
The cool thing about this type of commenting is that the comments you type here show up in IntelliSense in the code editor when you try to call the method (see Figure 5-7).
Figure 5-7
This makes it much easier for you and other developers to understand the purpose of the method and its parameters. Besides aiding development in the code editor, the XML comments can also be used to create goodlooking, MSDN-like documentation. A number of third-party tools are available that help you with this, including Microsoft’s own Sandcastle (http://msdn2.microsoft.com/en-us/vstudio/ bb608422.aspx) and Document! X from Innovasys (www.innovasys.com/).
Object Orientation Basics A chapter about writing code in ASP.NET wouldn’t be complete without a section on object orientation (OO). Object orientation, or object-oriented programming, is a highly popular style of programming where the software is modeled as a set of objects interacting with each other. Object orientation is at the heart of the .NET Framework. Literally everything inside the framework is an object, from simple things like integers to complex things like a DropDownList control, a connection to the database, or a data-driven control. Because object orientation is such an important aspect of .NET, it’s important to be familiar with the general concepts of object-oriented programming. At the same time, you don’t have to be an expert on OO to be able to build web sites with ASP.NET. This section gives you a 10,000-foot
182 ❘ Chapter 5 Programming Your ASP.NET Web Pages
overview of the most important terms and concepts. This helps you get started with object orientation, so you can start building useful applications in the next chapter instead of keeping your nose in the books for the next three weeks.
Important OO Terminology In object orientation, everything revolves around the concept of objects. In OO everything is, in fact, an object. But what exactly is an object? And what do classes have to do with them?
Objects Objects are the basic building blocks of object-oriented programming languages. Just like in the real world, an object in OO-land is a thing, but stored in the computer’s memory. It can be an integer holding someone’s age or an open database connection to a SQL Server located on the other side of the world, but it can also be something more conceptual, like a web page. In your applications, you create a new object with the New (new in C#) keyword, as you saw with the calculator example. This process of creating new objects is called instantiating and the objects you create are called instances. You can instantiate complex or custom types like Calculator as well as simple types like Integers and Strings: VB.NET
Dim myCalculator As Calculator = New Calculator() Dim age As Integer = New Integer() C#
Calculator myCalculator = new Calculator(); int age = new int();
Because it’s so common to create variables of simple types like Integer (int in C#) and String (string in C#), the compiler allows you to leave out the new keyword and the assignment. Therefore, the following code is functionally equivalent to the preceding age declaration: VB.NET
Dim age As Integer C#
int age;
All data types listed at the beginning of this chapter except System.Object can be created without the New keyword. Once you have created an instance of an object, such as the myCalculator object, it’s ready to be used. For example, you can access its methods and properties to do something useful with the object. But before you look at methods and properties, you need to understand classes first.
Classes Classes are the blueprints of objects. Just as you can use a single blueprint to build a bunch of similar houses, you can use a single class to create multiple instances of that class. So the class acts as
Object Orientation Basics ❘ 183
the definition of the objects that you use in your application. At its most basic form, a class looks like this: VB.NET
Public Class ClassName End Class C#
public class ClassName { }
Because this code simply defines an empty class, it cannot do anything useful. To give the class some behavior, you can give it properties, methods, and constructors. In addition, you can let the class inherit from an existing class to give it a head start in terms of functionality and behavior. You’ll come to understand these terms in the next couple of sections.
Properties Properties of an object are the characteristics the object has. Consider a Person object. What kind of properties does a Person have? It’s easy to come up with many different characteristics, but the most common are: ➤➤
First name
➤➤
Last name
➤➤
Date of birth
You define a property in a class with the Property keyword (in VB.NET) or with a property header similar to a method in C#. In both languages, you use a Get block (get in C#) and a Set block (set in C#) to define the so-called getters and setters of the property. The getter is accessed when an object is asked for the value of a specific property, and the setter is used to assign a value to the property. Properties only provide access to underlying data stored in the object; they don’t contain the actual data. To store the data, you need what is called a backing variable. This is often a simple variable defined in the class that is able to store the value for the external property. In the following example, the variable _firstName is the backing variable for the FirstName property: VB.NET
Public Class Person Private _firstName As String Public Property FirstName() As String Get Return _firstName End Get Set(ByVal value As String) _firstName = value End Set End Property End Class
184 ❘ Chapter 5 Programming Your ASP.NET Web Pages
C#
public class Person { private string _firstName; public string FirstName { get { return _firstName; } set { _firstName = value; } } }
It is common to prefix the private backing variables with an underscore, followed by the first word in all lowercase, optionally followed by more words that start with a capital again. So the FirstName property has a backing variable called _firstName, LastName has one called _lastName, and so on. This way, all variables that apply to the entire class are nicely packed together in the IntelliSense list. Simply type an underscore in your code and you’ll get the full list of private variables. Note that the underscore is typically not used when defining variables inside a function or a subroutine. Just as the Public keyword you saw earlier, Private is also an access modifier. You learn more about access modifiers later in this chapter. The main reason for a property in a class is to encapsulate data. The idea is that a property enables you to control the data that is being assigned to it. This way, you can perform validation or manipulation of the data before it’s stored in the underlying backing variable. Imagine that one of the business rules of your application states that all first names must be written with the first letter as a capital. In non–object-oriented languages, the developer setting the name would have to keep this rule in mind every time a variable was filled with a first name. In an OO approach, you can make the FirstName property responsible for this rule so others don’t have to worry about it anymore. You can do this type of data manipulation in the setter of the property: VB.NET
Set(ByVal value As String) If Not String.IsNullOrEmpty(value) Then _firstName = value.Substring(0, 1).ToUpper() & value.Substring(1) Else _firstName = String.Empty End If End Set C#
set { if (!string.IsNullOrEmpty(value)) { _firstName = value.Substring(0, 1).ToUpper() + value.Substring(1); } else { _firstName = string.Empty; } }
Object Orientation Basics ❘ 185
This code demonstrates that both in VB.NET as in C#, the value parameter is accessible, just as a parameter to a method. The value parameter contains the value that is being assigned to the property. In VB.NET, the value parameter is defined explicitly in the property’s setter. In C# it’s not specified explicitly, but you can access it nonetheless. The code first checks if the value that is being passed is not Nothing (null in C#) and that it doesn’t contain an empty string using the handy String.IsNullOrEmpty method. The code in the If block then takes the first letter of value, using the SubString method of the String class, which it passes the values 0 and 1. The 0 indicates the start of the substring and the 1 indicates the length of the string that must be returned. String indexing is zero-based as well, so a start of 0 and a length of 1 effectively returns the first character of the value parameter. This character is then changed to uppercase using ToUpper(). Finally, the code takes the remainder of the value parameter using SubString again and assigns the combined name back to the backing variable. You can now use code that sets the name with arbitrary casing. But, when you try to access the name again, the first name will always begin with a proper first character: VB.NET
Dim myPerson As Person = New Person() ‘ Create a new instance of Person myPerson.FirstName = “imar” ‘ Accessing the setter that changes the value Label1.Text = myPerson.FirstName
‘ Accessing the getter that now returns Imar
C#
Person myPerson = new Person(); myPerson.FirstName = “imar”;
// Create a new instance of Person // Accessing the setter that changes the value
Label1.Text = myPerson.FirstName;
// Accessing the getter that now returns Imar
For simple properties that don’t need any data manipulation or validation, you can use so-called automatic properties. Automatic properties used to be available in C# only, but starting with the release of .NET 4, automatic properties are now available in VB.NET as well. With these properties, you can use a much more condensed syntax without the need for a private backing variable. When the code is compiled, the compiler will create the necessary backing variable for you, and you’ll need to refer to the public property. Here’s the DateOfBirth property of the Person class written as an automatic property: VB.NET
Public Property DateOfBirth As DateTime C#
public DateTime DateOfBirth { get; set; }
The Visual Basic implementation of automatic properties has one advantage over the C# version: you can declare the property and give it a value in one shot. The following snippet defines a CreateDate property and assigns it with the current date and time: VB.NET
Public Property CreateDate As DateTime = DateTime.Now
186 ❘ Chapter 5 Programming Your ASP.NET Web Pages
To assign a default value to an automatic property in C#, you need to set its value using constructors, which are discussed later. If you later decide you need to write code in the getter or the setter of the property, it’s easy to extend the relevant code blocks without breaking your existing applications. Until that time, you have nice, clean property definitions that don’t clutter up your class.
Creating Read-Only and Write-Only Properties At times read-only or write-only properties make a lot of sense. For example, the ID of an object could be read-only if it assigned by the database automatically. When the object is constructed from the database, the ID is assigned to the private backing variable. The public Id property is then made read-only to stop calling code from accidentally changing it. Likewise, you can have a write-only property for security reasons. For example, you could have a Password property on a Person object that you can only assign to if you know it, but no longer read it afterward. Internally, code within the class can still access the backing variables to work with the password value. Another good candidate for a read-only property is one that returns a combination of data. Consider a FullName property of a Person class that returns a combination of the FirstName and LastName properties. You use the setter of each individual property to assign data, but you can have a read-only property that returns the concatenated values. Read-only or write-only properties in C# are simple: just leave out the setter (for a read-only property) or the getter (for a write-only property). VB.NET is a bit more verbose and wants you to specify the keyword ReadOnly or WriteOnly explicitly. The following code snippet shows a read-only FullName property in both VB.NET and C#: VB.NET
Public ReadOnly Property FullName() As String Get Return _firstName & “ “ & _lastName End Get End Property C#
public string FullName { get { return _firstName + “ “ + _lastName; } }
When you try to assign a value to a read-only property, you’ll get an error in VWD. Similar to properties, objects can also have methods.
Methods If properties are the things that a class has (its characteristics), then methods are the things a class can do or the operations it can perform. A Car class, for example, has characteristics such as Brand, Model, and Color. Its methods could be Drive(), Brake(), and OpenDoors(). Methods give objects the behavior that enables them to do something.
Object Orientation Basics ❘ 187
You have already seen methods at work earlier when this chapter discussed some ways to write organized code using subs and functions. You simply add methods to a class by writing a function or a sub between the start and end elements of the class. For example, imagine the Person class has a Save method that enables the object to persist itself in the database. The method’s signature could look like this: VB.NET
Public Class Person Public Sub Save() ‘ Implementation goes here End Sub End Class C#
public class Person { public void Save() { // Implementation goes here } }
If you want to call the Save method to have the Person object save itself to the database, you’d create an instance of it, set the relevant properties such as FirstName, and then call Save: VB.NET
Dim myPerson As Person = New Person() myPerson.FirstName = “Joop” myPerson.Save() C#
Person myPerson = new Person(); myPerson.FirstName = “Joop”; myPerson.Save();
The Save method would then know how to save the Person in the database. Note that a new instance of the Person class is created with the New (new in C#) keyword followed by the class name. When this code fires, it calls the object’s constructor, which is used to create instances of objects.
Constructors Constructors are special methods in a class that help you create an instance of your object. They run as soon as you try to create an instance of a class, so they are a great place to initialize your objects to some default state. Earlier you learned that you create a new instance of an object using the New (new in C#) keyword: VB.NET
Dim myCalculator As Calculator = New Calculator()
188 ❘ Chapter 5 Programming Your ASP.NET Web Pages
C#
Calculator myCalculator = new Calculator();
The New keyword is followed by the object’s constructor: the name of the class. By default, when you create a new class in Visual Web Developer, you get a default constructor for C# but not for VB.NET. That’s not really a problem, though, because the compiler will generate a default constructor for you if no other constructor exists. A default constructor has no arguments and takes the name of the class in C# and the reserved keyword New in VB.NET: VB.NET
Public Class Person Public Sub New() End Sub End Class C#
public class Person { public Person() { } }
Although this default constructor is nice for creating standard instances of your classes, sometimes it is really useful to be able to send some information into the class up front, so it’s readily available as soon as it is constructed. For example, with the Person class, it could be useful to pass in the first and last names and the date of birth to the constructor so that data is available immediately afterwards. To enable this scenario, you can create an overloaded constructor. An overloaded constructor or method is essentially a copy of an existing method with the exact same name, but with a different method signature. To have the constructor accept the names and the date of birth, you need the following code: VB.NET
Public Sub New(ByVal firstName As String, ByVal lastName As String, ByVal dateOfBirth As DateTime) _firstName = firstName _lastName = lastName _dateOfBirth = dateOfBirth End Sub C#
With this code, you can create a new Person object: VB.NET
Dim myPerson As Person = New Person(“Imar”, “Spaanjaars”, New DateTime(1971, 8, 9)) C#
Person myPerson = new Person(“Imar”, “Spaanjaars”, new DateTime(1971, 8, 9));
The constructor accepts the values passed to it and assigns them to the private backing variables, so right after this line of code, the myPerson object is fully initialized. Visual Basic supports a slightly different syntax to declare and initialize an object in one fell swoop using the Dim myVariable As New ClassName syntax. The following code is equivalent to the previous instantiation of a Person instance: Dim myPerson As New Person(“Imar”, “Spaanjaars”, New DateTime(1971, 8, 9))
In addition to overloaded constructors, .NET offers another quick way to create an object and initialize a few properties: object initializers. With an object initializer, you provide the initial values for some of the properties at the same time you declare an instance of your objects. The following code creates a Person object and assigns it a value for the FirstName and LastName properties: VB.NET
Dim myPerson As New Person() With {.FirstName = “Imar”, .LastName = “Spaanjaars”} C#
Person myPerson = new Person() { FirstName = “Imar”, LastName = “Spaanjaars” };
In VB.NET, you need the With keyword in front of the properties list. In addition, you need to prefix each property name with a dot (.). Other than that, the syntax is the same for both languages. Object initializers are great if you quickly need to set a bunch of properties on an object without forcing you to write specialized overloaded versions of the constructors. Although it’s useful to have this Person class in your application, at times you may need specialized versions of a Person. For example, your application may require classes like Employee and Student. What should you do in this case? Create two copies of the Person class and name them Employee and Student, respectively? Although this approach certainly works, it has a few large drawbacks. The biggest problem is the duplication of code. If you decide to add a SocialSecurityNumber property, you now need to add it in multiple locations: in the general Person class and in the Employee and Student classes. Object inheritance, a major pillar of object orientation, is designed to solve problems of this kind.
Inheritance Earlier you learned that System.Object is the parent of all other data types in .NET, including all the built-in types and types that you define yourself, meaning that each type in .NET (except Object itself) inherits from Object. One of the benefits of inheritance is that you can define a behavior at a high level (for example in the Object class) that is available to inheriting classes
190 ❘ Chapter 5 Programming Your ASP.NET Web Pages
automatically without the need to duplicate that code. In the .NET Framework, the Object class defines a few members that all other objects inherit, including the ToString() method. To let one class inherit another, you need to use the Inherits keyword in VB.NET and the colon (:) in C# as shown in the following example that defines a Student class that inherits Person: VB.NET
Public Class Student Inherits Person C#
public class Student : Person { }
To see how inheritance works, think again about the Person class shown in earlier examples. That class had a few properties such as FirstName and LastName, and a Save method. But, if it is inheriting from Object, does it also have a ToString() method? You bet it does. Figure 5-8 shows the relation between the Object class and the Person class that inherits from Object. Figure 5-8 shows that Person inherits from Object (indicated by the line with a single arrow head pointing in the direction of the class that is being inherited from), which in turn means that a Person instance can do whatever an Object can do. So, for example, you can call ToString() on your Person object: Label1.Text = myPerson.ToString()
Figure 5-8
‘ Writes out Person
The default behavior of the ToString() method defined in Object is to say its own class name. In the preceding example, it means that the Person class inherits this behavior and thus says Person as its name. Usually, this default behavior is not enough and it would be much more useful if the Person could return the full name of the person it is representing, for example. You can easily do this by overriding the ToString() method. Overriding a method or property effectively redefines the behavior the class inherits from its parent class. To override a method you use the keyword Overrides in VB.NET and override in C#. The following snippet redefines the behavior of ToString in the Person class: VB.NET
Public Overrides Function ToString() As String Return FullName & “, born at “ & _dateOfBirth.ToShortDateString() End Function C#
public override string ToString() { return FullName + “, born at “ + _dateOfBirth.ToShortDateString(); }
Object Orientation Basics ❘ 191
With this definition of ToString in the Person class, it no longer returns the word “Person,” but now returns the full name of the person it is representing: Label1.Text = myPerson.ToString() ‘ Imar Spaanjaars, born at 8/9/1971
Notice how the code uses the read-only FullName property to avoid coding the logic of concatenating the two names again. You can’t just override any method member you see fit. For a method to be overridable, the parent class needs to mark the member with the keyword virtual (in C#) or Overridable (in VB.NET). Object inheritance in .NET enables you to create a hierarchy of objects that enhance, or add functionality to, other objects. This enables you to start out with a generic base class (Object). Other classes can then inherit from this class, adding specialized behavior. If you need even more specialized classes, you can inherit again from the class that inherits from Object, thus creating a hierarchy of classes that keep getting more specialized. This principle works for many classes in the .NET Framework, including the Page class. You may not realize it, but every ASPX page you create in VWD is actually a class that inherits from the class System.Web.UI.Page. This Page class in turn inherits from TemplateControl, which inherits from Control, which inherits from Object. The entire hierarchy is shown in Figure 5-9. At the bottom you see the class MyWebPage, which could be a Code Behind class of a page such as MyWebPage.aspx. In Figure 5-9 you can see that TemplateControl is an abstract class — a class that cannot be instantiated; that is, you cannot use New (new in C#) to create a new instance of it. It serves solely as common base class for others (like Page) that can inherit from it. The exact classes between Page and Object are not really relevant at this stage, but what’s important is that your page inherits all the Figure 5-9 behavior that the Page class has. The fact that all your ASPX pages inherit from Page is more useful than you may think at first. Because it inherits from Page, you get loads of properties and methods defined in this class for free. For example, the Page class exposes a Title property that, when set, ends up as a element in the page. Your page can simply set this property, and the parent Page class handles the rest for you: VB.NET
Title = “Beginning ASP.NET 4 in C# and VB from Wrox” C#
Title = “Beginning ASP.NET 4 in C# and VB from Wrox”;
You use inheritance in the next chapter when you create a BasePage class that serves as the parent class for most of the pages you create in the Planet Wrox web site. In earlier examples, including the override for the ToString() method, you have seen the keyword Public. Additionally, when creating backing variables, you saw the keyword Private. These keywords are called access modifiers and determine the visibility of your code.
192 ❘ Chapter 5 Programming Your ASP.NET Web Pages
Access Modifiers Earlier in this chapter I mentioned that a core concept of OO is encapsulation. By creating members such as functions and properties, you make an object responsible for the implementation. Other objects interacting with this object consider those methods and properties as black boxes. That is, they pass some data in and optionally expect some result back. How the method performs its work is of no interest to them; it should just work as advertised. To enable an object to shield some of its inner operations, you need a way to control access to types and members. You do this by specifying an access modifier in front of the class, property, or method name. The following table lists the available access modifiers for C# and VB.NET and explains their purpose. C#
VB.NET
Description
public
Public
The class or member can be accessed from everywhere, including code outside the current application.
protected
Protected
Code with a protected access modifier is available only within the type that defines it or within types that inherit from it. For example, a protected member defined in the Page class is accessible to your ASPX page because it inherits from Page.
internal
Friend
Limits the accessibility of your code to other code within the same assembly. An assembly is a set of one or more compiled code files (either an .exe or a .dll file) containing reusable .NET code.
private
Private
A class or member that is accessible only within the type that defines it. For example, with the Person class, the _firstName variable is accessible only from within the Person class. Other code, like an ASPX page, cannot access this field directly, and needs to access the public FirstName property to get or set the first name of a person.
Of these four access modifiers, only protected and internal (Protected and Friend in VB) can be combined. The other two must be used separately. By combining protected and internal, you can create members that are accessible by the current class and any class that inherits from it in the current assembly only. As with some of the other OO concepts, you won’t be spending half your day specifying access modifiers in your code. However, it’s good to know that they exist and what they do. That way, you may have a clue why sometimes your classes don’t show up in the IntelliSense list. There’s a fair chance you forgot to specify the public access modifier (Public in VB.NET) on the class in that case. The default is internal (Friend in VB.NET), which makes the class visible to other classes in the same assembly (usually a file with a .dll extension) but hides it from code outside the assembly. Adding the keyword public or Public in front of the class definition should fix the problem.
Events The final important topic that needs to be discussed in this chapter is events. ASP.NET is an eventdriven environment, which means that code can execute based on certain events that occur in your
Object Orientation Basics ❘ 193
application. Events are raised by certain objects in the application and then handled by others. Many objects in the .NET Framework are capable of raising an event, and you can even add your own events to classes that you write. To be able to handle an event raised by an object, you need to write an event handler, which is basically a normal method with a special signature. You can wire up this event handler to the event using event wiring syntax, although VWD takes care of that most of the time for you. When an object, such as a control in a web page, raises an event, it may have the need to pass additional information to the event handler, to inform it about relevant data that caused or influenced the event. You can send out this information using an event arguments class, which is the class System. EventArgs or any class that inherits it. To see how all these terms fit together, consider what happens when you click a button in a web page. When you click it, the client-side button in the browser causes a postback. At the server, the Button control sees it was clicked in the browser and then raises its Click event. It’s as if the button says: “Oh, look everyone. I just got clicked. In case anyone is interested, here are some details.” Usually, the code that is interested in the button’s Click event is your own page that needs to have an event handler to Figure 5-10 handle the click. You can create an event handler for the Button by double-clicking it in the designer. Alternatively, you can double-click the relevant event on the Properties Grid of the control with the Events tab listed (see Figure 5-10), which you can open by clicking the button with the lightning bolt on the toolbar. If you double-click the control in Design View or the event name in the Properties Grid, Visual Web Developer writes the code for the event handler for you. The following snippet shows the handler for a Button control’s Click in VB.NET and C#: VB.NET
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Button1.Click End Sub C#
In the VB.NET example, you see a standard method with some arguments, followed by Handles Button1.Click. This is the event wiring code that hooks up the Button control’s Click event to the Button1_Click method. Now, whenever the button is clicked, the code inside Button1_Click is executed. The C# version doesn’t have this Handles keyword. Instead, with C# you’ll find that VWD has added the following bold code to the Button control in the markup of the page:
194 ❘ Chapter 5 Programming Your ASP.NET Web Pages
With this piece of markup, the compiler generates the necessary code to link up the Button1_Click method to the Click event of the button. At runtime you’ll see the exact same behavior: when you click the button, the code in Button1_Click is executed. You can also see that this Button1_Click event handler has two parameters: an Object called sender and an EventArgs class called e. This is a standard .NET naming scheme and is followed by all objects that generate events. The sender parameter contains a reference to the object that triggered the event; Button1 in this example. This enables you to find out who triggered an event in case you wired up multiple events to the same event handler. The second parameter is an instance of the EventArgs class and supplies additional arguments to the event. With a button’s click, there is no additional relevant data to submit, so the plain and empty EventArgs class is used. However, in later chapters (for example, Chapter 9, which deals with data-driven Web Forms), you see some examples of classes that fire events with richer information. With the concepts of events, you have come to the end of the section on object orientation. This section should have familiarized you with the most important terms used in object-oriented programming. You see practical examples of these concepts in the remainder of this book.
Practical Tips on Programming The following list presents some practical tips on programming: ➤➤
Always give your variables meaningful names. For simple loop counters, you can use i although loopCount probably describes the purpose of the variable much better. Don’t prefix variables with the word var. All variables are variables, so adding var only adds noise to your code. Consider useful names such as _firstName and _categoryId as opposed to strFName or catI for private fields, and names like FirstName and Person for public properties and classes, respectively.
➤➤
Experiment and experiment. Even more so than with working with controls and ASPX pages, the best way to learn how to program is by actually doing it. Just type in some code and hit Ctrl+F5 to see how the code behaves. The compiler will bark at you when something is wrong, providing you with useful hints on how to fix it. Don’t be afraid to mess anything up; just keep trying variations until the code does what you want it to do. If you can’t make your code work, check out Chapter 18 that deals with debugging. You’ll find useful tips to locate and fix many of the errors that may occur in your code.
➤➤
When writing functions or subroutines, try to minimize the number of lines of code. Usually, methods with more than 40 or 50 lines of code are a sign of bad design. When you see such code, consider the option to move certain parts to their own routine. This makes your code much easier to understand, leading to better code with fewer bugs. Even if a method is used only once, keeping a chunk of code in a separate method can significantly increase the readability and organization of your code.
Summary ❘ 195
➤➤
When writing comments in your code, try to describe the general purpose of the code instead of explaining obvious statements. For example, this comment (seen many times in real code) is completely useless: Dim loopCount As Integer = 0
‘ Declare loopCount and initialize it to zero
Anyone with just a little bit of coding experience can see what this code does.
Summary Although programming can get really complex, the bare basics that you need to understand are relatively easy to grasp. The fun thing about programming is that you don’t have to be an expert to make useful programs. You can simply start with a simple Hello World example and work from there, each time expanding your view on code a little. This chapter covered two main topics. First you got an introduction to programming in .NET using either C# or VB.NET. You saw what data types and variables are and learned about operators, decision making and loops. You also saw how to write organized code using functions, subs and namespaces and how to add comments to your code. The final section of this chapter dealt with object orientation. Though object orientation in itself is a very large subject, the basics are easy to pick up. In this chapter you learned about the basic elements of OO programming: classes, methods, properties, and constructors. You also learned a bit about inheritance, the driving force behind object-oriented design. In the next chapter, which deals with creating consistent-looking web pages, you see inheritance again when you create a BasePage class that serves as the parent for most of the Code Behind classes in the Planet Wrox project.
Exercises
1.
Considering the fact that the oldest person in the world lived to be 122, what’s the best numeric data type to store a person’s age? There are bonus points to collect if you come up with an even better alternative to store someone’s age.
2.
What does the following code do? VB.NET
DeleteButton.Visible = Not DeleteButton.Visible C#
DeleteButton.Visible = !DeleteButton.Visible;
continues
196 ❘ Chapter 5 Programming Your ASP.NET Web Pages
3.
Given the following class Person, what would the code look like for a new class Student that contains a string property called StudentId? Make use of inheritance to create this new class. VB.NET
Public Class Person Public Property Name As String End Class C#
public class Person { public string Name { get; set; } }
Answers to Exercises can be found in Appendix A.
⊲⊲ What You Learned in this Chapter Class
A blueprint for objects in a programming language
Collection
A special data type that is capable of holding multiple objects at the same time
Encapsulation
Hiding the inner workings and data of a class to the outside world in order to better manage and protect that data
Instantiating
The process of creating a new object in memory based on a type’s definition
Method
An operation on an object, like Brake() for a Car class
Namespace
A way to structure classes and other types in a hierarchical manner
Object Orientation
A popular style of programming where the software is modeled as a set of objects interacting with each other
Overriding
Redefining the behavior in a child class of a member defined in a parent class
Property
A characteristic of an object, like the first name of a person
CONFER PROGRAMMER TO PROGRAMMER ABOUT THIS TOPIC.
Visit p2p.wrox.com
6
Creating Consistent Looking Web Sites What You Will Learn in This Chapter: ➤➤
How to use master and content pages that enable you to define the global look and feel of a web page
➤➤
How to work with a centralized base page that enables you to define common behavior for all pages in your site
➤➤
How to create themes to define the look and feel of your site with an option for the user to choose a theme at runtime
➤➤
How to create skins to make site-wide changes to control layout
➤➤
What the ASP.NET page life cycle is and why it’s important
When you’re building a web site you should strive to make the layout and behavior as consistent as possible. Consistency gives your site a professional appearance and it helps your visitors to find their way around the site. Fortunately, ASP.NET 4 and Visual Web Developer 2010 offer a number of great features and tools to implement a consistent design, helping you create greatlooking pages in no time. In previous chapters you learned how to work with VWD, HTML, CSS, and server controls to create your web pages visually. Chapter 5 introduced you to programming in ASP.NET. This chapter is the first that combines these concepts, by showing you — among many other things — how to use programming code to change the appearance of the site. The next section shows you how to create a master page that defines the general look and feel of a page. The ASPX pages in your site can then use this master page without the need to rewrite the layout. The remaining sections of this chapter build on top of this master page.
198 ❘ Chapter 6 Creating Consistent Looking Web Sites
Consistent Page Layout with Master Pages With most web sites, only part of the page changes when you go from one page to another. The parts that don’t change usually include common regions like the header, a menu, and the footer. To create web pages with a consistent layout you need a way to define these relatively static regions in a single template file. Versions of ASP.NET prior to 2.0 did not have a template solution so you were forced to duplicate your page layout on every single page in the web site, or resort to weird programming tricks. Fortunately, this is no longer the case due to master pages. The biggest benefit of master pages is that they enable you to define the look and feel of all the pages in your site in a single location. This means that if you want to change the layout of your site — for instance, if you want to move the menu from the left to the right — you only need to modify the master page and the pages based on this master will pick up the changes automatically. When master pages were introduced in ASP.NET 2.0, they were quickly embraced by the developer community as the template solution for ASP.NET pages because they are very easy to use. Even better, VWD has great design-time support, because it enables you to create and view your pages at design time during development, rather than only in the browser at runtime. To some extent, a master page looks like a normal ASPX page. It contains static HTML such as the , , and elements, and it can also contain other HTML and ASP.NET server controls. Inside the master page, you set up the markup that you want to repeat on every page, like the general structure of the page and the menu. However, a master page is not a true ASPX page and cannot be requested in the browser directly; it only serves as the template that real web pages — called content pages — are based on. Instead of the @ Page directive that you have seen in Chapter 4, a master page uses an @ Master directive that identifies the file as a master page: VB.NET
C#
Just like a normal ASPX page, a master page can have a Code Behind file, identified by its CodeFile and Inherits attributes: VB.NET
C#
To create regions that content pages can fill in, you need to define ContentPlaceHolder controls in your page like this:
Consistent Page Layout with Master Pages ❘ 199
You can create as many placeholders as you like, although you usually need only a few to create a flexible page layout. The content pages, which are essentially normal ASPX files, but without the usual code you find in them like the , , , and elements, are connected to a master page using the MasterPageFile attribute of the Page directive: VB.NET
C#
The page-specific content is then put inside a Content control that points to the relevant ContentPlaceHolder:
Note that the ContentPlaceHolderID attribute of the Content control points to the ContentPlaceHolder that is defined in the master page. Right now it points to the default name of ContentPlaceHolder1, but in a later exercise you see how to change this. At runtime, when the page is requested, the markup from the master page and the content page are merged, processed, and sent to the browser. Figure 6-1 shows a diagram of the master page and the content page that result in the final page that is sent to the browser. Master Page Header Content Page
Menu
ContentPlaceHolder
Header
Menu
Final Page
Figure 6-1
200 ❘ Chapter 6 Creating Consistent Looking Web Sites
To see this process in action, the following sections guide you through creating master and content pages.
Creating Master Pages Master pages are added to the site using the Add New Item dialog box. They can be placed anywhere in the site, including the root folder, but from an organizational point of view, it’s often easier to store them in a separate folder. Just like normal ASPX pages, they support the inline code model as well as the Code Behind model. The master pages used in the Planet Wrox project use the Code Behind model exclusively. In the following exercise, you learn how to create a simple master page and add some HTML to it to define the general structure of the pages in your web site.
Try It Out
Creating a Master Page
1.
Open the Planet Wrox project in VWD if it is not open already.
. 2
In Chapter 2 you created a folder called MasterPages to hold your master pages and then added a single master page to that folder. If you didn’t carry out that exercise, add the master page now. To do this, create the MasterPages folder, right-click the new folder, choose Add New Item, and select Master Page. Make sure that the master page uses Code Behind and that it is using your preferred programming language. Name the master page Frontend.master.
3.
Add the following highlighted code between the tags of the master page, replacing the tags and the ContentPlaceHolder that VWD added for you when you created the master. Note that this is almost the same code you added to Default.aspx in Chapter 3, except for the element and the element within the Header . The element takes the user back to the home page, and will be styled later. Header Goes Here Menu Goes Here Sidebar Goes Here Footer Goes Here
Make sure that you have the ContentPlaceHolder within the MainContent tags. You can drag one from the Toolbox onto the page or enter the code directly, using IntelliSense’s helpful hints. In both cases you should give the control an ID of cpMainContent. 4.
Next, switch the master page into Design View and then drag Styles.css from the Styles folder in the Solution Explorer onto the master page. As soon as you drop the file, VWD updates the Design View and shows the layout for the site that you created in Chapter 3. If the design doesn’t change,
Consistent Page Layout with Master Pages ❘ 201
switch to Markup View and ensure there’s a tag in the head of the page pointing to your CSS file:
The page should now look like Figure 6-2 in Design View.
Figure 6-2
Note the area with the purple border around it between the menu and the footer region in your Design View. This is the ContentPlaceHolder control that is used by the content pages. You see how to use it in the next exercise. . 5
You can save and close the master page because you’re done with it for now.
How It Works Within Visual Web Developer, master pages behave like normal pages. You can add HTML and server controls to them, and you can manage the page both in Markup and Design View. The big difference is, of course, that a master page isn’t a true page itself; it only serves as a template for other pages in your site.
In the next section you see how to use this master page as the template for a content page.
Creating Content Pages A master page is useless without a content page that uses it. Generally, you’ll only have a few master pages, whereas you can have many content pages. To base a content page on a master page, you check the option Select Master Page at the bottom right of the Add New Item dialog box when you add a new Web Form to your site. Alternatively, you can set the MasterPageFile attribute on the page directly in the Markup View of the page. You saw this @ Page directive earlier in this chapter when master and content pages were introduced.
202 ❘ Chapter 6 Creating Consistent Looking Web Sites
Content pages can only directly contain Content controls that each map to a ContentPlaceHolder control in the master page. These content controls in turn can contain standard markup like HTML and server control declarations. Because the entire markup in a content page needs to be wrapped by tags, it’s not easy to turn an existing ASPX page into a content page. Usually the easiest thing to do is copy the content you want to keep to the clipboard, delete the old page, and then add a new page based on the master page to the web site. Once the page is added, you can paste the markup within the tags. You see how this works in the following exercise.
Try It Out
Adding a Content Page
In this Try It Out you see how to add a content page to the site that is based on the master page you created earlier. Once the page is added you add content to the regions.
1 .
In previous exercises you added standard ASPX pages to your project, which should now be “upgraded” to make use of the new master page. Because VWD has no built-in support to change a standard page into a content page, you need to manually copy the content from the old ASPX page into the new one. If you want to keep the welcome text you added to Default.aspx earlier, copy all the HTML between the MainContent tags to the clipboard (that is, the and the two
elements that you created earlier) and then delete the Default.aspx page from the Solution Explorer. Next, right-click the web site in the Solution Explorer and choose Add New Item. Select the correct programming language, click Web Form, name the page Default.aspx, and then, at the bottom of the dialog box, select the check boxes for Place Code in Separate File and Select Master Page, as shown in Figure 6-3.
Figure 6-3
Finally, click the Add button. . 2
In the Select a Master Page dialog box (see Figure 6-4), click the MasterPages folder in the lefthand pane, and then in the area at the right, click Frontend.master.
Consistent Page Layout with Master Pages ❘ 203
Click OK to add the page to your web site.
Figure 6-4
Instead of getting a full page with HTML as you got with standard ASPX pages, you now only get two placeholders as shown in this VB.NET example:
3.
Switch to Design View and note that everything is grayed out and read-only, except for the region for cpMainContent. Figure 6-5 shows you how the page should look.
Figure 6-5
4.
If you still have the old markup from the Default.aspx on the clipboard, click once inside the cpMainContent placeholder and press Ctrl+V. (Note: you can do this both in Design View and in Markup View). This adds the markup to the page, right between the tags.
5.
Save your changes by pressing Ctrl+S and press Ctrl+F5 to open the page in your browser. The browser should display the page very closely to what you saw in Design View (see Figure 6-5).
204 ❘ Chapter 6 Creating Consistent Looking Web Sites
6.
Now take a look at the HTML for the page in the browser. You can do this by right-clicking the page and choosing View Source or View Page Source. Note that the source of the final page in the browser is a combination of the source of the master page and the content page: Header Goes Here Menu Goes Here Hi there visitor and welcome to Planet Wrox
We're glad you're paying a visit to www.PlanetWrox.com, the coolest music community site on the Internet.
...
The first four lines come from the master page and the highlighted lines of HTML code come from the content page. . 7
Switch back to VWD and create a new page called Login.aspx in the root of the site based on the master page. Notice how VWD remembered your last settings with regard to the master page and Code Behind (make sure both are checked in case you unchecked them earlier). Switch to Markup View and create an element inside the cpMainContent placeholder with the text Log in to Planet Wrox. There’s no need to add any other controls to this page just yet, but it serves as the basis for the login functionality you’ll create in Chapter 16. Without any content in the MainContent element, the Sidebar will be moved to the left of the page.
. 8
Go back to Default.aspx and switch to Design View. Beneath the welcome text with the header and two
elements, create a new paragraph (press Enter in Design View) and type some text (for example, You can log in here). Notice how this new paragraph has a class attribute called Introduction as VWD applies the previous class to new paragraphs automatically. Remove this class using the Clear Styles option of the Apply Styles window, or manually remove it from the code in Markup View.
. 9
Highlight the words “log in” and choose Format ➪ Convert to Hyperlink from the main menu. In the dialog box that follows, click the Browse button and select the Login.aspx page that you just created. Click OK twice.
1 0.
Save all changes and press Ctrl+F5 again to view Default.aspx in the browser. Then click the link you created in the preceding step. You should now be taken to Login.aspx. Note that the general layout, like the header and the sidebar, is maintained. The only thing that changes when you go from page to page is the content in the main content area.
How It Works When a page based on a master page is requested in the browser, the server reads in both the content page and the master page, merges the two, processes them, and then sends the final result to the browser. In step 6 of this exercise you saw that the HTML in the browser for the requested page contained the markup from both files.
Consistent Page Layout with Master Pages ❘ 205
Master pages will save you from a lot of work when it comes to updating or radically changing the looks of your site. Because the entire design and layout of your site is defined in the master page, you only need to touch that single file when you want to make any changes. All content pages will pick up the changes automatically.
A Closer Look at Master Pages So far you’ve seen a master page with a content placeholder for the main content. But if you look at the master page in Markup View, you’ll find another content placeholder in the head section of the page: ...
This placeholder is added for you automatically to each new master page you create. You can use it in content pages to add page-specific content that belongs between the tags of a page, such as CSS (both embedded and external style sheets) and JavaScript, like a reference to the jQuery library. You learn more about JavaScript and jQuery in Chapters 10 and 11. You need to add content to this placeholder in Markup View, because it’s not visible in Design View. The ContentPlaceHolder called cpMainContent in the master page currently does not contain any markup itself. However, it doesn’t have to be like this. You can easily add your own content there that will serve as the default in your content pages as long as it’s not overridden by the content page. For example, you can have the following ContentPlaceHolder in a master page: This is default text that shows up in content pages that don’t explicitly override it.
When you base a new page on this master page, you won’t see this default at first in Markup View. However, you can open the Content control’s Smart Tasks panel, shown in Figure 6-6, and choose Default to Master’s Content.
Figure 6-6
206 ❘ Chapter 6 Creating Consistent Looking Web Sites
When you click Yes when asked if you want to default to the master page content, VWD removes the entire Content control from the Markup View of the page. However, when you request the page in the browser you will still see the default content from the master page. In Design View, the content is still visible, now presented as a read-only area on the design surface. A master page with default content can be useful if you add a new ContentPlaceHolder at a later stage. Existing pages can simply display the default content, without the need for you to touch all these pages. New pages can define their own content. Once you have defaulted to the master page’s content, you can create custom content again by opening the Smart Tasks panel and choosing Create Custom Content. This will copy the default contents into a new Content control that you can then modify.
Nesting Master Pages It is also possible to nest master pages. A nested master page is a master that is based on another master page. Content pages can then be based on the nested master page. This is useful if you have a web site that targets different areas that still need to share a common look and feel. For example, you can have a corporate web site that is separated by departments. The outer master page defines the global look and feel of the site, including corporate logo and other branding elements. You can then have different nested master pages for different departments. For example, the sales department’s section could be based on a different master than the marketing department’s, enabling each to add their own identity to their section of the site. Visual Web Developer 2010 has excellent Design View support for nested master pages, giving you a good look at how the final page will end up. Creating a nested master page is easy: check the Select Master Page check box when you add a master page just as you do when you add a normal content page to the site. Then add ContentPlaceHolder controls to the Content controls at locations that you want to override in the content pages. Finally, you choose your nested master page as the master for new content pages you create.
Master Page Caveats Although master pages are great and can save you a lot of work, there are some caveats that you need to be aware of. For starters, the ASP.NET runtime changes the client ID of your controls in the page. This is the id attribute that is used in client script to access controls from JavaScript in the browser. With normal ASPX pages, the server-side ID of a control is usually inserted one on one in the final HTML. For example, a Button control with a server-side ID of Button1 in a normal ASPX page defined with this code:
ends up with a client-side ID like this in the final HTML:
Using a Centralized Base Page ❘ 207
However, the same button inside an control ends up like this:
The name attribute has been prefixed with the auto-generated ID of the master page (ctl00) and both the name and the id attributes contain the ID of the ContentPlaceHolder control (cpMainContent). This means that any client-side code that refers to Button1 should now refer to cpMainContent_Button1.
Note that this is not just a master page problem. You’ll also run into this behavior in other situations; for example, when working with user controls (discussed in Chapter 8) and data-bound controls (discussed in Chapter 13 and onward). The second caveat is related to the first. Because the name and id of the HTML elements are changed, they add considerably to the size of the page. This may not be problematic for a single control, but once you have pages with lots of controls, this could impact the performance of your site. The problem gets worse with nested master pages, where both content controls are appended to the ID. The same button inside a nested master page can end up like this:
To mitigate the problem, you should keep the IDs of your ContentPlaceHolder and Content controls as short as possible. To improve readability, this book uses longer names, like cpMainContent. However, in your own sites, you can reduce this to cpMC or cp1 to save some bandwidth on every request.
NOTE ASP.NET 4 introduces a new feature called ClientIDMode that helps minimize the problems typically associated with changing client-side IDs. You learn more about this feature in Chapter 8.
Master pages enable you to define the general look and feel of your site in a single location, thus improving the consistency and maintainability of your site. However, there is another way to improve consistency: centralize the behavior of the pages in your web site. You can do this with a so-called base page, which is discussed next.
Using a Centralized Base Page In Chapter 5 you learned that by default all ASPX pages derive from a class called System.Web.UI .Page. This means all of your pages have at least the behavior defined in this class.
208 ❘ Chapter 6 Creating Consistent Looking Web Sites
However, in some circumstances this behavior is not enough and you need to add your own stuff to the mix. For example, you may have the need to add some behavior that applies to all the pages in your site. Instead of adding this behavior to each and every individual page, you can create a common base page. All the pages in your site can then inherit from this intermediate page instead of from the standard Page class. The left half of Figure 6-7 shows how a default ASPX page called MyWebPage inherits from the Page class directly. The right half shows a situation where the ASPX page inherits from a class called BasePage, which in turn inherits from Page.
Figure 6-7
To have your pages inherit from this base page, you need to do two things: ➤➤
Create a class that inherits from System.Web.UI.Page in the App_Code folder of your web site.
➤➤
Make the web pages in your site inherit from this base page instead of the standard Page class.
In an upcoming exercise you create a new base page class inside the App_Code folder. For now, the sole purpose of this class is to check the Title of the page at runtime to stop pages with an empty title or a meaningless title like “Untitled Page” making it to the browser. Giving your pages a unique and helpful title helps your pages to be found by the major search engines, so it’s recommended to always include a title in your web pages. Checking the title programmatically is relatively easy to do, which enables you to focus on the concept of inheritance rather than on the actual code. In the section that discusses themes later in this chapter you modify the base page once more, this time to retrieve the user’s preference for a theme.
NOTE Previous versions of VWD used “Untitled Page” as the default title for new Web Forms. However, starting with the Service Pack 1 release of VWD 2008, the new default title is now an empty string. I decided to leave the check for “Untitled Page” in the base page for cases where you add older pages with an incorrect title to your new ASP.NET 4 web site.
Before you can implement the base class, you need to know more about the ASP.NET page life cycle, an important concept that describes the process a web page goes through when requested by a browser.
An Introduction to the ASP.NET Page Life Cycle When you think about how a page is served by a web server to the browser and think of this process as the life cycle of a page, you can probably come up with a few important moments in the page’s life. For example, the initial request by the browser is the starting point for the page’s “life.” Similarly, when the page has sent its entire HTML to the browser, its life may seem to end. However, more interesting events are going on in the page’s life cycle. The following table describes eight broad
Using a Centralized Base Page ❘ 209
phases the page goes through. Within each phase, at least one event is raised that enables a page developer to hook into the page’s life cycle and perform actions at the right moment. You see an example of this in the next exercise. Phase
Description
Page request
A request to an ASPX page starts the life cycle of that page. When the web server is able and allowed to return a cached copy of the page, the entire life cycle is not executed. In all other situations, the page enters the start phase.
Start
In this phase, the page gets access to properties like Request and Response that are used to interact with the page’s environment. In addition, during this phase the PreInit event is raised to signal that the page is about to go into the initialization phase. You use this event later to set the theme of a page.
Page initialization
During this phase, the controls you have set up in your page or added programmatically become available. Additionally, the Page class fires three events: Init, InitComplete, and PreLoad. Also during this phase, the control properties are loaded from View State and Control State again during a postback. So, for example, when you change the selected item in a DropDownList and then cause a postback, this is the moment where the correct item gets preselected in the drop-down list again, which you can then work with in your server-side code.
Load
During this phase the page raises the Load event.
Validation
In the validation phase, the Validation controls used to validate user input are processed. You learn about validators in Chapter 9.
Postback event handling
During this phase, the controls in your page may raise their own events. For example, the DropDownList may raise a SelectedIndexChanged event when the user has chosen a different option in the list. Similarly, a TextBox may raise the TextChanged event when the user has changed the text before she posted back to the server. When all event processing is done, the page raises the LoadComplete event. During this phase the PreRender event is raised to signal that the page is about to render to the browser. Shortly after that, SaveStateComplete is raised to indicate that the page is done storing all the relevant data for the controls in View State.
Rendering
Rendering is the phase where the controls (and the page itself) output their HTML to the browser.
Unload
The unload phase is really a clean-up phase. This is the moment where the page and controls can release resources like database connections. During this phase, the Unload event is raised so you can handle any cleanup you may need to do.
210 ❘ Chapter 6 Creating Consistent Looking Web Sites
One thing that’s important to realize is that all these events fire at the server, not at the client. So, even if you change, say, the text of a text box at the client, the TextChanged event of the TextBox control will fire at the server after you have posted back the page. Now you may wonder why you need to know all of this. The biggest reason to have some understanding of the page life cycle is that certain actions can only be performed at specific stages in the page life cycle. For example, dynamically changing the theme has to take place early in the page’s life cycle, as you’ll see later. To really understand the ASP.NET page life cycle, you need to know a little more about controls, state, events, and so on. Therefore, you’ll revisit the page life cycle again in Chapter 15 where you get a good look at all the different events that fire, and in what order. In the next exercise, you use the PreRender event of the Page class to check the title. Because a developer could set the page’s title programmatically during many events, checking for a correct title should be done as late as possible in the page’s life cycle, which is why PreRender is the best event for this, as shown in the next exercise.
Implementing the Base Page Implementing a base page is pretty easy: all you need to do is add a class file to your App_Code folder, add some code to it, and you’re done. What’s often a bit more difficult is to make sure each page in your site inherits from this new base page instead of from the standard System. Web.UI.Page class. Unfortunately, there is no way to configure the application to do this for you automatically when using Code Behind, so you’ll need to modify each page manually. Visual Web Developer makes it a little easier for you by enabling you to export a page template that already contains this code. In the next exercise you add a base page to the site and in a later exercise you see how to export a page to a template so you can add files that use the base page in no time.
Try It Out
Creating a Base Page
1.
Right-click the App_Code folder in the Solution Explorer and choose Add New Item. Select Class in the Templates list and name the file BasePage. You can choose any name you like but BasePage clearly describes the purpose of the class, making it easier to understand what it does.
. 2
Clear the contents of the file, and then add the following code: VB.NET
Public Class BasePage Inherits System.Web.UI.Page Private Sub Page_PreRender( ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender If Me.Title = “Untitled Page” Or String.IsNullOrEmpty(Me.Title) Then Throw New Exception( “Page title cannot be “”Untitled Page”“ or an empty string.”) End If End Sub End Class
Using a Centralized Base Page ❘ 211
C#
using System; using System.Web; public class BasePage : System.Web.UI.Page { private void Page_PreRender(object sender, EventArgs e) { if (this.Title == “Untitled Page” || string.IsNullOrEmpty(this.Title)) { throw new Exception( “Page title cannot be \”Untitled Page\” or an empty string.”); } } public BasePage() { this.PreRender += new EventHandler(Page_PreRender); } }
3.
Save the file and close it, and then open the Login.aspx page that you created earlier. Open its Code Behind file and change the Inherits code (the colon [:] in C#) so the login page inherits from the BasePage class you created earlier: VB.NET
Partial Class Login Inherits BasePage ... End Class C#
public partial class Login : BasePage { ... }
4.
Save the page and then request it in the browser by pressing Ctrl+F5. If you haven’t changed the title of the page earlier, you should be greeted by the error shown in Figure 6-8 in your browser. Instead of this generic error, you may see an error that displays the source for the BasePage class where the title is checked.
. 5
Go back to VWD and open the login page in Markup View. Locate the Title attribute in the @ Page directive (or add one if it isn’t there) and set its value to Log in to Planet Wrox. The following snippet shows the VB.NET version of the @ Page directive but the C# version is almost identical:
212 ❘ Chapter 6 Creating Consistent Looking Web Sites
Figure 6-8
6.
7.
Repeat steps 3 and 5 for all the pages in your site. To make this a bit quicker, you can use Find and Replace to quickly replace all the occurrences of System.Web.UI.Page with BasePage. Make sure you don’t accidentally replace it in the BasePage file in the App_Code folder itself. To prevent this from happening, make sure you search only in Code Behind files, like this: ➤➤
Open the Replace in Files dialog box (press Ctrl+Shift+H or select Edit ➪ Find and Replace ➪ Replace in Files).
➤➤
In the Find What box enter System.Web.UI.Page. In the Replace With text box enter BasePage.
➤➤
Expand the Find Options section and in the Look at These File Types text box enter *.aspx.vb or *.aspx.cs depending on the language you use. This leaves the BasePage file, which has a single extension of .vb or .cs, alone.
➤➤
Click Replace All and then click Yes to confirm the Replace operation.
Save the changes you made to any open page and then browse to Login.aspx again. If everything worked out as planned, the error should be gone and you now see the Login page. Remember, though, that all other pages in your site now throw an error when you try to access them. The fix is easy; just give them all a valid Title. For pages without a Title attribute in their page directive, you need to do this manually. For other pages, with an empty Title=”“ attribute, you can quickly do this by searching the site for Title=”“ and replacing it with something like Title=”Planet Wrox”. (Don’t forget to reset the Look At These File Types back to *.*). For pages other than the demo pages you’ve created so far, you’re better off giving each page a unique title, clearly describing the content it contains.
Using a Centralized Base Page ❘ 213
How It Works By default, all pages in your web site inherit from the Page class defined in the System.Web.UI namespace. This gives them the behavior required to make them act as web pages that can be requested by the browser and processed by the server. Because the inheritance model in .NET enables you to create a chain of classes that inherit from each other, you can easily insert your own base page class between a web page and the standard Page class. You do this by changing the Inherits statement (the colon [:] in C#) to your new BasePage: VB.NET
Partial Class Login Inherits BasePage C#
public partial class Login : BasePage
Inside this new BasePage class you add an event handler that is called when the class fires its PreRender event. As you learned earlier, this event is raised quite late in the page’s life cycle, when the entire page has been set up and is ready to be rendered to the client: VB.NET
Private Sub Page_PreRender( ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender ‘ Implementation here End Sub C#
Note that Visual Basic uses the Handles keyword to tell the compiler that the Page_PreRender method will be used to handle the event. In C#, you need to hook up this handler manually. A good place to do this is in the class’s constructor: public BasePage() { this.PreRender += new EventHandler(Page_PreRender); }
This highlighted line of code serves the same purpose as the Handles keyword in VB.NET: it tells the compiler what method to run when the page raises its PreRender event.
214 ❘ Chapter 6 Creating Consistent Looking Web Sites
Inside the event handler, the code checks the current page title. If the page title is still Untitled Page or an empty string (the default for any new page you add to your web project) it throws an exception. VB.NET
If Me.Title = “Untitled Page” Or String.IsNullOrEmpty(Me.Title) Then Throw New Exception( “Page title cannot be “”Untitled Page”“ or an empty string.”) End If C#
if (this.Title == “Untitled Page” || string.IsNullOrEmpty(this.Title)) { throw new Exception( “Page title cannot be \”Untitled Page\” or an empty string.”); }
Notice how the keywords Me (in VB.NET) and this (in C#) are used. These keywords are contextsensitive and always refer to the instance of the class where they are used. In this example, Me and this refer to the current instance of the BasePage class. This BasePage instance has a Title property (which it inherits from Page) that can be checked for unwanted values. If it still contains the default title (an empty string) or the text “Untitled Page” the code raises (or throws) an exception. This immediately stops execution of the page so you as a page developer can fix the problem by providing a valid title before the page ends up in public. In Chapter 18 you’ll learn more about exceptions and how to prevent and handle them. To display a double quote (“) in the error message, both languages use a different format. In Visual Basic, you need to double the quotes. In C#, you need to prefix the double quote with a backslash (\) to escape the double quote. In both cases, the error message ends up in the browser with a single pair of double quotes around the error message as you saw in Figure 6-8.
Because every new page you add to the site should now inherit from this new base page, you should create a page template that already has the correct code in its Code Behind and markup, making it easy to add the correct page to the site right from the start.
Creating Reusable Page Templates Visual Web Developer comes with a great tool to export templates for a number of different file types including ASPX pages, class files, and even CSS files. By creating a custom template, you define the code or markup that you need in every file once and then create new files based on this template, giving you a jump start with the file and minimizing the code you need to type. The next exercise shows you how to create your own templates.
Using a Centralized Base Page ❘ 215
Try It Out
Creating a Reusable Page Template
In this exercise you see how to create a template file for all new ASPX pages you add to your site. To avoid conflicts with existing pages in your current site, you create a new temporary page and use that for the template. Afterward you can delete the temporary file. 1.
Add a new Web Form to the site and call it Temporary.aspx. Make sure it uses Code Behind, uses your programming language, and is based on the master page in the MasterPages folder.
. 2
Open the Code Behind of this new page (by pressing F7) and change the Inherits line (the colon in C#) so the page inherits from BasePage instead of from System.Web.UI.Page. Also rename the class from Temporary to $relurlnamespace$_$safeitemname$: VB.NET
Partial Class $relurlnamespace$_$safeitemname$ Inherits BasePage ... End Class C#
public partial class $relurlnamespace$_$safeitemname$ : BasePage { ... }
Make sure you don’t remove any of the existing code, like the using statements or the Page_Load method in the C# version. Don’t worry about any compile errors you may get about unexpected characters like $. Once you start adding pages based on this template, $relurlnamespace$_$safeitemname$ will be replaced by the name of the page you’re adding. . 3
Switch to Markup View, and change the Inherits attribute from Temporary to $relurlnamespace$_$safeitemname$:
You can leave the CodeFile attribute alone; VWD will change it to the right Code Behind file automatically whenever you add a new page to the site. . 4
Optionally, add other code you want to add to your pages by default, like a comment block with a copyright notice.
. 5
Save all changes and then choose File ➪ Export Template. In the dialog box that follows, select Item Template and choose your programming language from the drop-down list at the bottom of the screen, shown in Figure 6-9.
. 6
Click Next and place a check mark in front of Temporary.aspx that you find near the bottom of the list. Click Next again to go to the Select Item References dialog box.
216 ❘ Chapter 6 Creating Consistent Looking Web Sites
Figure 6-9
7.
There is no need to set anything in the Select Item References dialog box now. If you had a web page referencing specific assemblies (.dll files) you could pick them here, so VWD adds the references for you automatically next time you add a file based on this template. In this case, click Next again to go to the Select Template Options screen. Type MyBasePage as the new template name, and optionally type a short note describing the purpose of the template. Figure 6-10 shows the final dialog box.
Figure 6-10
Using a Centralized Base Page ❘ 217
8.
Click Finish to create the template. VWD opens a Windows Explorer showing the new template as a ZIP file. You can close that window, because you don’t need it. If you want to carry out this exercise for both VB.NET and C#, be sure to rename the resulting ZIP file first before you make an export for the second language; otherwise the ZIP file gets overwritten. To rename the file, open Windows Explorer, go to My Documents (Documents in Windows Vista and Windows 7) and then browse to Visual Studio 2010\Templates\ ItemTemplates. You’ll find a file called MyBasePage.zip, which you can rename to something like MyBasePageCS.zip. Note that the file’s location is different from the one you see in Figure 6-10; the output location contains just a copy of the exported template that you can use as a backup.
. 9
Back in VWD, delete the temporary file Temporary.aspx you created. Then right-click the web site in the Solution Explorer and choose Add New Item. Note that your custom template now shows up in the list of templates, shown in Figure 6-11. If you click it, it even shows you the description you gave it earlier.
Figure 6-11
10. Type a new name for the page such as TestPage.aspx and click Add to add it to your site. Look at the markup and the Code Behind of the file and verify that $relurlnamespace$_$safeitemname$ has been renamed to _TestPage to reflect the new name of the page. If everything looks OK, you can delete TestPage.aspx because it’s not used in the Planet Wrox web site.
How It Works When you export the template, Visual Web Developer creates a ZIP file with the necessary files — an ASPX file and a Code Behind file in this exercise. This template is then stored in the ItemTemplates subfolder of the Visual Studio 2010 folder under your Documents folder. Some of the files in the ZIP file contain the placeholder $relurlnamespace$_$safeitemname$. When you add a new file to the site that
218 ❘ Chapter 6 Creating Consistent Looking Web Sites
is based on your template using the Add New Item dialog box, VWD replaces $relurlnamespace$ with the name of the folder (nothing, in the case of a file added to the root of the site) and $safeitemname$ with the actual name of the page. In this exercise, you typed TestPage.aspx as the new name for the page, so you ended up with a class in the Code Behind called _TestPage, which in turn inherits from the global BasePage. The underscore (_) is hardcoded between the two placeholders and is really only needed when adding a Web Form based on this template to a sub folder. However, it’s a valid start of a class identifier so you can safely leave it in for pages at the root of your web site. If you add a file to a sub folder, such as the Demos folder, the class name is prefixed with the name of the folder so you end up with a class called Demos_TestPage. In addition to $relurlnamespace$_$safeitemname$, you can use a few other placeholders. Search the MSDN site at http://msdn2.microsoft.com for the term $safeitemname$ to find the other template parameters. If you need to make a change to the exported template, either redo the entire export process, or manually edit the files in the zip file. With this exported template you now have a very quick way to add pages to your site that inherit from the BasePage class. You don’t need to manually change the Code Behind of the class file or the markup of the page anymore.
Besides master pages and the central BasePage class you have more options to create consistentlooking web sites. One of them is themes.
Themes So far you’ve seen how to create a master page to define the global look and feel of the pages in your site. You also saw how to centralize the behavior of your pages by using a central base page. There are, however, more ways to influence the look and feel of your site: themes and skins. Skins are dealt with later in the chapter because they are a part of themes, which need to be discussed first. A theme is a collection of files that define the look of a page. A theme typically includes skin files, CSS files, and images. You define themes in the special App_Themes folder in the root of your web site. Within this folder you create one or more subfolders that define the actual themes. Inside each subfolder, you can have a number of files that make up the theme. Figure 6-12 shows the Solution Explorer for a web site that defines two themes: Monochrome and DarkGrey. A link to each CSS file in the theme folder is added to your page’s section automatically whenever the theme is active. You see how this works later. The images in the theme folder are referenced from the CSS files. They can be used to change common elements of the web site, such as background images, or images used in bulleted lists or navigation lists.
Figure 6-12
Themes ❘ 219
To create a theme, you need to do the following: ➤➤
Create the special App_Themes folder if it isn’t already present in your site.
➤➤
For each theme you want to create, create a subfolder with the theme’s name, like Monochrome or DarkGrey in Figure 6-12.
➤➤
Optionally, create one or more CSS files that will be part of the theme. Although naming the CSS files after the theme helps in identifying the right files, this is not a requirement. Any CSS file you add to the theme’s folder is added to the page at runtime automatically.
➤➤
Optionally, add one or more images to the theme folder. The CSS files should refer to these images with a relative path as explained later.
➤➤
Optionally, add one or more skin files to the theme folder. Skins enable you to define individual properties (like ForeColor and BackColor) for a specific control that are then applied at runtime.
After you have followed these steps, you can configure your site or an individual web page to make use of this theme. To be able to set up the right theme, you should be aware that there are two types of themes.
Different Types of Themes An ASP.NET page has two different properties that enable you to set a theme: the Theme property and the StyleSheetTheme property. Both of these properties use the themes that you define in the App_Themes folder. Although at first they seem very similar, it’s their runtime behavior that makes the difference. The StyleSheetTheme is applied very early in the page’s life cycle, shortly after the page instance has been created. This means that an individual page can override the settings from the theme by applying inline attributes on the controls. So, for example, a theme with a skin file that sets the BackColor of a button to purple can be overridden by the following control declaration in the markup of the page:
The theme in the Theme property, on the other hand, is applied late in the page’s life cycle, effectively overriding any customization you may have for individual controls.
Choosing Between Theme and StyleSheetTheme Because properties of the StyleSheetTheme can be overridden by the page, and the Theme in turn can override these properties again, both serve a distinct purpose. You should set the StyleSheetTheme if you want to supply default settings for your controls. That is, the StyleSheetTheme can supply defaults for your controls, which can then be overridden at the page level. You should use the Theme property instead if you want to enforce the look and feel of your controls. Because the settings from the Theme cannot be overridden anymore and effectively overwrite any customizations, you can be assured that your controls look the way you defined them in the theme. There is one exception: by setting EnableTheming on the control to False you can disable theming for that control. You see this property and its effect toward the end of the chapter.
220 ❘ Chapter 6 Creating Consistent Looking Web Sites
Applying Themes To apply a theme to your web site, you have three different options: at the page level in the Page directive, at the site level by modifying the web.config file, and programmatically. ➤➤
Setting the theme at the page level: Setting the Theme or StyleSheetTheme property at the page level is easy: just set the relevant attribute in the Page directive of the page:
Replace Theme with StyleSheetTheme to apply a theme whose settings can be overridden by the individual pages. Figure 6-13 shows that as soon as you type Theme=” VWD pops up with a list with all the themes it finds in the App_Themes folder.
Figure 6-13
➤➤
Setting the theme at the site level: To enforce a theme throughout the entire web site, you can set the theme in the web.config file by adding a theme attribute to the <pages> element inside the <system.web> element: <pages theme=”DarkGrey”> ...
Make sure you type theme with all lowercase letters because the XML in the web.config file is case sensitive. ➤➤
Setting themes programmatically: The third and final way to set a theme is programmatically through code. You see how this works in a later exercise.
The next Try It Out exercise shows you how themes work. You create a theme, add the necessary CSS, and then configure the application to use the new theme.
Try It Out
Creating a New Theme for Your Web Site
In this exercise you create two themes: Monochrome and DarkGrey. For each theme, you add the CSS layout, which is applied to the site automatically. You configure the application to use one of the themes and then switch to the other to see the differences. 1.
Add the special App_Themes folder to your web site. To do this, rightclick the web site in the Solution Explorer and choose Add ASP.NET Folder ➪ Theme. This not only creates the App_Themes folder, but immediately creates a subfolder for the theme called Theme1 by default. Type Monochrome as the new name instead. Your Solution Explorer should now look like Figure 6-14. Figure 6-14
Themes ❘ 221
2.
. 3 4.
From the Styles folder, move the Styles.css file into this new Monochrome folder. You can either drag it directly into the new folder or use Ctrl+X to cut the file, click the Monochrome folder, and press Ctrl+V to paste it again. You can leave the empty Styles folder because it’s used again later. To make it clear later where your CSS is coming from, rename the file from Styles.css to Monochrome.css.
Because the main layout is now going to be controlled by the theme, you no longer need the element in the section of the master page pointing to the old CSS file, so you can
remove it. To this end, open the master page, switch to Markup View, and remove the following highlighted line from the code:
5.
The next step is to apply the theme to the entire web site. Open the web.config file from the root of the site and directly inside the <system.web> element, add a <pages> element with the theme attribute: <system.web> <pages theme=”Monochrome”> ...
6.
To test the theme, save all your changes and then request the Default.aspx page in your browser. The design of the site should be identical to how it was.
COMMON MISTAKES If you get an error about an invalid page title, go back to Visual Web Developer and change the Title attribute of the @ Page directive of Default.aspx to “Welcome to Planet Wrox.” If your design doesn’t look as it should, press Ctrl+F5 or Ctrl+R in the browser. This forces a “hard refresh,” which means you get the latest version of the files from the server instead of a cached local copy of the page.
Instead of linking to the CSS file from the master page, the CSS is now included in the page source through the theme set in the web.config file. To see how this works, open the HTML source of the page in the browser. At the top you should see the following code (I altered the layout for better readability): Welcome to Planet Wrox
222 ❘ Chapter 6 Creating Consistent Looking Web Sites
Note that a link to the style sheet from the Monochrome theme folder is injected in the of the page. The ASP.NET runtime does this for every CSS file it finds in the currently active theme folder, so be sure to keep your theme folder clean to avoid unnecessary files from being included and downloaded by the browser. Also note that the is added just right before the closing tag. This ensures that the theme file is included after all other files you may have added yourself (through the master page, for example). This is in contrast to how the styleSheetTheme attribute works. Because this type of theme allows its settings to be overridden, it’s imported at the top of the file, giving room for other CSS files that follow it to change the look and feel of the page. . 7
Return to Visual Web Developer and open the master page file in Design View. Notice how all the design is gone and VWD now shows the basic layout of the page again. Unfortunately, VWD does not display the theme you’ve set using the theme attribute. However, you can overcome this limitation by setting the styleSheetTheme instead. To do this, open the web.config file again, locate the <pages> element you created earlier, and add the following attribute: <pages theme=”Monochrome” styleSheetTheme=”Monochrome“>
8.
Save the changes to web.config, close and reopen the master page, and switch to Design View. You’ll see that VWD now applies the correct styling information to your pages.
9 .
To add another theme to the site, create a new folder under App_Themes and call it DarkGrey. Next, open the folder where you extracted the downloaded code that comes with this book. If you followed the instructions in the Introduction of this book, this folder is located at C:\BegASPNET\Resources. Open the Chapter 06 folder and then the DarkGrey folder. Position the Windows Explorer and VWD side by side and then drag the file DarkGrey.css from Windows Explorer into the DarkGrey theme folder in VWD. If dragging doesn’t work for you, you can use Ctrl+C in Windows Explorer to copy the file, and then use Ctrl+V in VWD to paste the file in the right folder. Your Solution Explorer should now resemble Figure 6-15.
Figure 6-15
You add the images that the CSS file refers to in a later exercise. 10. Open the web.config file once more and change both occurrences of Monochrome to DarkGrey in the <pages> element. Save the changes again and press Ctrl+F5. Instead of the purple Monochrome theme, you’ll now see the site with the DarkGrey theme applied as visible in Figure 6-16. If you don’t see the menu, the main content and the sidebar all next to each other, make sure your browser window is wide enough to display all content. If you don’t see the new theme appear, close all open browsers, ensure you changed web.config correctly and open Default.aspx again. If you still don’t see the theme, press Ctrl+F5 or Ctrl+R in your browser to force it to get a fresh copy from the server. If the new theme is still not displayed, right click the ASP.NET Development Server in the Windows tray bar, and choose Stop. Next, request Default.aspx in the browser again by pressing Ctrl+F5 in VWD.
Themes ❘ 223
Figure 6-16
How It Works In this exercise you first applied the Monochrome theme by changing the <pages> element in the web. config file. When the runtime sees that a theme is active, it scans the associated theme folder for .css files and includes a link to all those files in the page’s section. In the case of the Monochrome theme it finds the file Monochrome.css and adds it to the section automatically. An identical process took place when you changed the theme to DarkGrey. The linked style sheet then influences the way the page is displayed in the browser by changing the layout and colors used in the page. To enable design-time support in Visual Web Developer you need to change the styleSheetTheme in the web.config file as well. The only downside of this is that the relevant CSS file is now included twice: once for the Theme and once for the StyleSheetTheme. Because the exact same file is included twice, it doesn’t affect the layout of the site. All the selectors in the second file simply overrule those in the first. However, if you feel this duplication is a waste of CPU cycles, you should delete the styleSheetTheme attribute from the web.config file when you go live with the application. The layout of the page is changed radically because of the CSS in the DarkGrey.css file. If you want to know what CSS the file contains and what elements of the page it changes, open it up in VWD. It has lots of comments describing each selector in detail.
ASP.NET themes are not limited to just CSS files. As you learn next, themes can also contain images and skin files.
224 ❘ Chapter 6 Creating Consistent Looking Web Sites
Extending Themes Besides CSS files and skins (discussed toward the end of this chapter), a theme can also contain images. The most common use of theme images is referring to them from your CSS. To put this to good use it’s important to understand how CSS refers to images. By design, an image referred to by a CSS selector will be searched for relative to the location of the CSS file, unless you give it a path that starts with a forward slash (/) to indicate the root of the site. Consider, for example, the App_Themes folder depicted in Figure 6-17. To refer to the MenuBackground.jpg file in the Images folder of the Monochrome theme, you can add the following CSS to Monochrome.css: #MenuWrapper { background-image: url(Images/MenuBackground.jpg); } Figure 6-17
If you wanted to refer to an image in the Images folder in the root of the site, you would use this CSS: background-image: url(/Images/MenuBackground.jpg);
Note the leading forward slash in front of the image path to indicate the root of the site. This latter syntax is useful if you want to share images between different themes. Simply put them in a folder outside a specific theme, like an Images folder at the root, and then use this root-based syntax to refer to them. This only works correctly when you have set the Virtual Path property of the web site to a forward slash (/) as well. The next chapter digs a lot deeper into the different forms a URL can take to refer to a resource like an image and explains how to use the Virtual Path property.
Try It Out
Adding Images to Your Theme
In this Try It Out you add the images and CSS files to the site to complete both themes. The images and CSS files are available in the zip file that comes with this chapter’s code download, found at www.wrox. com/go/beginningaspnet4. You will overwrite the file Monochrome.css in the Monochrome theme, so if you made any customizations to that file, create a backup of it first.
1 .
Open Windows Explorer and navigate to the files you extracted from the zip file for this chapter (at C:\BegASPNET\Resources). Open the Chapter 06 folder and then the Monochrome folder. Select the Images folder and the Monochrome.css file.
2.
Drag (or copy and paste) the selected folder and files from Windows Explorer into the Monochrome theme folder in VWD. Click Yes when you’re asked to overwrite Monochrome.css.
3.
Repeat steps 1 and 2, but this time drag (or copy and paste) only the Images folder from the Windows Explorer’s DarkGrey folder into the DarkGrey theme folder in VWD. Your Solution Explorer now looks like Figure 6-17.
Themes ❘ 225
4.
Open up the master page from the MasterPages folder and remove the text Header Goes Here between the tags. Because the header will be filled with an image, you no longer need this text.
5.
Request the Default.aspx page in your browser by right-clicking it and choosing View in Browser. You should now see the web page with images from the DarkGrey theme, shown in Figure 6-18.
Figure 6-18
6.
Go back to VWD, open the web.config file, and switch the two theme attributes of the <pages> element from DarkGrey to Monochrome again. Open Default.aspx in your browser and you’ll see the page with the new theme and images as shown in Figure 6-19.
Figure 6-19
226 ❘ Chapter 6 Creating Consistent Looking Web Sites
How It Works From a theme point of view, not much has changed in this exercise. Just as you saw before, the theme is added to the head of the page. However, this time the style sheet in the theme points to images located in the theme folder. The browser reads the CSS file, follows the link to the images, downloads them, and then displays them at the right location as dictated by the various CSS selectors in the code file. The CSS files you added for both themes contain a lot of comments, so if you want to know what the CSS does, check out the files in the two theme folders.
Useful as themes may be to enable you, the page developer, to quickly change the appearance and even the layout of the site, they become even more useful if you let your users switch them at runtime. This way, users can customize the site to their liking. The next section shows you how to accomplish this.
Dynamically Switching Themes Switching themes at runtime has a few benefits. For example, you can please your users by enabling them to choose a theme with colors and layout they like. Not everyone appreciates a dark background with white text, so the option to change that at runtime is something that many people like. However, you can also deploy themes to help visually impaired users. By creating a theme that has a high-contrast color scheme and a large font size, you make it much easier for people to see your site. The themes in the Planet Wrox web site only change screen elements like colors and layout, but it’s easy to create a copy of one of those themes and then change the font size and the color scheme. Because of the way themes are applied to a page at runtime, you need to set the theme early on in the page’s life cycle, in the PreInit event to be precise. The base page of the web site is once again the ideal location to do this, because every page in the site inherits from this class. To enable users to change the theme, you can offer them a drop-down menu that automatically posts back to the server when they change the active option in the list. At the server, you get the chosen theme from the list, apply it to the page, and then store the selection in a cookie so it can be retrieved on subsequent visits to the web site. Cookies are little pieces of text that you can store on the user’s computer. The data you store in a cookie is sent only to the server that set it in the first place, so other sites can’t read the cookie from yours. However, because cookies are stored on the user’s computer as plain text, you should never use them to store any sensitive data, such as a password. Storing harmless data like the preferred theme is an excellent use of cookies, though. In the following two exercises you see how to implement the functionality to switch themes dynamically. The first exercise guides you through modifying the master page to enable the user to select a theme. This exercise only retrieves the name of the theme the user selects and stores it in a cookie. The second exercise then shows you how to apply that theme at runtime to every page that inherits from BasePage.
Themes ❘ 227
NOTE There has been a lot of debate about cookies and whether or not they can harm your privacy. Generally, cookies are safe, because they only store data that the server that sets it already has. They can’t be used to steal sensitive data from your computer if you haven’t given this data to the server yourself. In most scenarios, cookies improve the user’s browsing experience by remembering little pieces of data instead of asking you every single time you visit a page. Unfortunately, some large corporations like advertising agencies use a unique cookie to track your trails on the web, giving them some global idea of the sites you visit. To ensure that visitors to your site understand what information you have and keep about them, it’s usually a good idea to add a privacy statement to your site describing the intent and usage of cookies and any personal data you may keep.
Try It Out
Letting the User Select a Theme
In this exercise you add a DropDownList control to the master page. This control contains the available themes so a user can choose one. The user’s choice is stored in a cookie so it’s available again later. The final step is to preselect the right theme in the drop-down list when the user revisits the page. 1.
Open the master page in Markup View and locate the called Sidebar. Remove the static text Sidebar Goes Here and replace it with a DropDownList control by dragging it from the Toolbox between the two div tags. Change the ID of the control from DropDownList1 to ThemeList. Type some text (for example, Select a Theme) followed by a line break ( ) in front of the drop-down list to clarify the purpose of the list.
. 2 . 3
Switch to Design View, open the control’s Smart Tasks panel, and select Enable AutoPostBack.
4 .
On the same Smart Tasks panel, click the Edit Items link and insert two items: one with the text Monochrome and one with the text DarkGrey. Double-click the drop-down list to set up an event handler for the SelectedIndexChanged event. Instead of double-clicking, you can also select the DropDownList, press F4 to open its Properties Grid, click the button with the lightning bolt to switch to the Events tab, and double-click SelectedIndexChanged. Figure 6-20 shows the Properties Grid in Events mode. Any code you write in the SelectedIndexChanged handler Figure 6-20 fires at the server when the user makes a new selection in the drop-down list at the client. Within the handler block, add the following bolded code that retrieves the selected theme from the list and stores it in a cookie: VB.NET
Protected Sub ThemeList_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ThemeList.SelectedIndexChanged
228 ❘ Chapter 6 Creating Consistent Looking Web Sites
Dim preferredTheme As HttpCookie = New HttpCookie(“PreferredTheme”) preferredTheme.Expires = DateTime.Now.AddMonths(3) preferredTheme.Value = ThemeList.SelectedValue Response.Cookies.Add(preferredTheme) Response.Redirect(Request.Url.ToString()) End Sub C#
Still in the Code Behind of the master page, you need to add some code that preselects the correct item in the list again when the page loads. The best place to do this is in the Page class’s Load event. If you’re using C#, the Page_Load handler should already be there. When you’re using Visual Basic you can add one in two different ways: either double-click the page anywhere in Design View (this works in C# as well), or select (Page Events) from the left drop-down list just above the Document Window in the Code Behind (shown in Figure 6-21), and then choose Load from the second drop-down. This is a nice way to add handlers for other controls as well, like Button and DropDownList controls.
Figure 6-21
Within the handler block that VWD added for you, add the following code: VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load If Not Page.IsPostBack Then Dim selectedTheme As String = Page.Theme Dim preferredTheme As HttpCookie = Request.Cookies.Get(“PreferredTheme”) If preferredTheme IsNot Nothing Then selectedTheme = preferredTheme.Value End If
Themes ❘ 229
If Not String.IsNullOrEmpty(selectedTheme) AndAlso ThemeList.Items.FindByValue(selectedTheme) IsNot Nothing Then ThemeList.Items.FindByValue(selectedTheme).Selected = True End If End If End Sub C#
Save all changes and then request Default.aspx in your browser again. The drop-down list in the sidebar should display the first item in the list as selected. Select the other option from the list and the page will reload. The item you chose last in the drop-down list should now be preselected in the drop-down list. Notice that you keep seeing the same theme because you haven’t written any code yet that applies the selected theme. You see how to do that in a later exercise.
COMMON MISTAKES If you get an error, make sure you have no typos in the code. If nothing seems to happen (for example, the page doesn’t post back), check if you set the AutoPostBack attribute on the DropDownList control to True. If you don’t see any theme applied at all, check the <pages> element in web.config for a configured theme. Also, check the spelling of the name of the cookie (PreferredTheme) in both code blocks.
How It Works You made three important changes to the master page. First, you added the drop-down list and set AutoPostBack to True. This causes the page to submit itself back to the server as soon as you choose a new item in the list. When that happens, the code in the SelectedIndexChanged handler fires. This code creates a cookie that can be stored on the user’s computer. To make the cookie last between browser sessions, you need to set the Expires property. In the code example, the cookie is set to expire three months
230 ❘ Chapter 6 Creating Consistent Looking Web Sites
from now, which means the browser will discard it automatically after that period. However, whenever the user chooses a new theme, this date is extended, setting it for another three months: VB.NET
Dim preferredTheme As HttpCookie = New HttpCookie(“PreferredTheme”) preferredTheme.Expires = DateTime.Now.AddMonths(3) C#
HttpCookie preferredTheme = new HttpCookie(“PreferredTheme”); preferredTheme.Expires = DateTime.Now.AddMonths(3);
After the cookie has been created, you can set its Value property. In the example, the SelectedValue of the DropDownList (containing the name of the theme) is stored in the cookie. The cookie is then added to the Cookies collection using Response.Cookies.Add: VB.NET
Note that the cookie is added to the Cookies collection of the Response object that is associated with the response to the user. Later you see how to read this cookie again from the Cookies collection of the Request object that is associated with the request the user is making for a page. The final step is to redirect the user to the same page: VB.NET
Response.Redirect(Request.Url.ToString()) C#
Response.Redirect(Request.Url.ToString());
This is necessary because otherwise the new theme won’t be applied immediately. Because the theme needs to be set early in the page’s life cycle, it can no longer be set for the current request. By redirecting the user to the same page, a new request is made that can successfully apply the selected theme. The next exercise shows you the code to programmatically set the selected theme. The final change in the master page you made was a modification to the Page_Load handler. Inside this method, a String variable is declared that holds the currently active theme by looking at Page.Theme. This will be the default theme that is preselected in the drop-down list if the user doesn’t have a cookie holding her preferred theme. The code then sees if there is a cookie called PreferredTheme. If it exists, its value is used to give the string selectedTheme a new value. In the end, this String variable is then used to find the item in the drop-down list and preselect it. This way, the drop-down list always displays the currently configured site theme or the item the user has chosen manually, even if she comes back to the site next week. Note the use of the FindByValue method on the Items collection of the DropDownList control. This method returns the item if it is found or Nothing (null in C#) when the item isn’t there. This ensures that if the cookie contains a theme that is no longer available, the code doesn’t try to preselect an item in the list that doesn’t exist.
Themes ❘ 231
With the ability to let a user select a theme in place, the next step is to apply the chosen theme. As you learned previously, the theme needs to be set in the PreInit event, which takes place early in the page’s life cycle. Inside this event, you can see if the cookie with the selected theme exists. If it does, you can use its value to set the right theme.
Try It Out
Applying the User-Selected Theme
In this exercise, you modify the base page and add some code for the PreInit event to set the user’s theme.
1 .
Open the BasePage file from the App_Code folder and add the following code that sets the selected theme during the PreInit event. You can add this code before or after the method that checks the page title. VB.NET
Private Sub Page_PreInit(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.PreInit Dim preferredTheme As HttpCookie = Request.Cookies.Get(“PreferredTheme”) If preferredTheme IsNot Nothing Then Page.Theme = preferredTheme.Value End If End Sub C#
If you’re working with C#, you need to set up an event handler in the class’s constructor for the PreInit event, just as you did with the PreRender event handler in an earlier exercise. This tells the ASP.NET runtime which method will handle the PreInit event: public BasePage() { this.PreRender += new EventHandler(Page_PreRender); this.PreInit += new EventHandler(Page_PreInit); }
3.
Save changes to all open documents and then request Default.aspx in the browser. The page should load with the theme you chose last in the drop-down list in the previous exercise.
. 4
Choose a new item from the list. The page should reload and should now show the other theme. If you find that the page in the browser is showing a combination of the two themes, go back to VWD, open web.config, and remove the styleSheetTheme attribute from the <pages> element, leaving the theme attribute in place because it serves as the default for new visitors.
232 ❘ Chapter 6 Creating Consistent Looking Web Sites
How It Works With the hard work of getting the user’s favorite theme and storing it in a cookie already done, applying the theme is now very easy. The code in the PreInit event handler first verifies whether there is a cookie called PreferredTheme. It does this by comparing the return value of the Get method to Nothing (null in C#). VB.NET
Dim preferredTheme As HttpCookie = Request.Cookies.Get(“PreferredTheme”) If preferredTheme IsNot Nothing Then C#
HttpCookie preferredTheme = Request.Cookies.Get(“PreferredTheme”); if (preferredTheme != null)
Here, Request.Cookies is used to read from the cookies that the user’s browser sent together with the request. If the cookie exists, its Value property is used to set the correct theme: VB.NET
Page.Theme = preferredTheme.Value C#
Page.Theme = preferredTheme.Value;
Because the theme is set early in the page’s life cycle, this setting will carry through all the way to the end of the page, effectively giving the page the look and feel defined in it. Note that this code causes the page to crash when the cookie contains a theme that is no longer present in the site. In Chapter 18, you will learn more about the techniques to handle these kinds of scenarios and deal with them gracefully.
With the ability of setting the theme programmatically, you’re offering your users a quick and easy way to change the page to their likings. The theme affects colors and layout throughout each page in the entire web site. Combined with master pages this gives you a flexible way to influence the look and feel of an entire page. It could also be useful if you were able to change certain controls on a page. For example, you may have the need to give each button in your site the exact same look. This is where the ASP.NET skins come into play.
Skins Skins are simple text files that contain markup that enables you to define the look and feel of one or more server controls from a central location. Placed in a theme’s folder, they are an integral part of the ASP.NET themes feature. A skin file (with a .skin extension) contains the server-side presentational elements of a control. These settings are then applied to all the controls to which the skin applies. To see how this works, consider the following example that defines the skin or appearance of a Button control:
With this skin definition, the buttons in your site will get a BackColor of #cccccc and a ForeColor of #308462. All you need to do is create a skin file under your theme’s folder, add this markup
Skins ❘ 233
to it, and that’s it. From then on, all the buttons will be changed automatically. Just as with setting the properties on the controls directly as you saw earlier, these properties like BackColor and ForeColor are transformed into client-side HTML and CSS. Note that this skin markup is similar to the markup of a button. There are a few differences though. First of all, the control in the skin file cannot have an ID attribute. The ID is used to uniquely identify a control in a page, and because the skin is applied to all controls, there’s no point in giving it an ID. Another difference is the number of attributes you can set in the markup. Not all properties of a control are skinnable. For example, you can’t set the Enabled property of the Button through a skin. Microsoft’s MSDN documentation lists for each property whether or not they can be skinned. Another way to find out if you can skin a certain property is by simply trying it: just set the property in the skin and if you’re not allowed to set it, you’ll get an error at runtime. Generally speaking, properties that influence the appearance (BackColor, ForeColor, BorderColor, and so on) can be skinned and properties that influence behavior (Enabled, EnableViewState, and more) cannot be set. When you create a new skin file using the Add New Item dialog box, you get a bunch of text wrapped in a server-side comment block. You can safely remove these comments because they only give you a short example of how skins work. You can define multiple controls in a single skin file. However, from a maintainability point of view, it’s often easier to name each skin file after the control it represents. For example, you would have a file called Button.skin for buttons, Label.skin for labels, and so on. Instead of applying formatting elements directly to the control’s properties in the skin and thus to the final markup in the page, it’s often better to use the CssClass property to point to a CSS class in one of your CSS files. That way, it’s even easier to make site-wide changes and you avoid bloating the final HTML. Given the previous example, a file with the following skin definition and a class in the theme’s CSS file would give the same effect: .MyButton { color: #308462; background-color: #cccccc; }
Creating a Skin File Skin files must be created in the theme’s folder directly. You can’t store them in a subfolder like you do with the theme’s images. In the following exercise you see how to create a simple skin file to change the look and feel of all button controls in the web site. Later chapters in this book build on this knowledge by defining more complex skins for other controls like the GridView. When you start typing in a skin file, you’ll notice that the familiar IntelliSense doesn’t kick in. This makes it slightly difficult to define your controls and their attributes. However, there is a simple workaround:
1. 2.
Open VWD’s Options dialog box by choosing Tools ➪ Options. Expand the Text Editor category and click File Extension. If you don’t see these categories, make sure that Show All Settings at the bottom of the screen is selected.
234 ❘ Chapter 6 Creating Consistent Looking Web Sites
3.
In the Extension box type skin and then from the Editor drop-down list choose User Control Editor.
4.
Click the Add button and then click the OK button to dismiss the Options dialog box.
From now on, you’ll get IntelliSense in skin files (you may need to reopen existing skin files first if you already created one). With this setting on, you may get a warning in the Error List about build providers when you have a skin file open. You can safely ignore this error, because skins work fine at runtime even with these settings in VWD.
Try It Out
Creating a Skin for the Button Control
To effectively use skins, you should strive to use CssClass attributes as much as possible instead of applying inline attributes that all end up in the final HTML of the page, increasing its size and load time. However, to show you how it works in case you do have a special need to add inline attributes, this exercise shows you how to apply both.
1 .
In the Monochrome theme folder, add a new skin file and call it Button.skin. You add the file by right-clicking the Monochrome folder and choosing Add New Item. In the dialog box that follows select Skin File and type Button as the file name.
. 2
Delete the entire contents from the file and type the following code:
Note that this markup uses a combination of inline attributes for styling (the BackColor) and the CssClass to point to a selector in your CSS file. As explained earlier, you can ignore the warning about missing build providers because your skin files will work fine at runtime. As soon as you close the skin file, the warning goes away. 3.
Open the Monochrome.css file from the theme folder and add this CSS selector at the end of the file: .MyButton { color: White; }
4.
Create a new Web Form in the Demos folder and call it SkinsDemo.aspx. Make sure you base it on the exported template you created earlier. Give the page a Title of Skins Demo and then add a Button by dragging it from the Toolbox into the cpMainContent area of the page. You end up with this code:
5.
Save all changes and then request SkinsDemo.aspx in the browser. If necessary, switch to the Monochrome theme. The button you added in step 4 should now have a purple background with white text on it. If the changed colors don’t show up, make sure you selected the right theme in the drop-down list and that you added the MyButton CSS class to the CSS file of the Monochrome theme. If you still don’t see the changes, press Ctrl+F5 or Ctrl+R to force a fresh copy of the CSS file from the server.
Skins ❘ 235
How It Works To see how it works, you should take a look at the HTML for the page in the browser. The Button control has been transformed in the following HTML:
Both the CssClass and the BackColor attributes in the skin have been added to the HTML. The former ended up as a class attribute on the button, and the latter has been transformed into a style attribute. The MyButton class in the CSS file gives the button its white text and the inline style determines the background color of the button. If you choose the DarkGrey theme in the drop-down list and then look at the HTML again, you’ll notice there are no class and style attributes, giving the button its default Windows look.
As you can see, skins are extremely easy to use and enable you to radically change the look of specific controls in your site. But what if you don’t want all your buttons to change purple and white at the same time? What if you need one special button that has a red background? You can do this with named skins.
Named Skins Named skins are identical to normal skins with one exception: they have a SkinID set that enables you refer to that skin by name. Controls in your ASPX pages can then use that SkinID to apply that specific skin to the control. The next exercise shows you how this works.
Try It Out
Creating a Named Skin for the Button Control
The easiest way to create a named skin is by copying the code for an existing one and then adding a SkinID attribute. Be aware that if you copy and paste a skin definition, VWD automatically adds an ID attribute (that is, if you connected skin files to the User Control Editor as described earlier). This ID is not allowed, so you need to remove it.
1 . . 2 3.
If VWD added an ID attribute, remove it, together with its value (for example, remove ID=”Button1”).
4.
Add a SkinID attribute of RedButton. You should end up with this code:
Open Button.skin again, copy all the code, and paste it below the existing markup.
Remove the CssClass attribute and its value, change the BackColor of the button to Red, and set the ForeColor to Black.
5.
Save and close the skin file.
. 6
Open SkinsDemo.aspx and add a second button. Set the SkinID of this button to RedButton. Notice how IntelliSense helps you pick the right SkinID. The code for the two buttons should now look like this:
236 ❘ Chapter 6 Creating Consistent Looking Web Sites
7.
Open SkinsDemo.aspx in the browser. You should now see two buttons with different colors. If you don’t see the different colors, ensure you have selected the Monochrome theme in the browser.
How It Works Named skins work almost exactly the same as normal skins. However, with a named skin a control can point to a specific skin in one of the skin files. In the SkinsDemo.aspx page, the first button gets its settings from the default, unnamed skin, and the other now gets its settings from the skin with its SkinID set to RedButton.
With named skins, you have a very flexible solution at your disposal. With the normal skins, you can quickly change the appearance of all controls in your site. You can then use a named skin to override this behavior for a few controls that you want to look different.
Disable Theming for Specific Controls If for some reason you don’t want to apply a skin to a specific control you can disable the skin by setting the EnableTheming property of the control, like this:
With EnableTheming set to False, the skin is not applied to the control. CSS settings from the theme’s CSS file are still applied, though.
Practical Tips on Creating Consistent Pages The following list provides some practical tips on creating consistent pages: ➤➤
When you create a new web site, always start by adding a master page that you base all other pages on. Even if you think you have a site with only a few pages, a master page will help you ensure a consistent look across the entire site. Adding a master page at a later stage to the site means making a lot of manual changes to existing pages.
➤➤
As soon as you find yourself adding styling information to complex controls like the TreeView and Menu (discussed in the next chapter) or data-aware controls like the GridView (discussed in Chapter 13), consider creating a skin for them. The fact that you can control the layout of all similar controls from a single location makes it a lot easier to update your site. If you want to override the layout for a few controls, you can always use named skins with a SkinID or disable the skin entirely by setting EnableTheming to False.
➤➤
When creating skins or setting style properties directly on a control, consider using the CssClass property instead, and then moving all styling-related properties to the CSS for the site or theme. This decreases the page’s size and makes it easier to make changes to the layout afterward.
➤➤
The Export Template feature of Visual Web Developer is a great time saver. You can use it not only to create a template for an ASPX page and its Code Behind, but also for other files like classes and CSS files, and even a complete web site. This enables you to jump-start the creation of new files, saving you from typing the same stuff over and over again.
Summary ❘ 237
Summary A consistent look and feel of all pages in your site is important to give your site a professional and attractive look. This in turn helps your visitors in finding the right information in your site, increasing the chances that they might visit your site again. ASP.NET 4 offers a number of great tools to aid you in creating a consistent looking web site. ASP.NET master pages and content pages help you create a layout that is repeated in every page that is based on that master. Where master pages define a centralized look and feel, you can use a Base Page class to centralize behavior such as checking the page for invalid titles. Themes are used to change the look and feel of the pages in your site and the controls they contain. Because themes can contain CSS files, images, and skins, you can change colors, fonts, positioning, and images simply by applying a theme. By making good use of techniques like named skins, and the EnableTheming attribute, you can create a design that applies to your entire site, while you maintain the flexibility to overrule the design on a control-by-control basis. The Planet Wrox web site is now starting to grow. This means it becomes more difficult for you and your visitors to find the right pages. The next chapter shows you a number of different ways for your users to navigate your site so they won’t have any problems finding the page they are looking for.
Exercises
1.
What’s the difference between a ContentPlaceHolder and a Content control? In what type of page do you use which one?
2.
How do you hook up a Content control in a content page to the ContentPlaceHolder in the master page?
3.
Imagine you have created a skin that gets applied to all buttons in your site with the following skin definition:
The imaginary CSS class MyButton sets the background color of the button to black and the foreground color to white. To draw attention to a specific button in a page, you decide to give it a red background instead. Which options do you have to control the look of this single button?
4.
Explain the differences between setting the Theme property and the StyleSheetTheme property for a page.
5.
Name three different ways to set the Theme property for a page and explain the differences between the options.
6.
What’s the main reason for implementing a base page in your web site? A nswers to Exercises can be found in Appendix A.
238 ❘ Chapter 6 Creating Consistent Looking Web Sites
⊲⊲ What You Learned in This Chapter Base page
A class inheriting from the ASP.NET Page class that serves as the parent class for your ASPX pages
Content page
An ASPX Web Form that uses a master page to build up its global appearance and layout
Cookies
Little pieces of text that you can store on the user’s computer and access again from the server
Master page
A central page that defines the look and feel of content pages that use the master page
Named skin
An ASP.NET skin with an explicit SkinID set enabling you to refer to this skin by its ID
Page life cycle
The cycle of events that an ASPX page goes through when requested by a browser
Skin
A collection of presentational settings to influence the appearance of controls in the browser
Theme
A collection of CSS styles, skins and images to change the appearance of pages in your site
7
Navigation What You Will Learn in This Chapter: ➤➤
How to move around in your site using server controls and plain HTML
➤➤
How to address pages and other resources like images
➤➤
How to use the ASP.NET Menu, TreeView, and SiteMapPath navigation controls
➤➤
How to programmatically send users from one page to another
When your site contains more than a handful of pages, it’s important to have a solid and clear navigation structure that enables users to find their way around your site. By implementing a good navigation system, all the disconnected web pages in your project will form a complete and coherent web site. When you think about important parts of a navigation system, the first thing that you may come up with is a menu. Menus come in all sorts and sizes, ranging from simple and static HTML links to complex, fold-out menus driven by CSS or JavaScript. But there’s more to navigation than menus alone. ASP.NET comes with a number of useful navigation controls that enable you to set up a navigation system in no time. These controls include the Menu, TreeView, and SiteMapPath, which you learn about in this chapter. Besides visual controls like Menu, navigation is also about structure. A well-organized site is easy for your users to navigate. The Web.sitemap file that is used by the navigation controls helps you define the logical structure of your site. Another important part of navigation takes place at the server. Sending a user from one page to another in Code Behind based on some condition is a very common scenario. For example, imagine an administrator entering a new CD or concert review in the Management section of the web site. When the review is done, you may want to show the administrator the full details by redirecting to a new page.
240 ❘ Chapter 7 Navigation
In this chapter, you learn how to use the different navigation options at your disposal. Before you look at the built-in navigation controls, however, you need to understand the different options you have to address the resources in your site like ASPX pages and images.
Different Ways to Move Around Your Site The most common way to let a user move from one page to another is by using the element. This element has an href attribute that enables you to define the address of a page or other resource you want to link to. Between the tags you can place the content you want to link, such as text, an image, or other HTML. The following snippet shows a simple example of the element: You can log in here
With this code in a web page, users, after clicking the text “You can log in here,” will be taken to the page Login.aspx that should be in the same folder as the page that contains the link. The element has a server-side counterpart called the HyperLink, which can be created in the markup using . It eventually ends up as an element in the page. The NavigateUrl property of this control maps directly to the href attribute of the element. For example, a server-side HyperLink in a content page such as this: You can log in here
produces the following HTML in the browser: You can log in here
Other than the long ID that is assigned by the ASP.NET runtime, this code is identical to the earlier example. In both cases, the href attribute points to the page Login.aspx using a relative URL. The next topic describes the differences between relative and absolute URLs.
Understanding Absolute and Relative URLs Key to working with links in your site is a good understanding of the different forms a Uniform Resource Locator (URL) to a resource inside or outside your web site can take. A URL is used to uniquely identify a resource in your or another web site. These URLs are used in different places, including the href attribute of a hyperlink or a element to point to a CSS file, the src attribute pointing to an image or a JavaScript source file, and the url() value of a CSS property. These URLs can be expressed as a relative URL or as an absolute URL. Both have advantages and disadvantages that you should be aware of.
Relative URLs In the previous examples you saw a relative URL that points to another resource relative to the location where the URL is used. This means that the page containing the element and the Login. aspx page should both be placed in the same folder in your site. To refer to resources in other folders you can use the following URLs. All the examples are based on a site structure shown in Figure 7-1.
Different Ways to Move Around Your Site ❘ 241
To link from Login.aspx in the root to Default.aspx in the Management folder you can use this URL: Management
To refer to the image Header.jpg from Default.aspx in the Management folder you can use this URL:
The two leading periods “navigate” one folder up to the root, and then the path goes back in the Images folder to point to Header.jpg. For a deeper folder hierarchy, you can use multiple double periods, one Figure 7-1 for each folder you want to go upward in the site hierarchy, like this element, which can be used to refer to the same image from pages in the Reviews folder, which is located under the Management folder:
One benefit of relative URLs is that you can move a set of files around to another directory at the same level without breaking their internal links. However, at the same time, they make it more difficult to move files to a different level in the site hierarchy. For example, if you moved the page Login.aspx to a separate folder like Members, the link to the Management folder would break. The new Members folder doesn’t have Management as its subfolder so Management/Default.aspx is no longer a valid link. To overcome this problem, you can use root-based relative URLs.
Root-Based Relative URLs Root-based relative URLs always start with a leading forward slash to indicate the root of the site. If you take the link to the Management folder again, its root-based version looks like this: Management
Note the leading forward slash in front of the Management folder to indicate the root of the web site. This link is unambiguous. It always points to the file Default.aspx in the Management folder in the root. With this link, moving the Login.aspx page to a subfolder doesn’t break it; it will still point to the exact same file.
Relative URLs in Server-Side Controls With ASP.NET Server Controls you have another option at your disposal to refer to resources in your web site: you can use the tilde (~) character to point to the current root of the site. To understand what problems this tilde solves you need to be aware of the way Visual Web Developer creates new web sites and hooks them up to the built-in web server. When you create a new web site, VWD by default creates a site in a separate application folder under the built-in web server where all your pages are created. So, for example, when you create a new site and press Ctrl+F5 to open the default page in your browser you end up with an address similar to http://localhost:23143/MySiteName. Usually when you put your site live on a remote server, you don’t want to have this application folder
242 ❘ Chapter 7 Navigation
anymore. Instead, users browse to a URL such as http://www.PlanetWrox.com and expect to see the site. Fortunately, there’s an easy way to stop VWD from creating this separate folder: you can set the Virtual Path property of the project to a forward slash (/) using the Properties Grid for the web site. To see how this setting influences the way you refer to files, the following Try It Out shows you how to create a new web site and add some images to it that use different URL naming schemes. You then change the virtual path property to see what effect this property has.
Try It Out
Investigating the Behavior of the Virtual Path Property
In this Try It Out, you create a brand new web site that is only used for this exercise. Afterward, you can delete the web site if you want. For this exercise, you also need an image file — for example, the Header.jpg from the previous chapter; any other image will do as well.
1 . . 2
Choose File ➪ New Web Site in VWD to create a new web site. Choose ASP.NET Empty Web Site as the template and set the Web Location to File System. Note that VWD offers a path that ends with WebSite1. If you’ve created other web sites before without giving them an explicit name, you may have a path that ends with WebSite2, WebSite3, and so on. Click OK to create the site.
. 3
Add a new Web Form called Default.aspx to the site (use the default Web Form option and not your custom template) and then add an image to the root of the site called Header.jpg. You can drop one of the images from the previous chapter in the root of the site, or you can use an existing image and add that to the site. If you don’t rename the image to Header.jpg, make sure you adjust the code in the next step.
. 4
In Default.aspx, add the following code to the Markup View that inserts three ASP.NET Image controls using different ways to address the image. The images are separated by a line break:
5.
Press Ctrl+F5 to open the page in the browser. Note that the address bar of the browser reads something like http://localhost:49696/WebSite1/Default.aspx. Your port number and application name may be slightly different, but what’s important to notice is that the web site is located in a separate folder under the web server called localhost. You’ll also find that the second image shows up broken. That’s because the leading slash refers to the root of the web server, so the image is looked for at http://localhost:49696/Header.jpg, which doesn’t exist because the image is located in the WebSite1 subfolder. Open up the source of the page in the browser and look at the three elements:
The first two URLs are identical to what you added to the ASPX page. However, the third one has been modified to refer to an image in the same folder as the page that references the image.
Different Ways to Move Around Your Site ❘ 243
6.
Close your browser and go back to VWD, click the root of the web site in the Solution Explorer, and press F4 to open up the web site’s Properties Grid. Set the Virtual Path from /WebSite1 to / as shown in Figure 7-2.
. 7
Press Ctrl+F5 again to reopen Default.aspx in the browser. The address bar now reads something like http://localhost:49696/Default.aspx. As you can see, the page Default.aspx is now located at the root of the server. Therefore, all three images show up correctly.
. 8
Figure 7-2
Go back to VWD and create a folder called Test. Drag the Default.aspx file from the root of the site into this new folder and then request the page in the browser. This time the first image will be broken. If you look at the HTML source, you’ll see this:
The first element tries to find an image relative to the current document. Because the current document lives in the Test folder and the image is located at the root of the site, this results in a broken image. The other two src attributes point to the correct image in the root of the site. . 9
You can close the test project in VWD now, and delete it from disk because it is no longer needed.
How It Works This example demonstrates that the ~ syntax to indicate the root of the site is often the most reliable way to refer to a resource like an image. In three different cases you saw in this exercise, only the third image showed up correctly every time. When a server control such as the Image control needs to render a path, it finds out what the current application root is and adjusts the path accordingly. This can be a real time saver when you decide to move around folders and files or develop your site using VWD’s default setting of an additional application folder and then try to put your site on a production server that enables you to access files from the actual root of the server. Plain HTML controls can also benefit from this syntax by changing them to server controls. You do this by adding a runat=”server” attribute to the element. You can type the code snippet runat and then press the Tab key once or twice (depending on whether the IntelliSense list is open or not) to quickly add the required attribute to any element. With the attribute, the following element exhibits the same behavior as the third from the previous example:
You won’t be able to use the ~ syntax in pure client HTML elements and CSS files. That is, if you try to use the preceding src attribute without adding runat=”server” to the img element to make it a serverside image or you try to use the ~ syntax in a CSS file or code block, the image won’t show up in the browser. For these scenarios you should use relative linking instead.
244 ❘ Chapter 7 Navigation
The remainder of this book assumes you’ve set the Virtual Path property to a forward slash (/), which makes it easier to refer to your files using URLs without the ~ syntax. If you find that some resources don’t show up correctly, check whether you set the virtual path property of your web site correctly.
Absolute URLs In contrast to relative URLs that refer to a resource from a document or site root perspective, you can also use absolute URLs that refer to a resource by its full path. So instead of directly referring to an image and optionally specifying a folder, you include the full name of the domain and protocol information (the http:// prefix). Here’s an example that refers to the Wrox logo at the Wrox Programmer to Programmer site (http://p2p.wrox.com) where you go for questions regarding this and other Wrox books or for general questions regarding programming:
Absolute URLs are required if you want to refer to a resource outside your own web site. With such a URL, the http:// prefix is important. If you leave it out, the browser will look for a folder called p2p.wrox.com inside your own web site. Absolute URLs are unambiguous. They always refer to a fixed location, which helps you to make sure you’re always referring to the exact same resource, no matter where the source document is located. This may make you think that they are ideal to use everywhere — including references to resources within your own site — but that’s not the case. The extra protocol and domain information adds to the size of the page in the browser, making it unnecessarily slower to download. But more importantly, it creates difficulties if you’re changing your domain name, or if you want to reuse some functionality in a different web site. For example, if you previously had your site running on www.mydomain.com but you’re moving it to www.someotherdomain.com, you will need to update all the absolute URLs in the entire web site. You will also have trouble with absolute URLs during development. Quite often, you test your web site on a URL such as http://localhost. If you were to point all your images to that URL, they would all break as soon as you put your site on a production domain like www.PlanetWrox.com. In short, use absolute URLs with caution. You always need them when referring to resources outside your web site, but you should give preference to relative URLs within your own projects wherever possible.
Understanding Default Documents In the context of URLs you should also know about default documents. When you browse to a site like www.domainname.com you magically see a page appear. How does this work? Each web server has so-called default documents, a list of document names that can be served to a browser when no explicit document name is supplied. So, when you browse to www.domainname.com, the web server scans the directory requested (the root folder in this example) and processes the first file from its default document list it finds. In most ASP.NET scenarios, the web server is set up to use Default.aspx as the default document. So, when you browse to www.domainname.com on an ASP.NET web server, you are actually served the page www.domainname.com/Default.aspx.
Using the Navigation Controls ❘ 245
In the links you create, you should generally leave out Default.aspx when it isn’t needed. It decreases the page size, but more importantly, it makes it easier for your users to type the address. Now that you have seen how you can use URLs to point to documents and other files, it’s time to look at some higher-level controls that make use of these URLs: the ASP.NET navigation controls.
Using the Navigation Controls ASP.NET 4 offers three useful navigation tools: SiteMapPath, TreeView, and Menu. Figure 7-3 shows basic examples of the three navigation controls, without any styling applied. The SiteMapPath on the left shows the user the path to the current page. This helps if users want to go up one or more levels in the site hierarchy. It also helps them to understand where they are. The TreeView can display the structure of your site and enables you to expand and collapse the different nodes; in Figure 7-3 the entire tree is expanded. The Menu control on the right initially only displays the “Home” menu item. However, as soon as you move the mouse over the menu item, a submenu appears. In Figure 7-3 one of these child elements is the Reviews item, which in turn has child elements itself.
Figure 7-3
Although quite different in behavior and appearance, these three navigation controls have part of their design in common.
Architecture of the Navigation Controls To make it easy to show relevant pages in your site using a Menu, a TreeView, or a SiteMapPath, ASP.NET uses an XML-based file that describes the logical structure of your web site. By default, this file is called Web.sitemap. This file is then used by the navigation controls in your site to present relevant links in an organized way. Simply by hooking up one of the navigation controls to the Web.sitemap file you can create complex user interface elements like fold-out menus or a tree view.
246 ❘ Chapter 7 Navigation
Examining the Web.sitemap File By default, you should call the site map file Web.sitemap. This enables the controls to find the right file automatically. For more advanced scenarios you can have multiple site map files with different names, with a configuration setting in the web.config file that exposes these additional files to the system. In most cases, a single site map file will be sufficient. A basic version of the site map file can look like this: <siteMap xmlns=”http://schemas.microsoft.com/AspNet/SiteMap-File-1.0”> <siteMapNode url=”~/” title=”Home” description=”Go to the homepage”> <siteMapNode url=”~/Reviews” title=”Reviews” description=”Reviews published on this site” /> <siteMapNode url=”~/About” title=”About” description=”About this site” />
The site map file contains siteMapNode elements that together form the logical structure of your site. In this example, there is a single root node called “Home,” which in turn contains two child elements, “Reviews” and “About.”
Key Elements of the Web.sitemap File Each siteMapNode can have many child nodes (but there can only be one siteMapNode directly under the siteMap element), enabling you to create a site structure that can be both wide and deep at the same time. The siteMapNode elements in this example have three of their attributes set: url, title, and description. The url attribute should point to a valid page in your web site. You can use the ~ syntax you saw in the previous section to refer to application-root–based URLs. The ASP.NET runtime doesn’t allow you to specify the same URL more than once, but you can work around that by making the URL unique by adding a query string. For example, ~/Login.aspx and ~/Login.aspx?type=Admin will be seen as two different pages. You see more of the query string later in this chapter. The title attribute is used in the navigation controls to display the name of the page. You see more about this later when you work with the Menu, TreeView, and SiteMapPath controls. The description attribute is used as a tooltip for the navigation elements. Figure 7-3 shows a tooltip for the “By Genre” item. The navigation controls work together with the ASP.NET security mechanism. That is, you can automatically hide elements from controls like the Menu that users don’t have access to. Security is described in more detail in Chapter 16. To be able to work with the Web.sitemap file, ASP.NET makes use of the SiteMapDataSource control, which you’ll find under the Data category of the Toolbox. When you use the SiteMapPath control to display a breadcrumb, ASP.NET will find the Web.sitemap file itself. With the other two navigation controls, you need to specify a SiteMapDataSource explicitly as an intermediate layer to the Web.sitemap file. To create a useful Web.sitemap file, you need to add one to your site and then manually add the necessary siteMapNode elements to it. There is no automated way in Visual Web Developer to create a sitemap file based on the current site’s structure, although third-party solutions are available that help you with this.
Using the Navigation Controls ❘ 247
Try It Out
Creating a Web.sitemap File
In this exercise you add a new Web.sitemap file to the site and add a bunch of siteMapNode elements to it. This site map serves as the basis for the other navigation controls in the site. If you already set the Virtual Path property of the web site earlier, you can skip the first step. 1.
Open the Planet Wrox project again in VWD and click the web site in the Solution Explorer to select it. Press F4 to open the Properties Grid and then set the Virtual Path property to a forward slash (/) as shown in Figure 7-2. From now on, it’s assumed that you always run the web site with this root-based URL.
. 2
Right-click the web site in the Solution Explorer, choose Add New Item, and click Site Map. Leave the default name set to Web.sitemap and click Add. You end up with one root element containing two child nodes in the Web.sitemap file.
. 3
Modify the Web.sitemap so it contains this code: <siteMap xmlns=”http://schemas.microsoft.com/AspNet/SiteMap-File-1.0”> <siteMapNode url=”~/” title=”Home” description=”Home”> <siteMapNode url=”~/Default.aspx” title=”Home” description=”Go to the homepage” /> <siteMapNode url=”~/Reviews/Default.aspx” title=”Reviews” description=”Reviews published on this site”> <siteMapNode url=”~/Reviews/AllByGenre.aspx” title=”By Genre” description=”All Reviews Grouped by Genre” /> <siteMapNode url=”~/Reviews/All.aspx” title=”All Reviews” description=”All Reviews” /> <siteMapNode url=”~/About/Default.aspx” title=”About” description=”About this Site”> <siteMapNode url=”~/About/Contact.aspx” title=”Contact Us” description=”Contact Us” /> <siteMapNode url=”~/About/AboutUs.aspx” title=”About Us” description=”About Us” /> <siteMapNode url=”~/Login.aspx” title=”Login” description=”Log in to this web site” />
Remember, you don’t have to type all this code yourself. You can find a copy of the file in this chapter’s code file that you can download from the Wrox web site. . 4
Save the file; you’re done with it for now.
COMMON MISTAKES Make sure you type the code exactly as shown here. Notice that some items, such as the first “Home” element, contain other child elements and have their closing tag further down the code. In contrast, items such as “By Genre” are using self-closing tags and do not have any child elements.
248 ❘ Chapter 7 Navigation
How It Works Although you didn’t add any spectacular code in the Web.sitemap file, a few things are worth discussing. First of all, note that the site map only contains a single root node called Home. This is enforced by the Web.sitemap file, which doesn’t allow more than one root element. The downside of this is that this single root element will also be the root item of your Menu and TreeView controls. In Figure 7-3 you can see how all submenus of the TreeView fall under the Home node. In most web sites, however, it’s much more common to have the Home item at the same level as the others. Therefore, in this exercise you added an additional Home node directly under the parent node to align it with the Reviews, About, and Login items. In a later exercise you see how to hide the root element from the controls, enabling you to only show the “first children” of the root node and their children. To overcome the problem that URLs in the siteMapNode elements need to be unique, you set one to ~/ and the other to ~/Default.aspx. Because of the way web servers can handle default documents, this eventually points to the same file.
A Web.sitemap file all by itself isn’t very useful. You need to add navigation controls to your site to make use of the site map. In the next section you see how to use the Menu control. Later sections dig into the TreeView and SiteMapPath controls.
Using the Menu Control The Menu control is very easy to use and tweak. To create a basic menu, all you need to do is add one to your page, hook it up to a SiteMapDataSource control, and you’re done. But at the same time, the control is quite flexible and has around 80 public properties (including the ones shared by all controls) that enable you to tweak every visual aspect of the control. The following table lists the most common properties used with the menu. Refer to the MSDN online help for a complete description of this control. Property
Description
CssClass
Enables you to set a CSS class attribute that applies to the entire control.
StaticEnableDefaultPopOutImage
A Boolean that determines whether images are used to indicate submenus on the top-level menu items.
DynamicEnableDefaultPopOutImage
A Boolean that determines whether images are used to indicate submenus on submenu items.
DisappearAfter
Determines the time in milliseconds that menu items will remain visible after you move your mouse away from them.
MaximumDynamicDisplayLevels
Determines the number of levels of submenu items that the control can display. Useful with very large site maps to limit the number of items being sent to the browser.
DataSourceID
The ID of a SiteMapDataSource control that supplies the data for the menu from the Web.sitemap file.
Using the Navigation Controls ❘ 249
Property
Description
Orientation
Determines whether to use a horizontal menu with dropout submenus, or a vertical menu with fold-out submenus.
RenderingMode
New in ASP.NET 4, this property determines whether the control presents itself using tables and inline styles or unordered lists and CSS styles.
IncludeStyleBlock
New in ASP.NET 4, this property gives you full control (and responsibility) in styling the control. When set to False, ASP.NET does not add the embedded style sheet block used to lay out the Menu, making you responsible for writing the CSS.
The Menu control also has a few properties that start with Static or Dynamic. The Static properties are used to control the main menu items that appear when the page loads. Because they don’t change or get hidden when you hover over them, they are considered static. The submenus are dynamic, because they appear only when you activate the relevant main menu items. In addition to these properties, the menu also has a number of style properties that enable you to change the look and feel of the different parts of the menu.
Using the Rendering Mode Earlier versions of the Menu control were criticized because of the HTML they generated. In ASP.NET 2.0 and 3.5 the Menu control generated bloated HTML using tables and inline styles. Besides increasing the size of the page unnecessarily, this also meant that the Menu was much harder to style using your own CSS. Fortunately, this has been fixed in ASP.NET 4 with the introduction of the RenderingMode property on the control. By default in new ASP.NET 4 sites, this property ensures the control renders itself as an unordered list using
elements. You can override this behavior by setting the RenderingMode property to Table instead. You can also set this property to List to force the control to render itself as unordered lists even if you configured the entire web site to render itself in legacy mode to be backwards compatible with ASP.NET 3.5. This is done by setting the controlRenderingCompatibilityVersion attribute of the <pages> element in the web.config file to 3.5: <system.web> <pages controlRenderingCompatibilityVersion=”3.5” />
However, for new ASP.NET 4 web sites, like the Planet Wrox project, you don’t need to set this value explicitly and the control will use a modern CSS and unordered-lists-based menu instead. You’ll see the Menu control and the HTML it generates in the next exercise.
Creating a Basic Version of the Menu Control To see how the Menu control operates, you’re best off creating a very basic version first. Once you understand how it works and how it operates under the hood, you can style the menu to your liking so it blends in with the rest of the design of your site.
250 ❘ Chapter 7 Navigation
Try It Out
Adding a Menu to the Site
In this exercise, you see how to add a simple Menu control to the master page that uses the Web.sitemap file to build up the menu. The Menu is added to the MenuWrapper area in the master page and presents the menu items horizontally. Because of this orientation, this Menu is suitable only for the Monochrome theme. Later you add a TreeView to represent the pages in the site, and write some code that shows the Menu for the Monochrome theme and the TreeView for the DarkGrey theme. 1.
Open the master page in Markup View and locate the called MenuWrapper. Remove the placeholder text Menu Goes Here. If you added some default text between the ContentPlaceHolder tags in the MainContent earlier, now is a good place to remove that text again.
. 2
From the Navigation category of the Toolbox, drag a Menu and drop it between the MenuWrapper div tags. Set the CssClass of the Menu control to MainMenu:
3.
Switch to Design View. You may notice that the Design View doesn’t look like the final page anymore. That’s because you may have removed the styleSheetTheme attribute from the <pages> element in web.config. You can leave it like this for now. With much of the styling already done, this isn’t so important. You can still see how the content inside the cpMainContent placeholder is going to end up in the browser.
4.
Click the Menu control’s grey arrow to open the Smart Tasks panel.
5.
From the Choose Data Source drop-down list select . In the dialog box that appears click the Site Map icon. Figure 7-4 shows what your screen should look like now.
Figure 7-4
Using the Navigation Controls ❘ 251
6.
Click OK to close the dialog box.
. 7
When you return to the page, the Menu control now shows the top-level element, Home (see Figure 7-5).
Figure 7-5
If your Design View doesn’t look like this, but looks much closer to the final page, open the web. config file and remove the styleSheetTheme attribute from the <pages> element.
8.
Click the SiteMapDataSource once and then press F4 to open or activate the Properties Grid. Change the ShowStartingNode property from True to False. Note that as soon as you do this, the Menu control in the designer is updated and shows all direct child menus under the root element: Home, Reviews, About, and Login. Figure 7-6 shows how your menu should look now.
Figure 7-6
9.
Click the Menu control once to select it and then make the following changes to the properties of the control using the Properties Grid. Because the Menu control has so many properties, you may find it easier to find them if you alphabetically sort the list of properties in the Properties Grid. You can do that by clicking the second button on the toolbar with an A, a Z, and an arrow on it. Property
Value
StaticEnableDefaultPopOutImage
False
Orientation
Horizontal
252 ❘ Chapter 7 Navigation
When you’re ready, the code for your Menu should look like this:
10. Save the changes to the master page and then request Default.aspx in your browser. If necessary, use the Theme drop-down list to make Monochrome the active theme. You should now see the menu in the horizontal menu area. Hover your mouse over the items, and you’ll see sub items appear as in Figure 7-7, which shows the page in Google’s Chrome browser.
Figure 7-7
Note that the text on the sub items is hard to read. That’s because the CSS from the Monochrome theme has changed the text of all anchors in the menu area to white and no explicit background color has been set. After you’ve seen how the Menu control works, you get a chance to fix its styling. Don’t worry if the menu doesn’t look good in the DarkGrey theme. You see how to implement a different navigation control for that theme later in this chapter.
How It Works When a page with the menu is sent to the browser, the Menu control asks the SiteMapDataSource, defined in the same master page, for data. This data source control in turn reads the Web.sitemap file and then hands over the XML to the Menu control. Based on the hierarchical XML, the Menu is able to generate the necessary HTML and JavaScript. It generates a
element for the top menu items with nested sub elements, each containing one or more menu items. The Menu control initially hides the submenus through some JavaScript. When you hover your mouse over one of the main menu items, the submenu becomes visible. This is done by some JavaScript as well. If you search the source of the page for the JavaScript code that hides or shows the menu, you won’t find it. So where is the JavaScript that is used to show and hide the relevant menu items? The answer is in the cryptic <script> tag in the page that looks similar to this: <script src=”/WebResource.axd?d=vxurWY7jjhneEhwNQbmdBEdPSXwLRytjgBhME9lyLoo1 &t=633925206143355520” type=”text/javascript”>
Using the Navigation Controls ❘ 253
This <script> tag references a special ASP.NET handler called WebResource.axd. The seemingly random characters in the query string (the part of the URL after the question mark) tell the ASP.NET runtime to fetch a JavaScript file that contains the functionality for the menu. The file doesn’t exist on your disk, but is returned by the WebResource.axd handler on the fly based on the query string. If you’re brave, you can look at the file by requesting it in your browser by copying the value of the src attribute and pasting it right after the port number of your web site in the browser (for example, http://localhost:50404). You can safely ignore the file, because you don’t need to make any changes to it for the menu to function correctly. The WebResource.axd syntax is also used by other controls, like the TreeView that uses it to retrieve the images used in the TreeView. In addition to the JavaScript, you also find a CSS <style> block at the top of your page which sets the default layout for your menu items. Among other things, it removes the default bullet that
elements display by default and removes underlining from the elements in the menus.
To better integrate the Menu control with the existing design of the Monochrome theme, you can style it using CSS.
Styling the Menu Control The Menu control exposes a number of complex style properties that enable you to change the looks of items such as the main and submenu items. You can also define how these items look when they are active (selected) or when you hover your mouse over them. Each of these style properties has a number of subproperties for visual aspects, such as font, color, and spacing. Figure 7-8 shows the Properties Grid for the StaticMenuItemStyle, which defines the looks of the main menu items that are visible when the page first loads. Most of the properties, like BackColor, ForeColor, and Font are added to the <style> block at the top of the page that contains the Menu control. This makes it difficult to reuse the design in other pages or with other themes, so it’s much better to use CSS instead. You see how this works next.
Try It Out
Figure 7-8
Styling the Menu Control
In this exercise you add some CSS rules to the Monochrome.css file to influence the way the Menu control is styled. By default, the Menu control adds CSS classes to the menu items such as level1 and level2, which makes it easy to apply styling at various levels in the menu.
1 .
Open Monochrome.css from the Monochrome theme folder and add the following CSS rules. You can leave out the comments placed between /* and */, because they only serve to describe the purpose of the selectors. If you don’t feel like typing all this CSS, remember you can also get a copy of
254 ❘ Chapter 7 Navigation
this file from the code download that comes with this book and copy it from that file into yours. You find the file Monochrome.css in the Monochrome theme folder for this chapter. Remember, CSS is case sensitive, so type the selectors exactly as shown here: ul.level1 { /* Defines the appearance of main menu items. */ font-size: 14px; font-weight: bold; height: 19px; line-height: 19px; } ul.level1 .selected { /* Defines the appearance of active menu items */ background-color: #BCD1FE; } a.level1 { /* Adds some white space to the left of the main menu item text */ margin-left: 5px; } a.level2 { /* Defines the appearance of the sub menu items */ background-color: #cccccc; padding-left: 8px; } a.level1:hover, a.level2:hover { /* Defines the hover style for the main and sub items */ background-color: #BCD1FE; }
2.
Save and close the file.
. 3
Next, create the following folders and Web Forms that you’ll use in this and later chapters. Use the MyBasePage template to create the new files. Also, in Markup View, give each page a meaningful Title to avoid errors later. Folder
File Name
Title
/Reviews
Default.aspx
My Favorite Reviews
/Reviews
All.aspx
All Reviews
Using the Navigation Controls ❘ 255
4.
Folder
File Name
Title
/Reviews
AllByGenre.aspx
Reviews Grouped by Genre
/About
Default.aspx
About this Site
/About
Contact.aspx
Contact Us
/About
AboutUs.aspx
About Us
Save all changes and open the Default.aspx page from the root in your browser. Your site menu now looks a lot better and more in line with the rest of the Monochrome theme. When you hover the mouse over a main menu, the submenus appear, showing the text on a light grey background. When you hover over a submenu, its background color changes again. Figure 7-9 shows the expanded Reviews menu with the hover style applied to the By Genre menu item in Opera.
Figure 7-9
COMMON MISTAKES If you get an error when you navigate to one of the new pages you created, make sure you gave all of them a valid title. Because they all inherit from the base page, the title is checked when the page loads.
How It Works The Menu control renders itself as a series of
and
elements. The menu items itself are simple elements with a class attribute to indicate at what level they are. If you look in the HTML for the page in the browser you see something like this:
Home
256 ❘ Chapter 7 Navigation
Reviews
By Genre
All Reviews
...
Although you haven’t worked with the Repeater control before, it may look familiar, because it works in a manner similar to the other data controls. Within the you define the markup that you want repeated for each item in the data source. Using Eval you can get the value of the Title and assign it to the Literal, which is wrapped in a pair of tags. A similar construct is used for the BulletedList to feed it a DataSource:
In addition to assigning simple properties like the Text of the Literal from the Name of the underlying data item, you can also use Eval to get complex properties. In this example, Eval(“Reviews”) is used to get the collection of Reviews for the current Genre. The BulletedList control then understands how to handle this data source and retrieves the Title from each individual Review object and then displays that in the list. The diagram in Figure 14-8 shows you how each Genre contains one or more reviews whose titles are displayed below the name of the genre. After you have set up the Repeater and defined the query, you need to start the data-binding process. You do this by assigning the results of the query to the DataSource property of the Repeater, followed by a call to DataBind() as shown in the C# example: Repeater1.DataSource = allGenres; Repeater1.DataBind();
Name (from AnonymousType(0).Name) Review Title (from AnonymousType(0).Reviews(0).Title) Review Title (from AnonymousType(0).Reviews(1).Title) Review Title (from AnonymousType(0).Reviews(2).Title) Review Title (from AnonymousType(0).Reviews(3).Title) Name (from AnonymousType(1).Name) Review Title (from AnonymousType(1).Reviews(0).Title) Review Title (from AnonymousType(1).Reviews(1).Title) Name (from AnonymousType(2).Name) Review Title (from AnonymousType(2).Reviews(0).Title) Review Title (from AnonymousType(2).Reviews(1).Title) Review Title (from AnonymousType(2).Reviews(2).Title) Figure 14-8
496 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
These two lines set things in motion: as soon as you call DataBind(), the query is executed and the relevant genres and reviews are retrieved from the database. In this example, the genres are sorted on their Name, but obviously you can order on other properties, such as SortOrder, as well. The Repeater then loops through each item in the result set (this item is the anonymous type you just saw) and uses that item’s Name to fill in the element with the genre name. The Repeater then assigns the Reviews collection to the inner BulletedList control’s DataSource property. This control loops over the available Review instances, using their Title to build up the bulleted list. In this example, you see that genres without reviews are displayed in the list as well. In the Exercises section at the end of the chapter, you’ll find an exercise that shows you how to hide empty genres.
Although it may take you some time to fully understand the principles behind these LINQ queries and the Entity Framework, I am sure you are beginning to appreciate their power and accessibility. With just a few lines of code and a few controls, you can create powerful data presentation pages. However, it’s possible to create LINQ queries that execute against EF and use them with the ASP.NET Server Controls with even fewer lines of code. You see how this works in the next section, which deals with the new EntityDataSource, the ListView, and the DataPager controls.
Using Server Controls with LINQ Queries So far you have seen one way to bind the results of a LINQ query against EF to a control in your ASPX page: assign the data to the control’s DataSource property and then call DataBind. This way of getting data into the controls has a few shortcomings. First of all, this method does not support the editing, updating, and deleting of data directly. Secondly, because you define the data source in the Code Behind, the GridView doesn’t know what data you’re feeding it until runtime, so you get no tool support to set up its columns. These shortcomings are easy to overcome by using the new server controls, including the ListView and the EntityDataSource control.
Using Data Controls with the Entity Framework In the previous chapter you were introduced to some of the data controls like the GridView and the SqlDataSource. But ASP.NET 4 ships with more controls that let you create data-driven pages with very few lines of code. Two of these controls provide a visual interface in your ASP.NET pages, and the third one works as a bridge between your data-bound controls and your underlying data sources. The following table briefly introduces you to these controls. Control
Description
EntityDataSource
As with the SqlDataSource that you saw in previous chapters, the EntityDataSource works as a bridge between your data-bound controls and the underlying data source: EF in this case.
ListView
The ListView control provides a visual interface that enables you to display, insert, edit, and delete items in a database, providing full CRUD services.
Using Server Controls with LINQ Queries ❘ 497
Control
Description
DataPager
The DataPager is used together with the ListView and enables you to paginate the data in the data source, feeding data to users in bite-sized chunks instead of all records at once.
The next few sections provide you with more detail about these controls and show you how to use them in a few Try It Out exercises.
Introducing the EntityDataSource Control As its name implies, the EntityDataSource is a close relative of the SqlDataSource and other data source controls. The EntityDataSource control is to EF what the SqlDataSource control is to SQL-based data sources: it provides a declarative way to access your model. Just like the SqlDataSource control, EntityDataSource gives you easy access to the CRUD operations and additionally makes sorting and filtering of data very easy. The following table describes the main properties and capabilities of this new control. Property
Description
EnableDelete EnableInsert EnableUpdate
Determine whether the control provides automatic insert, update, and delete capabilities. When enabled, you can use the control together with data-bound controls like the GridView or ListView to support data management.
ContextTypeName
The name of the ObjectContext class that the control should use. In the examples in this book, this type name is PlanetWroxEntities.
EntitySetName
The name of the entity set from the EF diagram you want to use, such as Reviews.
Select OrderBy Where
Enable you to define the query that the EntityDataSource control fires against the model. Each of these properties maps to one of the query operators you’ve seen before.
Together with a data-bound control, the EntityDataSource provides you full access to the underlying SQL Server database through LINQ to EF. The next exercise shows you how to use the control in your ASPX pages.
Try It Out
A Simple EntityDataSource Application
In this Try It Out you start building the Gig Pics feature of Planet Wrox, a section of the web site where users can upload photos they created during concerts of their favorite bands. You see how to let a user create a new photo album that acts as a container for the pictures that are uploaded. You see how to use the EntityDataSource and a DetailsView to create a user interface that enables you to enter a name
498 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
for a new photo album into the system. In later exercises you see how to add pictures to this photo album. 1.
Add the following two tables to your database using the Database Explorer window. Refer to Chapter 12 for more details about creating tables, primary keys, and identity columns. If you don’t want to create the tables by hand, you can also grab the database that comes with this chapter’s code download. PhotoAlbum Column Name
Data Type
Description
Id
int
The unique ID (identity and primary key) of the photo album.
Name
nvarchar(100)
The name of the photo album.
Column Name
Data Type
Description
Id
int
The unique ID (identity and primary key) of the picture.
Description
nvarchar(300)
A short text describing the picture.
ToolTip
nvarchar(50)
A tooltip displayed when you hover over a picture.
ImageUrl
nvarchar(200)
The virtual path to the picture on disk.
PhotoAlbumId
int
The ID of the photo album this picture belongs to.
Picture
For both tables, make sure that none of the columns in the two tables are nullable by unchecking their Allow Nulls check boxes. Make the Id column the primary key by clicking it once, and then clicking the yellow key icon on the Table Designer toolbar. Additionally, make this column the table’s Identity column by setting the (Is Identity) property on the Column Properties Grid to Yes. Refer to Chapter 12 if you’re not sure how to do this. . 2
On the Database Explorer open the database diagram that you created in Chapter 12 and add the two new tables to it by dragging them from the Database Explorer. Arrange the two new tables side by side if necessary. Next, drag the Id column from the PhotoAlbum table onto the PhotoAlbumId column of the Picture table. Confirm that the Primary Key Table is PhotoAlbum with Id as the selected column and that Picture is the Foreign Key Table with PhotoAlbumId as the selected column as shown in Figure 14-9.
Figure 14-9
Using Server Controls with LINQ Queries ❘ 499
3.
4 .
Click OK twice to apply the changes and then save and close the diagram. Click Yes to confirm the changes made to the two tables. Next, open the ADO.NET Entity Framework Model file PlanetWrox.edmx from the App_Code folder by double-
clicking it. Right-click an empty spot of the diagram and choose Update Model from Database. In the wizard that appears, expand Tables and then check the two tables you just created: PhotoAlbum and Picture. Click Finish to have the two tables added to your model. Your diagram should end up like Figure 14-10. If you’re using a non-English version of VWD you need to pluralize the names of the entity sets and properties again. To do this, click the Picture class, open its Properties Grid by pressing F4, and change the Entity Set Name from Picture to Pictures. Repeat this for the PhotoAlbum class and change its Entity Set Name to PhotoAlbums. Finally, click the Picture property on the diagram for the PhotoAlbum class, press F2 to rename the item, and then enter Pictures as the new name.
Figure 14-10
Save all your changes and close the diagram. 5.
Create a new Web Form based on your custom template in the root of the site and call it NewPhotoAlbum.aspx. Give the page a title of Create New Photo Album.
6.
Switch the page into Design View and from the Data category of the Toolbox, drag a DetailsView control and drop it into the cpMainContent placeholder. On the DetailsView control’s Smart Tasks panel, open the Choose Data Source drop-down list and select . In the Data Source Configuration Wizard dialog box, click the Entity icon and click OK. In the Named Connection drop-down, choose PlanetWroxEntities.
NOTE If you get an error about incorrect metadata, close the dialog, delete the existing EntityDataSource control from the page and manually drag a new one from the Toolbox. On the DetailsView control’s Smart Task panel choose the new data source control. Then open the EntityDataSource control’s Smart Task panel and choose Configure Data Source.
Click Next to go to the Configure Data Selection screen, shown in Figure 14-11. From the EntitySetName drop-down list, choose PhotoAlbums.
500 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
Figure 14-11
7.
For this exercise, you need insert behavior, so check off the Enable Automatic Inserts check box. Click Finish to close the Data Source Wizard.
. 8
If you don’t see the Id and Name columns in the DetailsView, but a general Databound Col0 instead, click the Refresh Schema link on the control’s Smart Task panel. Enable inserting for the DetailsView control by checking the item Enable Inserting on the same Smart Tasks panel.
. 9
1 0.
Open the DetailsView control’s Properties Grid and change the DefaultMode from ReadOnly to Insert.
Switch to Markup View, locate the BoundField for the Id property of the PhotoAlbum and set its InsertVisible property to False so you don’t get a text box for the ID when inserting a new photo album.
11. Select the EntityDataSource control in Design View, open its Properties Grid, and switch to the Events tab. Double-click the Inserted event, visible in Figure 14-12. 12. At the top of the code file, add the following Imports/using statement to bring your entities model in scope so you can access classes such as PhotoAlbum: VB.NET
Imports PlanetWroxModel C#
using PlanetWroxModel;
Using Server Controls with LINQ Queries ❘ 501
Figure 14-12
Then, in the event handler that VWD added for you write the following code that redirects the user to a new page once the photo album has been inserted in the database: VB.NET
Protected Sub EntityDataSource1_Inserted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.EntityDataSourceStatusEventArgs) _ Handles EntityDataSource1.Inserted Dim myPhotoAlbum As PhotoAlbum = CType(e.Entity, PhotoAlbum) Response.Redirect(String.Format(“ManagePhotoAlbum.aspx?PhotoAlbumId={0}”, myPhotoAlbum.Id.ToString())) End Sub C#
13. Save all changes and then request NewPhotoAlbum.aspx in the browser. COMMON MISTAKES If the page you get is empty, make sure you set the DefaultMode property of the DetailsView to Insert.
Enter a new name for the photo album such as Placebo playing live at Rock Werchter 2009 and click the Insert link. You’ll get a Resource Not Found error (because you haven’t created ManagePhotoAlbum.aspx yet) but you can at least see the ID of the new photo album in the address bar of your browser.
How It Works You started this exercise by adding the Picture and PhotoAlbum tables to both the database and EF diagram. These tables are used to store data about photo albums and the pictures they contain. Each individual picture belongs to a PhotoAlbum referred to by its PhotoAlbumId that points to the Id
502 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
column of the PhotoAlbum table in the database. The Picture table is designed to only hold data about the picture; the actual picture file will be stored on disk, as you see later. To enable users to create a new photo album you added a DetailsView control to the page. To make sure the control can be used to insert new photo albums, you enabled inserting and then set the DefaultMode to Insert. This forces the control to jump into insert mode, instead of the default read-only mode. You then hooked up an EntityDataSource to the DetailsView, which takes care of inserting the photo album in the PhotoAlbum table. The code for the EntityDataSource control looks like this:
If you are using Visual Basic.NET, your code won’t have the OnInserted attribute set. Note how straightforward the EntityDataSource is in this scenario: you point it to a DefaultContainerName, the PlanetWroxEntities in this example, which is the main entrance for the control to get its data. You also turned on inserting by setting EnableInsert to True. Additionally, you set the EntitySetName, so the control knows what object to use from the EF diagram. For simple inserts, this is all you need to do. When the page loads in the browser, the DetailsView renders a user interface that enables you to enter a new name for the photo album. When you click Insert, the data you entered is assembled and forwarded to the EntityDataSource. This control in turn creates a new PhotoAlbum instance and then saves it in the database by sending the appropriate INSERT SQL statement to the database. In many situations, this standard behavior is not enough. You may need to validate the data that is entered or you may have a need to change the actual data before it gets sent to the database. You see an example of the latter in a later Try It Out when you upload images to the server. Another common requirement is retrieving the ID of the newly created item, which is then sent to the next page. This exercise used the following code to accomplish that: VB.NET
Dim myPhotoAlbum As PhotoAlbum = CType(e.Entity, PhotoAlbum) Response.Redirect(String.Format(“ManagePhotoAlbum.aspx?PhotoAlbumId={0}”, myPhotoAlbum.Id.ToString())) C#
The cool thing about the EntityDataSource control is that it works with strongly typed objects, where the type maps to the tables you added to the model diagram. In this case, you are working with real instances of PhotoAlbum, the class that represents the photo albums in the system. This enables you to retrieve the photo album you have inserted in the database in the Inserted event of the data source control. The e argument exposes a property called Entity that contains a reference to the new photo
Using Server Controls with LINQ Queries ❘ 503
album. Simply by casting it to a real PhotoAlbum (by using CType in VB.NET or putting the class name in parentheses before it in C#), you can access the properties of the PhotoAlbum, including its new ID that has been generated by the database (through the Identity settings on the ID column) and then stored in the Id property of the PhotoAlbum. The final line in the event handler takes the user to the next page and sends the ID of the new photo album in the query string. Note that you get an error when you leave the name field empty and click Insert. The previous chapter showed you how to modify the DetailsView to insert validation controls to its templates.
Now that you can insert new photo albums, the next logical step is to add pictures to the photo album. In the next exercise you see how to create a user interface with the ListView control that enables a user to upload new pictures in the photo album.
Introducing the ListView Control Up until now, you have seen a few data-bound controls at work. You saw the GridView, which is quite powerful because it supports updates, deletes, sorting, and paging of data, but lacks inserting and generates a lot of HTML markup. You also saw the Repeater control that gives you precise control over the generated HTML, but lacks most advanced features that the other data controls have, such as update and delete behavior and sorting and filtering capabilities. And finally you saw the DetailsView that enables you to insert or update one record at a time. The ListView is a “best of all worlds” control, combining the rich feature set of the GridView with the control over the markup that the Repeater gives you and adding the insert behavior of the DetailsView. The ListView enables you to display data in a variety of formats, including a grid (rows and columns like the GridView), as a bulleted list (similar to how you set up the Repeater earlier in this chapter), and in Flow format, where all the items are placed in the HTML after each other, leaving it up to you to write some CSS to format the data. The ListView displays and manages its data through templates that enable you to control many of the different views that the ListView gives you on its underlying data. The following table describes all the available templates that you can add as direct children of the ListView control in the markup of the page. Template
Description
Serves as the container for the control. It enables you to define a location where the individual data items are placed. The data items, presented through the ItemTemplate and AlternatingItemTemplate, are then added as children of this container.
Define the read-only mode for the control. When used together, they enable you to create a “zebra effect,” where odd and even rows have a different appearance (usually a different background color). continues
504 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
(continued) Template
Description
<SelectedItemTemplate>
Enables you to define the look and feel of the currently active, or selected, item.
<EditItemTemplate>
These two templates enable you to define the user interface for inserting and updating items in the list. You typically place controls like text boxes, drop-down lists, and other server controls in these templates and bind them to the underlying data source.
Defines the markup that is placed between the items in the list. Useful if you want to add a line, an image, or any other markup between the items.
<EmptyDataTemplate>
Displayed when the control has no data to display. You can add text or other markup and controls to it to tell your users there’s no data to display.
<EmptyItemTemplate>
Used in advanced presentation scenarios where data can be presented in different groups.
Although this long list of templates seems to suggest you need to write a lot of code to work with the ListView, this is not always the case. First of all, Visual Web Developer 2010 creates most of the code for you based on the data that is exposed by controls such as the EntityDataSource. Secondly, you don’t always need all templates, enabling you to minimize the code for the control. Besides the numerous templates, the control has the following properties that you typically set to influence its behavior. Property
Description
ItemPlaceholderID
The ID of a server-side control placed within the LayoutTemplate. The control referred to by this property is replaced by all the repeated data items when the control is displayed onscreen. It can be a true server control like an or a simple HTML element with a valid ID and its runat attribute set to server (for example,
). If you don’t set this property, ASP.NET tries to find a control with an ID of itemPlaceholder and uses that control instead.
DataSourceID
The ID of a data source control on the page, such as an EntityDataSource or a SqlDataSource control.
InsertItemPosition
The enumeration for this property contains three values — None, FirstItem, and LastItem — to determine the position of the InsertItemTemplate: either at the beginning or end of the list, or not visible at all.
Using Server Controls with LINQ Queries ❘ 505
Just like the other data-bound controls, the ListView has a number of events that fire at specific moments during the control’s lifetime. For example, it has ItemInserting and ItemInserted events that fire right before and after an item has been inserted in the underlying data source. Similarly, it has events that trigger right before and after you update or delete data. You see more about handling these kinds of events in the next chapter. Besides the templates, properties, and events you just saw, the ListView has more to offer. For a detailed explanation of the ListView control and all of its members and behavior, check out the MSDN documentation at http://tinyurl.com/mupjot. The next exercise shows you how to put all of this information together. You see how to define the various templates and set the relevant properties to control the look and feel of the ListView control.
Try It Out
Inserting and Deleting Data with the ListView Control
Inserting items with the ListView can be just as easy as with the DetailsView: you point the control to a data source and let VWD create the necessary templates for you. However, in many real-world web sites, these default templates won’t cut it. You may want to display fewer fields than are available in the data source, validate data before it gets sent to the database, or you may want to store data at a different location than the database. For example, you may want to store uploaded images on disk rather than in the database and then only store a reference to the file in the database table. The next exercise shows you how to customize the ListView templates and handle the Inserting event of the EntityDataSource when you build functionality to add pictures to your photo albums. This exercise has you work with a lot of code that is generated automatically by VWD. Most of what you need to do in this exercise is remove code instead of add new code. If you get lost somewhere, or you feel your code does not look like it should, remember this chapter comes with the full source code that you can download from the Wrox web site, so you can compare your code with mine.
1 . 2.
In the root of the web site, create a new Web Form based on your custom template. Call it ManagePhotoAlbum.aspx, set its Title to Manage Photo Album and switch it into Design View.
From the Toolbox, drag a ListView control onto the page in the cpMainContent placeholder and then hook it up to an EntityDataSource control by choosing in the Choose Data Source drop-down list on the Smart Tasks panel (just as you did with the DetailsView earlier). Click the Entity icon, click OK, choose PlanetWroxEntities as the named connection, and click Next. In the Configure Data Selection dialog of the EntityDataSource control’s wizard, visible in Figure 14-11, choose Pictures from the EntitySetName drop-down list.
. 3
Check the first and the last check box of the three at the bottom of the screen to give the EntityDataSource insert and delete support. Finally, click Finish to close the Configure Data
Source Wizard.
506 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
4.
Back in Design View, select the EntityDataSource control, open its Properties Grid, locate the Where property, and open its Expression Editor by clicking the ellipsis for that property. You may recall that the page ManagePhotoAlbum.aspx receives the photo album ID through the query string so you’ll set up a QueryStringParameter in this step to filter the ListView to those pictures belonging to the designated photo album. Click Add Parameter, enter PhotoAlbumId as the name and then fill in the Expression Editor dialog box as shown in Figure 14-13.
Figure 14-13
Make sure you enter it.PhotoAlbum.Id = @photoAlbumId in the Where Expression box at the top of the dialog. Next, click the Show Advanced Properties link and change the Type property of the parameter to Int32. When you’re done, click OK to dismiss the dialog box. . 5
Back in the page, the ListView should appear as a plain rectangle, shown in Figure 14-14, because you haven’t provided any template information yet.
Figure 14-14
Using Server Controls with LINQ Queries ❘ 507
6.
On the Smart Tasks panel of the ListView choose Configure ListView. (If you don’t see this link, click Refresh Schema first and reopen the Smart Tasks panel.) A dialog box appears that enables you to choose the layout of the control, a style, and whether or not you want to enable operations such as inserting and updating. Choose Bulleted List as the layout, and check the Enable Inserting and Enable Deleting items so your dialog box ends up as shown in Figure 14-15.
Figure 14-15
7. 8.
Click OK to close the dialog box. If you get a dialog box that asks if you want to regenerate the ListView control, click Yes.
Switch to Markup View and remove the code for the following templates. To make this as easy as possible, click the relevant opening tag once, then click the tag in the tag selector at the bottom of the Document Window to select the entire element and its content, and then press the Delete key. Alternatively, you can collapse the tag using the plus (+) symbol in the left margin, select the whole line, and delete it at once. ➤➤
➤➤
<EditItemTemplate>
➤➤
<EmptyDataTemplate>
➤➤
➤➤
<SelectedItemTemplate>
508 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
9.
Locate the
element in the LayoutTemplate and remove its ID, runat, and style attributes. Then add a class attribute and set it to ItemContainer. You can also remove the empty element that VWD added for you below the
. Your now contains this code:
10. Locate the ItemTemplate and remove the lines that make up the Id, PhotoAlbumId, and PhotoAlbum columns, bolded in the following code snippet, because you don’t need them. Make sure you don’t accidentally delete the opening
11. Repeat the previous step for the InsertItemTemplate that is also part of the ListView control’s markup. Compare your code with the following code and make any changes if necessary. Check if your ListView has a DataKeyNames property set to Id and add it if it isn’t there. Sometimes VWD
doesn’t add this property, while the code requires it to be there. Also check that the templates contain the correct controls. The order of the templates or the white space can be different in your code.
Using Server Controls with LINQ Queries ❘ 509
Description: ToolTip: ImageUrl:
Description: ToolTip: ImageUrl:
12. Switch back to Design View, select the EntityDataSource control, and open its Properties Grid. Switch to the Events tab and double-click the Inserting event. At the top of the page add an Imports or a using statement for the PlanetWroxModel namespace as you also did in the page All.aspx. Then in the event handler that VWD added for you, write the following code: VB.NET
Protected Sub EntityDataSource1_Inserting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls. EntityDataSourceChangingEventArgs) _ Handles EntityDataSource1.Inserting Dim photoAlbumId As Integer = Convert.ToInt32(Request.QueryString.Get(“PhotoAlbumId”)) Dim myPicture As Picture = CType(e.Entity, Picture) myPicture.PhotoAlbumId = photoAlbumId End Sub
510 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
13. Create a new Style Sheet file in the Styles folder and call it Styles.css. Replace the existing CSS in the file with this: .ItemContainer { width: 600px; list-style-type: none; clear: both; } .ItemContainer li { height: 300px; width: 200px; float: left; } .ItemContainer li img { width: 180px; margin: 10px 20px 10px 0; }
14. Save and close the file.
1 5.
Open the site’s Frontend master page (not the Management master page) from the MasterPages folder. Switch to Design View and drag the Styles.css file from the Solution Explorer onto the master page. This ensures that all pages in the site get a reference to this new style sheet, regardless of the theme that is being used.
1 6.
Save all your changes, close all open files, and then request NewPhotoAlbum.aspx in your browser. Make sure you don’t accidentally open ManagePhotoAlbum.aspx because it requires a query string that is sent by NewPhotoAlbum.aspx. Enter a new name for the photo album and click Insert. You’re taken to ManagePhotoAlbum.aspx where you can enter new pictures. For now, all you can do is enter the description of the picture, the tooltip, and a fake URL of the image (just enter some text); you see later how to modify this and let a user upload real pictures to the web site. Once you click the Insert button, a new item appears in the list, next to the Insert controls. Add a few more items and you’ll notice that the Insert controls move to a row below the others, as shown in Figure 14-16, which shows the Manage Photo Album page in Google Chrome.
Using Server Controls with LINQ Queries ❘ 511
Figure 14-16
17. Click the Delete button for an item and see how the item is removed from the list automatically.
1 8.
If you’re currently viewing the site in the Monochrome theme, use the drop-down list to switch to DarkGrey. Note that the layout is slightly messed up because there is not enough room to place three images side by side. The fix is easy though. Open up the DarkGrey.css file from the DarkGrey theme folder and add the following CSS at the end of the file: .ItemContainer { width: 400px; }
This overrules the width for the picture list to 400 pixels, so only two columns of pictures are shown in this theme.
How It Works You started this exercise by attaching an EntityDataSource control to the ListView control. The EntityDataSource is configured to work with the Pictures entity set. As you saw earlier, each picture is linked to a photo album by its PhotoAlbumId. To have the ManagePhotoAlbum.aspx page display only those pictures that belong to the current photo album (identified by the PhotoAlbumId query string) you set up a WhereParameter: <WhereParameters>
You should take note of two important parts in this markup. First, there’s the WhereParameters collection that contains a QueryStringParameter that looks at the PhotoAlbumId field in the query string. When the EntityDataSource is about to get its data, it retrieves the value for the parameter from the query string. The second important part is the Where attribute of the EntityDataSource control. It uses a Where clause to limit the items that are returned from the database: Where=”it.PhotoAlbum.Id = @photoAlbumId”
This gets all the Pictures from the Picture table that have the requested PhotoAlbumId. The “it” in the Where clause is an implicit range variable, just as other range variables in queries you’ve seen before. However, here you need to use “it” and cannot make up your own name as you can do with your own LINQ queries. At runtime, the Where clause is filled in with the actual PhotoAlbumId, retrieved from the query string, which ensures only pictures belonging to the current album are returned. The first time the page loads after you create a new photo album, there won’t be any pictures. However, as soon as you start adding items using the InsertTemplate of the ListView control, you’ll see them appear in the list. To display the pictures on the page, you used the ListView control. Just like other data-bound controls, the ListView is able to display repetitive data in a structured way. In this example, you set the ListView to bulleted list mode, so the control presents its data as a set of
elements. You define the container of the items with the :
Note that this
has its ID set to itemPlaceholder. This tells the ListView control where to add the individual pictures. At runtime, this element will be replaced by the actual items from the templates like . When the ListView control needs to display its data, it creates an item based on the for each data item in the data source. In this example, each data item is a strongly typed Picture object which provides access to properties such as ToolTip and Description:
... ToolTip: ...
Using Server Controls with LINQ Queries ❘ 513
With this code in place, each item in the data source is presented as a series of labels that display relevant properties of the picture. Eval(PropertyName) is used to retrieve the requested value from the object, which is then displayed as the Label control’s text. Note that at this stage, the only displays data about the picture. You see how to upload and display real pictures later. Note the CommandName of the Button control in the ItemTemplate. It’s set to Delete, which turns this button into a true Delete button. When you click it, the ListView figures out what picture you clicked the button for and then instructs the EntityDataSource control to delete the associated picture from the database. The CSS you added to Styles.css displays the items in an organized way. By setting the class attribute of the
control to ItemContainer, the following CSS is applied to that list: .ItemContainer { width: 600px; list-style-type: none; clear: both; }
The first property sets the entire width of the list to 600 pixels and the second declaration removes the bullet from the items in the list. Each item in the list is then displayed within a
element, to which the following CSS is applied: .ItemContainer li { height: 300px; width: 200px; float: left; }
Each item gets a forced width of 200 pixels. The float property tells each
element to float next to each other. Within the parent area of 600 pixels you can fit three
elements of 200 pixels each, causing the fourth and further elements to be placed on their own line. This is a great alternative to presenting data with HTML tables, which generally needs a lot more markup to achieve the same effect. Finally, each image within the
element gets a forced width of 180 pixels and 10 pixels of margin at the top and bottom, 20 pixels on the right (to create some room between the images), and none at the left side: .ItemContainer li img { width: 180px; margin: 10px 20px 10px 0; }
In the DarkGrey theme, the width of the ItemContainer is overridden to only 400 pixels. This way, the is just wide enough to display two images side by side.
514 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
In contrast to many of the other data-bound controls, the ListView also supports inserting by defining an InsertItemTemplate that contains one or more controls that are bound to properties in the underlying object. For example, the Description property of the picture is bound like this:
Description: ...
Instead of Eval(PropertyName), this code uses Bind(PropertyName) to set up a two-way binding mechanism. This ensures that the ASP.NET runtime is able to figure out the relationship between the Description property of a Picture and the text box called DescriptionTextBox, even after a postback. So when you enter some details and click the special Insert button (with its CommandName set to Insert), a new Picture object is constructed, its properties, such as Description, Title, and ToolTip, are filled with the values from the associated server controls in the InsertItemTemplate, and then the picture instance is forwarded to the EntityDataSource control, which takes care of saving the item in the database and refreshing the list of pictures that are displayed on the page. Once the EntityDataSource control is about to save the picture, it fires its Inserting event. In that event handler in this exercise you added some code that linked the new Picture instance to the PhotoAlbumId like this: VB.NET
Dim photoAlbumId As Integer = Convert.ToInt32(Request.QueryString.Get(“PhotoAlbumId”)) Dim myPicture As Picture = CType(e.Entity, Picture) myPicture.PhotoAlbumId = photoAlbumId C#
In previous versions of the Entity Framework, this code wouldn’t be so straightforward. Rather than setting the PhotoAlbumId you would retrieve and then assign the entire PhotoAlbum property with some cryptic code. Fortunately, the Include Foreign Key Columns option you enabled earlier in this chapter has given you a PhotoAlbumId property on the Picture class that enables you to directly set the ID of the PhotoAlbum (which you retrieved from the query string). This in turn relates the Picture in the database to a specific photo album in the PhotoAlbum table.
Obviously, it’s not very user friendly to have users enter the ImageUrl of an image directly. It would be much easier for them if they could pick an image from their local computer and upload it to the server. You see how to accomplish this in the next exercise.
Using Server Controls with LINQ Queries ❘ 515
Try It Out
Customizing Templates of the ListView Control
The default templates for the ListView control that VWD generates based on the information from the EntityDataSource are enough only in the most trivial situations. Usually, you need much more control. For example, in the ItemTemplate you may want to display an actual Image control instead of the plain ImageUrl property as text. Likewise, in the InsertItemTemplate you may want to display a file upload control instead of a simple text box. In this exercise, you see how to change the standard templates so you can incorporate both features. Additionally, you see how to handle the Inserting event of the EntityDataSource control to save the uploaded file to disk, and update the database with the URL of the image. For this example to work, the account used by the web server (the account you use to log on to your machine if you are using the built-in web server that comes with VWD) needs read and write permissions to the GigPics folder that you create in this exercise. In case you run into problems with this exercise, refer to the section “Understanding Security in IIS” in Chapter 19.
1 .
Create a new folder in the root of the web site called GigPics. This folder will contain concert pictures uploaded by users.
2.
Open the page ManagePhotoAlbum.aspx in Markup View and locate the element. Remove the Label that displays the ImageUrl and replace it with an Image control, with its ImageUrl set to the ImageUrl of the picture object.
Remove the text ImageUrl: that appears right above the image. 3.
To enable users to upload images, you need to replace the TextBox for the ImageUrl property with a FileUpload control. You also need to remove the text ImageUrl: again. You do this in the InsertItemTemplate:
Note that you don’t need to bind the property to the control here. Because the uploaded image needs special treatment, you’ll write some code in the Code Behind of the page instead of relying on the built-in data-binding capabilities. . 4
Set the CausesValidation property of the Cancel button in the to False:
5.
Similarly, set the CausesValidation property of the Delete button in the to False.
516 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
6.
Switch to the Code Behind of the page (press F7) and then extend the Inserting event handler with the following code, which saves the file to disk and then updates the ImageUrl property of the Picture instance with its new location: VB.NET
myPicture.PhotoAlbumId = photoAlbumId Dim FileUpload1 As FileUpload = CType(ListView1.InsertItem.FindControl(“FileUpload1”), FileUpload) Dim virtualFolder As String = “~/GigPics/” Dim physicalFolder As String = Server.MapPath(virtualFolder) Dim fileName As String = Guid.NewGuid().ToString() Dim extension As String = System.IO.Path.GetExtension(FileUpload1.FileName) FileUpload1.SaveAs(System.IO.Path.Combine(physicalFolder, fileName + extension)) myPicture.ImageUrl = virtualFolder + fileName + extension C#
Go back to Markup View and add three validation controls to the InsertItemTemplate: two RequiredFieldValidator controls hooked up to the text boxes for the Description and ToolTip, and one CustomValidator with its ErrorMessage set to Select a valid .jpg file and its ID set to cusValImage. Finally, set the TextMode property of the text box for the Description to MultiLine and enter a line break (a ) before the Insert button. You should end up with the following code: Description: ToolTip:
Using Server Controls with LINQ Queries ❘ 517
8.
Switch to Design View, select the ListView control, and set up an event handler for its ItemInserting event by double-clicking the event in the Event tab of the Properties Grid. Complete the event handler with the following code: VB.NET
Protected Sub ListView1_ItemInserting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewInsertEventArgs) _ Handles ListView1.ItemInserting Dim FileUpload1 As FileUpload = CType(ListView1.InsertItem.FindControl(“FileUpload1”), FileUpload) If Not FileUpload1.HasFile OrElse Not FileUpload1.FileName.ToLower().EndsWith(“.jpg”) Then Dim cusValImage As CustomValidator = CType(ListView1.InsertItem.FindControl(“cusValImage”), CustomValidator) cusValImage.IsValid = False e.Cancel = True End If End Sub C#
Save all your changes, and then request NewPhotoAlbum.aspx in your browser. Enter a new name for the photo album and click the Insert link. Insert a few pictures by entering a description and a tooltip, selecting a .jpg picture from your hard drive, and clicking the Insert button. Then enter the description and tooltip of another image, but leave the file upload box empty. When you click Insert, you get an error message indicating that you didn’t upload a valid JPG file, as shown in Figure 14-17 in Apple’s Safari browser.
1 0.
Click the Browse button of the file upload box, browse for a valid .jpg file, and click the Insert button once more. The file now gets uploaded successfully.
518 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
Figure 14-17
How It Works You haven’t changed much in the actual process of inserting the Picture into the database. The ListView control still collects all the relevant data from the page and then sends it to the EntityDataSource control, which then inserts the item in the Picture table in the database through EF. What is different is the way you set up the templates and the way you handled the events of the EntityDataSource and ListView controls. Look at the templates first. Inside the ItemTemplate you added an to take the place of the plain text label. As you can see in Figure 14-17 this displays the actual image, rather than just its URL. To enable a user to upload the images, you replaced the TextBox control in the InsertItemTemplate with a FileUpload control. Additionally, you added a few validation controls to force the user to enter the required fields. As soon as you click the Insert button, the page posts back and the ListView control fires its ItemInserting event. This event is a good place to perform any custom validation. One of the arguments this event handler receives (the e argument) is of the type ListViewInsertEventArgs, a class that provides context-sensitive information to the ItemInserting event. When you detect an error, you can set the Cancel property of this e argument to True to tell the ListView control you want to cancel the insert operation. Inside this event handler you added some code that “finds” the upload control in the InsertItem template. Because you can potentially have multiple controls with the same name (for example, a FileUpload control in the InsertItemTemplate and EditItemTemplate) you cannot access FileUpload1 directly. Instead, you need to use FindControl on the InsertItem object to search for the control: VB.NET
Dim FileUpload1 As FileUpload = CType(ListView1.InsertItem.FindControl(“FileUpload1”), FileUpload)
When you have a reference to the FileUpload control you can check its HasFile property to see if a file has been uploaded. Additionally, you can check FileUpload1.FileName.ToLower().EndsWith(“. jpg”) to see if a file with a .jpg extension has been uploaded. To ensure that this test is only carried out when the user has uploaded a file, the code uses OrElse in VB and || in C# to short-circuit the logic in the If statement, as explained in Chapter 5. If the user doesn’t upload a valid file, the code in the If block runs. It uses FindControl again to find the CustomValidator control and sets its IsValid property to False. This tells the control to display its ErrorMessage property when the page renders. Finally, to stop the ListView from continuing the insert operation you need to set the Cancel property of the e argument to True: VB.NET
e.Cancel = True C#
e.Cancel = true;
If the user uploaded a valid .jpg file, the ListView continues with its insert operation, which eventually results in an insert action against the EntityDataSource control. When that control is about to send the insert operation to EF, it fires its Inserting event, giving you a last chance to hook into the process and look at the data. Inside this event handler, you used similar code to find a reference to the FileUpload control inside the InsertItem template. You then used the following code to determine the physical and virtual folder for the file, its name, and its extension: VB.NET
Dim Dim Dim Dim
virtualFolder As String = “~/GigPics/” physicalFolder As String = Server.MapPath(virtualFolder) fileName As String = Guid.NewGuid().ToString() extension As String = System.IO.Path.GetExtension(FileUpload1.FileName)
The variable virtualFolder holds the virtual location — starting off the root of the web site — of the folder where the uploaded images are stored. Using Server.MapPath you can turn this into a physical folder. Assuming you have your project in its default location of C:\BegASPNET\Site, the physicalFolder variable now contains C:\BegASPNET\Site\GigPics. Next, a new, random file name is generated using Guid.NewGuid(). The Guid class is able to generate more or less random file names that are guaranteed to be unique across time and space. This code assigns the variable fileName something like f6d8ed05-2dbe-4aed-868a-de045f9462e3, which guarantees a unique file name. Finally, the extension of the file is retrieved using the static GetExtension method of the Path class in the System.IO namespace.
520 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
At this stage, you have all the required information to store the file on disk, and then update the database. Storing the file on disk is easy using the SaveAs method of the FileUpload control: VB.NET
This code takes the physical folder, the file name, and the extension and passes it to the SaveAs method, which saves the file at the requested location. Finally, the Picture instance is updated with the new ImageUrl: VB.NET
This assigns something like ~/GigPics/f6d8ed05-2dbe-4aed-868a-de045f9462e3.jpg to the ImageUrl property, which is the new virtual location of the uploaded image. Right after you insert the new image, the ListView is updated and now shows the new image using the Image control with its ImageUrl set to the image you just uploaded.
You can imagine that if you upload a large number of images for a single photo album, the page becomes more difficult to manage. This is especially true at the front end where users may be accessing your site over a slow network connection. Instead of presenting them all the images in the photo album on a single page, you can split up the photo album in multiple pages, enabling the user to go from page to page. You see how to do this in the next section, which discusses the DataPager control.
Introducing the DataPager Control In older versions of ASP.NET paging was only possible using functionality built in directly to some controls, like the GridView and the DetailsView, or by manually writing code. This has changed somewhat with the introduction of the DataPager control in ASP.NET 3.5. The DataPager is different in that it is a separate control that you can use to extend another, databound control. Currently, the .NET Framework lets you use the DataPager only to provide paging capabilities to the ListView control, but the developer community has been active writing implementations for other controls, like the GridView, as well. You can hook up the DataPager to the ListView control in two ways: you can either define it within the of the ListView control or you can define it entirely outside the
Using Server Controls with LINQ Queries ❘ 521
ListView. In the first case the DataPager knows to what control it should provide paging capabilities automatically. In the latter case, you need to set the PagedControlID property of the DataPager to the ID of a valid ListView control. You see how to configure and use the DataPager in conjunction with a ListView next. Being able to define the DataPager outside of the ListView control is useful if you want to place it at a different location of the page, in the Footer or Sidebar area, for
example.
Try It Out
Paging Data with the ListView and DataPager Controls
In this Try It Out you create the front-end page of the Gig Pics feature. Users to your site can choose one of the available photo albums from a drop-down list and then view all the available pictures in a pageable list that is created by a ListView and a DataPager control. Figure 14-20 shows the final result of this exercise.
1 .
In the root of your site, create a new folder called PhotoAlbums. Inside this folder create a new Web Form based on your custom page template and call it Default.aspx. Set the Title of the page to All Photo Albums.
2.
Switch to Design View and drop a DropDownList control on the page. On the control’s Smart Tasks panel enable AutoPostBack and then hook it up to a new EntityDataSource control by clicking Choose Data Source, and then choosing from the first drop-down list. Click the Entity icon, click OK, and select PlanetWroxEntities in the Named Connection dropdown list. Click Next and on the Configure Data Selection dialog box choose PhotoAlbums from the EntitySetName drop-down list and select the fields Id and Name, shown in Figure 14-18.
Figure 14-18
522 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
3.
Click Finish to close the Data Source Wizard and then in the Choose Data Source dialog box select Name from the Data Field to Display drop-down list, and leave Id selected in the Data Field for the
Value drop-down list. If you don’t see the items in the lists, click Refresh Schema first. . 4 . 5
. 6
Click OK to close the Data Source Configuration Wizard. Below the DropDownList add a new ListView control and connect it to a new EntityDataSource by selecting from the drop-down list on the control’s Smart Tasks panel. Click the Entity icon and click OK. Select PlanetWroxEntities in the drop-down and click Next. In the dialog box that follows, choose Pictures from the EntitySetName drop-down list and click Finish. Select the new EntityDataSource control (called EntityDataSource2) in Design View, open its Properties Grid, and click the button with the ellipsis for the Where property to open up the Expression Editor. Check off the checkbox for an automatically generated Where clause at the top of the dialog. Click the Add Parameter button and enter PhotoAlbumId as the name for the parameter. Then choose Control in the Parameter Source drop down property and choose DropDownList1 as the ControlID. Finally, click the Show Advanced Properties link and change the Type of the parameter to Int32. Your Expression Editor should look like Figure 14-19.
Figure 14-19
Click OK to close the dialog. . 7
On the ListView control’s Smart Tasks panel, click Configure ListView (click Refresh Schema and reopen the Smart Tasks panel first if you don’t see that option). Select Bulleted List as the layout, and in the Options area choose Enable Paging. The drop-down list below it should default to Next/ Previous Pager, which is fine for this exercise.
8.
Click OK and VWD creates a couple of templates for you.
Using Server Controls with LINQ Queries ❘ 523
9.
Switch to Markup View and remove the following templates and the code they contain: ➤➤
➤➤
<EditItemTemplate>
➤➤
➤➤
➤➤
<SelectedItemTemplate>
10. Remove the ID, runat, and style attributes from the
in the LayoutTemplate and add a class attribute and set it to ItemContainer. Locate the DataPager inside the LayoutTemplate and add a PageSize attribute set to 3. Finally, add clear: both; to the empty style attribute:
11. Modify the code in the ItemTemplate so it ends up like this:
This creates an Image control with its ImageUrl and ToolTip properties bound to the corresponding properties of the Picture object that you’re binding to. The ToolTip appears when you hover your mouse over the image in the browser. Below the image, a simple Label control displays the Description of the image. You don’t need the other properties that were initially defined in the template for this exercise.
1 2.
Next, wrap the entire code in the cpMainContent content block in an UpdatePanel with a ContentTemplate element to avoid page flicker when paging the list of pictures, or when choosing a new photo album from the list. ...
524 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
13. Open the Web.sitemap file for the site and add a main menu and two submenu items for the Gig Pics section, between the Reviews and About sections: <siteMapNode url=”~/PhotoAlbums/” title=”Gig Pics” description=”All Gig Pics”> <siteMapNode url=”~/PhotoAlbums/Default.aspx” title=”Gig Pics” description=”All Gig Pics” /> <siteMapNode url=”~/NewPhotoAlbum.aspx” title=”New Album” description=”Create a new Photo Album with Gig Pics” /> <siteMapNode url=”~/About/Default.aspx” title=”About” description=”About this site”>
14. Because you added another menu item, you need to change the width of each item in the menu for the Monochrome theme. To do this, open up Monochrome.css and change the width for the .MainMenu ul li selector from 200 to 160 pixels: .MainMenu ul li { width: 160px; }
15. Save all your changes and then request Default.aspx from the PhotoAlbums folder in your browser. Choose a photo album from the drop-down list and the page reloads, showing you the relevant pictures in the photo album.
COMMON MISTAKES If nothing happens when you choose a new item from the drop-down list, go back to VWD and ensure you set AutoPostBack on the DropDownList control to True.
If you don’t have any pictures in a photo album, or not enough to fill an entire page, choose New Album from the Gig Pics menu, create a new photo album, and add at least four images to it. Then click the Gig Pics menu item and choose your new photo album from the drop-down list. Note that there is now a paging user interface, enabling you to move forward and backward through the list of pictures in the photo album using the First, Previous, Next, and Last buttons visible at the bottom of the screen in Figure 14-20. Because of the AJAX panel you added, the selection and paging operations now occur completely flicker-free.
NOTE Your first few albums will end up broken. That’s because you didn’t supply images when you created them. You can delete the albums (and their associated picture records) from the database if you want to clean up a bit. In Chapter 16 you'll develop functionality to delete images from photo albums.
Using Server Controls with LINQ Queries ❘ 525
Figure 14-20
How It Works Most of what you have seen in this exercise should be familiar. You connected a DropDownList to an EntityDataSource similar to how you created the Genres drop-down list in the previous chapter. The ListView and its associated EntityDataSource are also similar to what is discussed earlier in this chapter. However, instead of a Where parameter that looks at the query string, the code now uses a Where parameter that looks at the DropDownList: <WhereParameters>
Because you set AutoGenerateWhereClause to True on the EntityDataSource control, a Where clause is created on the fly based on these parameters. This is in contrast to an earlier example where you explicitly defined a Where clause. For simple scenarios as in the preceding exercise, relying on the automatically generated Where clause works fine. For more detailed scenarios, it’s good to know you can also assign one explicitly. When the EntityDataSource is about to get its data from the database, it looks at the SelectedValue property of the drop-down list and then only retrieves the pictures that match the requested PhotoAlbumId. The biggest difference from previous examples is the addition of the DataPager. As demonstrated in this exercise, paging is handled for you automatically. All you need to do is embed a DataPager control somewhere in the LayoutTemplate of the ListView and the rest is taken care of automatically. If you place the DataPager outside the ListView, don’t forget to hook it up to the ListView by setting the PagedControlID property. If you prefer links or images over buttons, you can set the ButtonType
526 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
property of the NextPreviousPagerField element to Link or Image, respectively. If you prefer a numeric pager, replace the NextPreviousPagerField item with a NumericPagerField:
In this exercise you set the PageSize to 3 so it’s easier to fill more than one page and see the pager at work. In real-world applications the PageSize is usually a bit higher, such as 10 or 20. Because the layout uses a three-column layout for the Monochrome theme and a two-column layout for the DarkGrey theme, you may want to select a value that’s dividable by both, such as 18.
A Few Notes about Performance The preceding exercise has two known performance problems that you should be aware of. First, although the ItemTemplate of the ListView resizes the images to 180 pixels in the browser by setting their widths through CSS, the actual image is left unmodified. This means that if you upload a large image, the entire image is still downloaded, only to display as a small thumbnail image. It would be better to create a true thumbnail image at the server, and send that to the browser instead. The chapter entitled “Greeting Cards” in my book ASP.NET 2.0 Instant Results (ISBN: 978-0-471-74951-6) has a number of examples on resizing images on the server. The other potential performance problem lies in the way the data is paged. With the DataPager control, the data is paged inside the ASPX page. That means that all data is retrieved from the database and sent to the EntityDataSource control. The DataPager control then selects the right records to display on the current page. This works fine for result sets of up to hundreds of records. However, as soon as the number of items in a photo album or other collection grows above that number, you may find that your pages start to slow down. If that’s the case you may want to look at paging at the database level. The ADO.NET Entity Framework supports this scenario using the Skip() and Take() methods you’ve seen before.
Practical LINQ and ADO.NET Entity Framework Tips Here are some practical LINQ and ADO.NET Entity Framework tips: ➤➤
In this chapter you saw how to create anonymous types to shape the data you want to return from your queries. The compiler and IntelliSense are invaluable tools in determining what data you can return and what properties you have available. Spend some time playing around with the anonymous types, looking at the different options that the IntelliSense lists give you.
➤➤
Just as with other data access methods like the SqlDataSource control you saw in the previous chapter, try to filter your data as much as possible. If you know you only need reviews in the Jazz genre, be explicit and incorporate a Where clause in your code or EntityDataSource control that limits the list of reviews at the database level. This speeds up your queries and data retrieval, improving the overall speed of the application.
➤➤
Make use of anonymous types to decrease the memory consumption of your LINQ queries. For example, instead of retrieving the entire Review object, use the New keyword to create a new anonymous type on the fly. Because this new object contains only the properties you really need, you save yourself the overhead of bringing in the full object.
Summary ❘ 527
Summary LINQ is a compelling and exciting technology that ships with .NET 4. LINQ is an important plumbing technique in many data-access scenarios, including database access in ASP.NET web applications using the ADO.NET Entity Framework. Because LINQ is so important, it has been integrated in many different places in .NET. LINQ is available for objects enabling you to query in-memory collections. Additionally, LINQ is available for XML, Entities, ADO.NET, and DataSets, each type providing access to a different data store, but with the same, unified querying language. LINQ is also used as the query language for the ADO.NET Entity Framework. To work with EF in your ASP.NET web applications you have a couple of different options. First, you can write queries in the Code Behind of a page and then bind the results to a data-bound control using the DataSource property and DataBind method of the control. Alternatively, you can use the EntityDataSource control that serves as the bridge between your data-bound controls and your model. Combined with the new ListView and DataPager controls, the EntityDataSource gives you the ability to create fully functional CRUD pages. Until now, the database-driven pages you have seen look quite dull. You haven’t applied any styling, or provided any conditional formatting, where data is presented differently based on its values. This can be accomplished through control styles and the many events of the data-bound and data source controls. The next chapter shows you how to make use of these styles and events.
Exercises
1.
Imagine you have a page in the Reviews folder called MostRecent.aspx. This page should show the 10 most recently added reviews. What would your LINQ query look like if you only wanted to show the review’s Title property and the name of the genre it belongs to? You should use the Take method to limit the result set to 10. If you’re having trouble writing the code to get the last reviews, look at the section titled “First, FirstOrDefault, Last, and LastOrDefault,” which shows you how to get the last review in the database.
2.
What is the major benefit of the ListView control over other data controls like GridView and Repeater?
3.
Currently the Default.aspx page from the PhotoAlbums folder just shows the thumbnails of the pictures. What would you need to do to display the full-size picture on its own page using a LINQ query?
4.
When you delete a picture from the ListView on the Photo Album page, only the database record is deleted, but the image on disk is left untouched. Make use of the static System. IO.File.Delete method to delete the item from disk. Choose an appropriate event of the EntityDataSource to handle this and make use of e.Entity that’s available.
5.
Currently, the AllByGenre.aspx page displays the title of the genre, regardless of whether it has any reviews attached to it. How can you hide genres that don’t have any reviews? Make use of the Count method on the Reviews collection to solve this question. Answers to Exercises can be found in Appendix A.
528 ❘ Chapter 14 LINQ and the ADO.NET Entity Framework
⊲⊲ What You Learned in This Chapter ADO.NET Entity Data Model file
The file that contains the information necessary to map your object model to the tables in your database
Anonymous types
Types that are created on-the-fly without defining them explicitly
Entity Framework
A technology to create a strongly typed object model with an underlying database that enables you to interact with the data in your database
Entity sets
A collection of objects in your entity model. For example, a PhotoAlbum instance has a Pictures entity set that contains
the pictures in the album EntityDataSource control
An ASP.NET control that serves as a bridge between your ASPX pages and the Entity Framework
Lazy loading
A technique where data is not loaded from the database until it is accessed at runtime
LINQ
Language integrated query; the part of.NET Framework programming languages that provides querying support against all kinds of data collections, including objects, XML and databases
Range variable
A variable defined in your LINQ query that is subsequently used in the select and where parts
Strong typing
A programming concept where the type of a variable is explicitly defined when the variable is declared
Type inference
A technique where the compiler determines the type of a variable based on the data it gets assigned. This enables you to create strongly typed variables without explicitly defining the variable’s type first
CONFER PROGRAMMER TO PROGRAMMER ABOUT THIS TOPIC.
Visit p2p.wrox.com
15
Working with Data — Advanced Topics What You Will Learn in This Chapter: ➤➤
How to change the formatting of the various data-bound controls using styles, themes, and skins
➤➤
How to handle the various events that are fired by the data controls to change their appearance conditionally
➤➤
How to hand-code the UI of data access pages to get complete freedom over the page’s structure and markup
➤➤
How to use the built-in caching mechanisms to improve the performance of your web site
The previous three chapters introduced you to a lot of new concepts. Chapter 12 discussed databases in general and SQL Server 2008 Express edition in particular. That chapter also covered the basic operations to create, read, update, and delete data. Chapter 13 focused mostly on working with the SqlDataSource control and the different data-bound controls that you have at your disposal. Finally, the preceding chapter explored the world of the ADO.NET Entity Framework, Microsoft’s latest data access strategy designed to speed up the way you write data access code. To help you really understand the core concepts of data access, those three chapters focused mainly on the data source controls and the principles behind them, and much less on the presentation of data with the data-bound controls. Obviously, in a real-world application this is not enough and you need a way to present data in a clear and attractive way. The data-bound controls that ship with ASP.NET 4 provide many options to change the way data is presented. They enable you to completely change the design (font, colors,
530 ❘ Chapter 15 Working with Data — Advanced Topics
spacing, and so on) of the data they are presenting. Additionally, you can tweak these controls to hide specific columns, modify column headers, and even change the look and feel of the controls programmatically. In the next sections you see how to style your controls using a variety of techniques. Later sections in this chapter show you how to hand-code your data access pages, giving you great flexibility. Near the end of the chapter, caching — a technique to improve the performance of your web site — is discussed.
Formatting Your Controls Using Styles Chapters 13 and 14 explained how to work with the numerous data-bound ASP.NET 4 controls. You learned how to display and edit lists of data with controls like GridView, Repeater, and ListView, as well as how to work with single record controls such as DetailsView. So far, you’ve relied on the built-in look and feel of the controls, which often results in dull and plain-looking screens. Figure 15-1 shows the GridView that you created in Chapter 13 to manage the genres in the Planet Wrox database. This control relies on the default settings of the browser to display text and links, which usually results in purple and blue links with the default font, such as Times New Roman. Additionally, the columns in the grid are just as wide as necessary to display the text they contain. It would be a lot easier on the eyes if you could present the GridView as in Figure 15-2 instead.
Figure 15-1
Figure 15-2
The column for the Edit and Delete links is now a little wider, separating it clearly from the actual content in the grid. The Id column has been hidden and the Name column has been made wider as well. The different colors for the header, footer, items, and alternating items make the data in the grid a lot easier to read. Because some genres have reviews attached to them, their Delete link is disabled. And finally, a little glyph has been added to the Sort Order heading to indicate the direction the column is sorted on. Changing the dull-looking GridView from Figure 15-1 into the snazzier one shown in Figure 15-2 can easily be accomplished with the use of ASP.NET styles and the many events that the data-bound
Formatting Your Controls Using Styles ❘ 531
controls fire. In the next section you see how to apply these styles to a single control in a page. In the section that follows you see how to move the styles to a theme, so styles can be reused more easily by all controls in a section of your site. You have already seen some styles at work in Chapter 7, where you used them to style the Menu and the TreeView controls. However, because styles are used so much for formatting data-bound controls, they really deserve their own topic.
An Introduction to Styles Many of the data-bound and navigation controls have a number of style properties that enable you to modify the look and feel of the control. For example, the GridView control has a RowStyle property that enables you to customize the look of an individual row in the grid. Likewise, the DetailsView has a CommandRowStyle property that is used to control the appearance of the command row that holds commands such as Insert, Delete, Cancel, and so on. Ultimately, each style property inherits from the Style class that lives in the System.Web.UI.WebControls namespace. Figure 15-3 shows you a filtered view of the diagram for this class with its most common properties visible. As you can see from their names, the properties of the Style class are used to change style-related information on the objects to which this class is applied. Each of these properties is eventually converted to a CSS property or an HTML attribute, such as background-color, border, and so on. Other styles, like the styles for the GridView control, add various layout-related properties, such as different options to control alignment. The following table lists the most important properties of the various Style-derived classes that are available. Note that not every property is available for every style. IntelliSense shows you exactly what properties you can use in a certain style.
Figure 15-3
Property
Description
BackColor ForeColor
Enable you to change the background and text color of the elements. They map to the CSS properties background-color and color, respectively.
BorderColor BorderStyle BorderWidth
Enable you to change the border of the element to which the style is applied. They map directly to their CSS counterparts border-color, border-style, and border-width.
CssClass
Enables you to assign a CSS class instead of inline style information. You should try to give preference to the CssClass property over the individual style properties because they minimize page bloat. You see how to use CssClass in a later exercise.
Font
Enables you to set the font for the element through the various subproperties like Font-Names, Font-Size, and Font-Bold. These properties end up as various CSS font properties, such as font-family, font-size, and font-weight. continues
532 ❘ Chapter 15 Working with Data — Advanced Topics
(continued) Property
Description
HorizontalAlign VerticalAlign
End up as align and valign attributes on the HTML element to which they are applied and enable you to control the alignment of the contents of the element. For example, you use HorizontalAlign to left-, center-, or right-align the text of the column headers of a GridView.
Wrap
Ends up as a white-space: nowrap; CSS declaration when set to False and determines whether a piece of text is allowed to wrap to a new line.
Height Width
Enable you to control the width and height of the elements to which they are applied and map directly to their CSS height and width counterparts.
The different data-bound controls all have a different set of styles, although they do share a few. The following table lists the available styles for the GridView and the DataList classes, and describes their purpose. The other data-bound controls have slightly different styles, but from their names you should be able to see what they do and determine what they are used for. Check out the MSDN documentation (at http://tinyurl.com/lsab9e) for a full description of the Style class. Another good way to learn more about the different styles that are available is by using Visual Web Developer’s Auto Format, which inserts a number of styles for you. You see later how to use and improve the styles that are generated by the Auto Format feature. Style
Description
RowStyle AlternatingRowStyle
Control the look of a single row in the GridView. By default, the RowStyle affects all rows and the AlternatingRowStyle can override the RowStyle on only even rows when it’s set. Because the DataList doesn’t work with rows, but with more generic data items, it has ItemStyle and AlternatingItemStyle properties instead, but their behavior is more or less the same.
SelectedRowStyle
Can be applied to selected rows, and gives you the opportunity to visually present selected rows differently from unselected rows. The DataList control has a SelectedItemStyle that serves the same purpose.
EditRowStyle
Can be applied to rows that are currently in edit mode. For example, when you click the Edit link for a row in the GridView on the Genres page in the Management section, the row switches to edit mode and this EditRowStyle is applied. To define the layout of an editable item in the DataList, you use the EditItemStyle instead.
Formatting Your Controls Using Styles ❘ 533
Style
Description
EmptyDataRowStyle
Enables you to define the look of the row that is displayed when the grid is bound to an empty data source. This style works together with the EmptyDataText property of the grid that contains the text displayed when there are no records, or with the EmptyDataTemplate that enables you to define your own custom template to be displayed when an empty data source is used. The DataList has no support for an EmptyDataRowStyle.
RowStyle FooterStyle
Control the appearance of the header and footer rows of the GridView.
PagerStyle
Enables you to influence the look of the pager bar displayed in the GridView when paging is enabled. The DataList does not support paging all by itself, and as such has no PagerStyle.
Collectively, these styles enable you to change the looks of the header and the entire column when the column is sorted in ascending or descending order. These styles are only available to the GridView control.
Some controls, like Repeater and ListView, have no built-in styles. Because these controls do not contribute any HTML to the page all by themselves and leave it up to you to define the look and feel in the numerous templates these controls have, there is no point in having separate styles; you can simply add the necessary style or class information to the elements you define in their templates. To show you how to use these styles with your controls, the next exercise guides you through the process of enhancing the GridView control in the Genres page of the Management section. In a later exercise, you see how to move the style-related information to a theme and CSS file to improve the reusability of the code and to reduce the amount of HTML sent to the browser on each request.
Try It Out
Applying Styles
In this Try It Out, you use the built-in formatting capabilities of VWD to change the appearance of the GridView control. You see how VWD creates the necessary styles for you, each with their relevant styling properties set. In a later exercise you see how to modify these styles to use the CssClass properties instead, resulting in a much easier-to-manage style set.
1 .
Open Genres.aspx from the Management folder of the main Planet Wrox application that you have been working on so far.
. 2
Switch the page into Design View and open the GridView control’s Smart Tasks panel. Make sure you open that of the GridView and not the one for the surrounding Content block.
534 ❘ Chapter 15 Working with Data — Advanced Topics
3.
At the top of the panel, click the Auto Format link.
. 4
From the list of format schemes on the left, choose Classic. The Preview on the right is updated and now looks like Figure 15-4.
Figure 15-4
5.
Click OK to have VWD generate the necessary templates for you. The GridView is updated in Design View immediately, showing the selected format scheme.
. 6
Switch back to Markup View and inspect the various styles that have been generated. You should see the following styles, some placed before and others placed below the element: ... <EditRowStyle BackColor=”#2461BF” /> ... Some styles are not shown here to save some space <SortedDescendingHeaderStyle BackColor=”#4870BE” />
7.
Save the changes to the page and request it in the browser by pressing Ctrl+F5. You should see the list of genres with the selected formatting scheme applied.
. 8
Open the HTML source for the page by right-clicking the page in the browser and choosing the View Source or View Page Source command. Scroll down a bit until you see an HTML table with its id set to cpMainContent_GridView1. You’ll see that the table itself has a style attribute that sets text color and border properties:
Formatting Your Controls Using Styles ❘ 535
Additionally, you see that the numerous child elements of the table (table rows and anchor elements) all have different style settings applied. For example, odd and even rows now have the following style applied:
...
...
Click on the headers of the GridView a few times. The header and the column change color to indicate the column is now sorted.
How It Works The different style elements you created in step 5 are converted into their CSS and HTML equivalents. For example, RowStyle and AlternatingRowStyle have their BackColor set to a different background color:
When the control renders its HTML it applies these backgrounds to the table row of the items and alternating items:
...
...
The same principle is applied to the other styles in the GridView. If you look at the source of the page in the browser, you see a lot of page bloat, because each individual row has its properties set. This increases the page size, especially with larger results displayed in the GridView. To decrease the page size and improve the performance of the page, you should move the style definitions to a page theme and then use CSS and jQuery instead. You see how to do this next.
Combining Styles, Themes, and Skins Chapter 6 discussed how to create consistent-looking web pages using master pages, themes, and skins. With the basic theme infrastructure set up, it’s now easy to add a new theme that applies to the entire Management section. Earlier you saw how to create a skin file to change the appearance of a button; in the following exercise you see how to reuse this concept to create a skin file for the GridView, enabling you to style all GridView controls in the Management folder in one fell swoop.
Try It Out
Creating Advanced Style Solutions
In this Try It Out you move the various Style properties from the Genres.aspx page into a separate .skin file. You also move the inline style information to a separate CSS file. You then use some jQuery to separate data and appearance of the page even further.
1 .
On the Solution Explorer, right-click the App_Themes folder, choose Add ASP.NET Folder ➪ Theme, and type Management as the new theme name.
536 ❘ Chapter 15 Working with Data — Advanced Topics
2.
Right-click this new folder and choose Add New Item. Add a skin file called GridView.skin. You should end up with a Solution Explorer looking like Figure 15-5.
. 3
Open the Genres.aspx page in Markup View and delete all the styles you created in the previous exercise except the HeaderStyle, the PagerStyle, the SortedAscendingHeaderStyle and the SortedDescendingHeaderStyle. From the four remaining styles, remove all attributes and replace them with a single CssClass attribute named after the style and prefixed with GridView. You should end up with the following styles:
Don’t worry if VWD adds red error lines under the CSS class names. Because the CSS classes aren’t defined yet, it can’t find them. Later you will add them to the theme’s CSS file, where VWD still can’t find them. They’ll work fine at runtime, though, so don’t worry. . 4
Select the styles in the code editor and then cut them to the clipboard using Ctrl+X. Switch to the file GridView.skin, delete all existing code (the comment text you saw earlier), and paste the styles into the skin file.
. 5
Wrap the styles in an element with its runat attribute set to server and its CssClass attribute set to GridView. Don’t add an ID attribute, because skin files don’t need this. You should end up with this code: ... styles go here
6.
Open the resources folder for this chapter (located at C:\BegASPNET\ Resources\Chapter 15 if you followed the instructions in the Introduction section of this book) in Windows Explorer, select the Images folder and the Management.css file and press Ctrl+C to copy them. Switch back to VWD, click the Management folder under App_Themes and press Ctrl+V. Just as the other two themes, the management theme now has its own style sheet and Images folder, shown in Figure 15-6. The two images are used to change the header for sorted columns in the GridView as you’ll see later.
7.
Figure 15-6
Open the web.config file for the Management folder that you added earlier and set the theme to Management: <system.web> <pages theme=”Management”>
Formatting Your Controls Using Styles ❘ 537
8.
Open the file Management.master from the MasterPages folder, switch to Markup View, and below the ContentPlaceHolder in the section of the page drag the file jquery-1.4.1.min.js from the Scripts folder. VWD inserts the following <script> element for you: <script src=”../Scripts/jquery-1.4.1.min.js” type=”text/javascript”>
9.
Go back to Genres.aspx and in Markup View under the Columns element of the GridView control, delete the bound field for the Id column. Users typically don’t need to see the ID of items in the user interface because they are often meaningless to them. By removing the Id column, you reduce the noise in the page. Set the ItemStyle-Width for the CommandField to 100px and for the Name column to 200px. Finally, set ShowSelectButton of the CommandField to False, and set the HeaderText of the SortOrder field to Sort Order with a space between the words. You should end up with this GridView:
10. Scroll down to the end of the page in Markup View and right before the closing tag, add the following jQuery code wrapped in a <script> block: <script type=”text/javascript”> $(function() { $(‘.GridView tr:odd:not(.GridViewPagerStyle)’). addClass(‘GridViewAlternatingRowStyle’); });
11. Save all your changes by pressing Ctrl+Shift+S and then open Genres.aspx in the browser. You should now see the list of genres that was presented in Figure 15-2, except for the disabled Delete links, which you add later. Click the header of the Name or Sort Order columns to order the data in the GridView. Notice how the GridView now shows a little glyph besides the name to indicate the sort direction.
1 2.
Click Manage Reviews in the main Management menu to open the Reviews page. Select a genre from the drop-down list to display a list of reviews. Note that the Reviews list — visible in Figure 15-7 — now also has the same styles applied as the Genres list you saw earlier except for the alternating row styles applied by jQuery.
538 ❘ Chapter 15 Working with Data — Advanced Topics
Figure 15-7
How It Works The concepts from this exercise should be familiar by now. You have seen how to create and apply themes and skins in Chapter 6, and how to use the various control styles in the previous exercise. You also saw the concepts behind jQuery in Chapter 11. What may be new is the way that odd rows in the GridView are selected to dynamically change their background color, skipping the footer row using the not filter: $(‘.GridView tr:odd:not(.GridViewPagerStyle)’)
First, all odd table rows are selected using the selector .GridView tr:odd. However, depending on the number of rows in the GridView, this may also select the footer row (with the paging controls in it) because the footer is rendered as a
as well. To stop the footer from being included you use the not filter and pass it an expression on which you want to filter. In this case, the expression is .GridViewPagerStyle because that’s the class name applied to the footer row. The jQuery code is only applied to the Genres.aspx page but you could move it to the Management master page or copy it to individual pages. Either way, it helps in removing page bloat because you don’t have to add a style or class attribute to each row in the GridView. Instead, you can let jQuery figure out what rows are odd and even. If you want, you can create a ContentPlaceHolder in the master page for the Management section as you’ve done with the Frontend.master file. Assigning the image to the sorted column header requires changes to a few selectors. First, each sorted header (ascending or descending) is given some padding: .GridViewSortedAscendingHeaderStyle, .GridViewSortedDescendingHeaderStyle { padding-left: 20px; }
This moves the text in the header cell a bit to the right, making room for the image. Then for both the ascending and descending sort order, there is a separate selector that assigns the image. The selector is applied by ASP.NET by adding a class attribute to the relevant HTML elements. The following shows the selector for a column that is sorted in ascending order: .GridViewSortedAscendingHeaderStyle { background-image: url(Images/SortAscending.png); }
Handling Events ❘ 539
The .GridViewHeaderStyle th selector then stops the background image from repeating, positions the image near the top and determines the background color and text alignment: .GridViewHeaderStyle th, .GridViewPagerStyle { background-color: #BCD1FE; background-repeat: no-repeat; background-position: 0 5px; text-align: left; }
By moving your control style declarations to a separate skin file that in turn is part of a theme, you have created a very flexible, maintainable solution. If you want to see how the new styles are applied, open the source of the page in the browser using its View Source command. Instead of inline styles the relevant class attributes are applied. If you want to change the layout of all the GridView controls in the Management section, all you need to do now is modify the relevant CSS in the file Management.css. If you need to make changes to other styles, don’t forget to add them to the GridView.skin file first. Obviously, you can still tweak the controls at the page level. Though the skin defines the global look and feel of the GridView, you can still set individual properties on columns as you did with the ItemStyle-Width in the Genres page.
Although styles, skins, and themes are powerful tools to style your web pages, you’ll find that they are often an all-or-nothing solution. For example, if you create ItemStyle and AlternatingItemStyle elements (rather than using jQuery as you just did), they are applied to each and every row in the grid. What if you wanted to change the look and feel of just a few rows? Or what if you wanted to change some rows based on the actual data that the row is holding? You see how to accomplish conditional formatting and more using event handling in the following section.
Handling Events Previous chapters have covered how the ASP.NET controls can raise events. You learned how to handle these events with event handler code that you typically add to the page’s Code Behind file. For example, you wrote code to handle a Button control’s Click event. Additionally, in the preceding chapter, you learned how to react to various events — such as Inserting and Inserted — that happen just prior to and after interaction with the database. However, most controls expose a lot more events. A solid understanding of the various events that fire during a control’s life cycle and the order in which they fire is important knowledge for an ASP.NET developer. Being able to hook into the control’s life cycle, tweaking parts of the output as you go, enables you to create flexible, dynamic web pages that do exactly what you want. To gain an understanding of the various events and the order in which they fire, the next section explains the basic steps in the ASP.NET control life cycle. You won’t see every event that is fired in the process, but instead you see the ones you are most likely to use. Later sections then show you how to make use of these events to change the behavior of your web pages.
540 ❘ Chapter 15 Working with Data — Advanced Topics
The ASP.NET Page and Control Life Cycles Revisited In Chapter 6 you learned about the stages in a page’s life. You learned about different events such as PreInit, Load, PreRender, and Unload. Besides these events that are raised by the ASPX page, all the other controls in your ASPX pages can raise their own events. These events can be as simple as a Button control’s Click event (triggered by a user action) or be more complex events, such as Inserting, which is raised by controls like the EntityDataSource and the SqlDataSource, or the DataBound event that is raised by various data-bound controls. You see many of these events in the next exercise.
Try It Out
Seeing the Page and Control Life Cycles at Work
To give you an idea of the different events that you can hook into during a page or control’s life cycle and the order in which they fire, this Try It Out shows you how to set up a page that displays some data from the Genres table using an EntityDataSource. You also add a button to the page that you can use to trigger a postback to see how that influences things. You then hook up a number of event handlers to a few interesting events of the controls on the page so you can see in what order things are called.
1 .
Inside the Demos folder create a new file called Events.aspx. Make sure it’s based on your custom page template so it inherits from BasePage. Set the page’s Title to Events Demo.
. 2
Switch the page to Design View, drop a GridView into the cpMainContent placeholder, and then hook it up to a new EntityDataSource control using the GridView’s Smart Tasks panel. Use PlanetWroxEntities as the named Connection and bind the EntityDataSource control to the Genres entity set. There’s no need to set up insert, update, or delete behavior nor do you need to add a Where clause.
. 3
Back on the GridView control’s Smart Tasks panel, click Refresh Schema if the GridView doesn’t show the columns for the Id, Name and SortOrder. Then enable sorting by selecting the second check box. If the BoundField for the Id column doesn’t have a ReadOnly attribute, add it to Markup View and set its value to True. Finally, if your GridView doesn’t have a DataKeyNames attribute set to Id, add it manually. When you’re done, your code should look like this:
Handling Events ❘ 541
4.
Make sure you’re in Markup View and directly under the opening Content tag add the following markup that creates a table with one row and two cells, each with a large heading (h1) and a Label control called NoPostBack and PostBack, respectively.
No PostBack
PostBack
5.
Switch to Design View and below the GridView drop a Button control and double-click it in Design View to set up an event handler for its Click event in the Code Behind.
. 6
Switch back to Design View and double-click the gray and read-only area of the page to set up a handler for the Page control’s Load event.
7.
Switch to Design View again, click the GridView, and open its Properties Grid by pressing F4. Switch to the Events tab and double-click the following events to set up handlers for them in the Code Behind. After each handler, switch back to Design View by pressing Ctrl+Tab so you can add the next event.
8.
9.
➤➤
Sorted
➤➤
Sorting
➤➤
RowCreated
➤➤
DataBinding
➤➤
DataBound
➤➤
RowDataBound
Repeat the preceding step, but now set up the following events for the EntityDataSource control: ➤➤
ContextCreating
➤➤
Selecting
Make sure you are in Code Behind and below the last event handler (but still within the class definition), add the following method that writes some text to one of the two labels depending on whether the current page request is the result of a postback: VB.NET
Private Sub WriteMessage(ByVal handlerName As String) If Page.IsPostBack Then PostBack.Text &= handlerName & “ ” Else NoPostBack.Text &= handlerName & “ ” End If End Sub
542 ❘ Chapter 15 Working with Data — Advanced Topics
10. To each of the event handlers that you have set up, add the following code that calls your custom method and sends it the name of the event, which is then added to the two Label controls by the WriteMessage method. Don’t forget to replace the name of the event in the text with the actual event name: VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load WriteMessage(“Page_Load”) End Sub C#
11. Finally, add the following event handler to the page manually: VB.NET
Protected Sub Page_PreRenderComplete(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRenderComplete WriteMessage(“Page_PreRenderComplete -----------------”) End Sub C#
The PreRenderComplete event fires very late in the Page control’s life cycle, making it an ideal place to put a line at the bottom of the event list. That way you can clearly see what set of events belong to each other, which in turn helps you to figure out what events are triggered during page load or a postback. 12. Save all your changes and open the page in the browser. Besides the GridView with the available genres, you should also see a list with event names under the No PostBack heading: Page_Load EntityDataSource1_ContextCreating
Note that the RowCreated and RowDataBound events are repeated multiple times — once for each genre from the database plus two more. You see later why that is. Click the button below the GridView to cause a postback. The No PostBack label won’t change, but the PostBack label now shows the following list of event names: GridView1_RowCreated GridView1_RowCreated ... GridView1_RowCreated GridView1_RowCreated Page_Load Button1_Click Page_PreRenderComplete -----------------
Click one of the column headers of the GridView to order the data it is displaying. Notice that the second label’s text is extended with a second set of event names. Each set is separated by a line of dashes, created by the Page_PreRenderComplete event handler.
How It Works Technically, this exercise isn’t complicated. You set up a bunch of event handlers for the various controls in your page. Inside the event handler you call a method that checks whether the page is currently loading for the first time or is loading due to a postback. Finally, the code updates one of the two Label controls with the name of the event that triggered the event handler. What’s interesting about this exercise is the order in which the events occur. Take a look at the first list, displayed when the page first loads: Page_Load EntityDataSource1_ContextCreating EntityDataSource1_Selecting GridView1_DataBinding GridView1_RowCreated GridView1_RowDataBound GridView1_RowCreated GridView1_RowDataBound ... GridView1_DataBound Page_PreRenderComplete -----------------
544 ❘ Chapter 15 Working with Data — Advanced Topics
First Page_Load is triggered. Then the GridView sees that it’s hooked up to an EntityDataSource and asks that control for its data. This causes the ContextCreating and Selecting events to be triggered. When the GridView receives the data from the EntityDataSource, it fires its DataBinding event to signal it’s about to bind the data to the control. The GridView then starts to create rows. For each item in the data source, it creates a row, fires RowCreated, binds the item’s data to the row, and finally calls RowDataBound. If you carefully count the number of times that RowCreated and RowDataBound are called, you’ll notice they are being called two times more than the actual number of items that are in the data source. This is because the same event is also raised when the control creates its header and footer rows. You see how to distinguish between these rows inside an event handler in a later exercise. Finally, when the GridView is done creating and binding all the rows in the data source, it fires its DataBound event.
On postback, the story looks quite different. When you click the button to cause a postback, the following events are raised: ... GridView1_RowCreated GridView1_RowCreated GridView1_RowCreated GridView1_RowCreated Page_Load Button1_Click Page_PreRenderComplete -----------------
Note that there are no RowDataBound or DataBound events in this list and the EntityDataSource is also nowhere to be seen. The GridView is able to reconstruct the entire control from View State, eliminating the need to access the database again. While getting the data from View State, the GridView still needs to recreate each row in the grid, so you still see the RowCreated events. Toward the end of the list you see the Page_Load event followed by the Button control’s Click event. It’s important to understand and remember that user-triggered control events like a Button control’s Click or a SelectedIndexChanged of a DropDownList occur after the Load event of the Page. Note that this Load event isn’t the start of the Page’s life cycle. Before the Load event, the Page is already instantiated and has fired its Init event. At the end of the exercise, you clicked a column header to sort the data in the grid. This time, the GridView knows it must sort the data that is being displayed. It cannot do that itself, so instead it asks the EntityDataSource for a fresh copy of the data in the order the user requested. Just as the first time the page loaded, you see the various RowCreated and DataBound events appear.
If you want to see other events at work, simply repeat steps 7 and 10 of the preceding exercise, setting up handlers for the various events. To see the effect of View State, try disabling it either at the control level (for example for the GridView) or at the page level. In Chapter 18 you learn a technique called tracing that enables you to find out this information for all controls in your page, including the time it takes to execute the various events.
Handling Events ❘ 545
Although the preceding exercise is quite useless in a real-world application, it should help you gain an understanding of the various control events and the order in which they fire. You can use the exact same principles to hook into the page and make modifications to the page itself, or to any of the controls in the page. In the next exercise you see how to change the appearance of rows in the data source, depending on the data that you are displaying.
The ASP.NET Page Life Cycle and Events in Data Controls As discussed earlier, the GridView raises its RowCreated and RowDataBound events for each row it adds to its output. These events are ideal to peek into the data and then, based on that data, take appropriate action. For example, you can use these events to verify whether a review that is being displayed is authorized. If it’s not (meaning it won’t be visible in the front-end web site) you can change the review’s appearance to draw attention to it. Another example of using events would be to hide or disable elements in the interface when it doesn’t make sense for them to be visible or active. You see how to disable the Delete link in the Genres GridView in the next exercise.
Try It Out
Hooking into RowDataBound
In this Try It Out you write an event handler for the RowDataBound event of the GridView control in the Genres page of the Management section. Within this event, you can diagnose the data item that is being bound to the GridView row, enabling you to see if the genre has reviews attached to it or not. If there are reviews associated with the Genre, you use some code to disable the Delete link so users cannot accidentally try to delete that genre.
1 .
Open the page Genres.aspx from the Management folder in Markup View and locate the SqlDataSource control. Find the SelectCommand and modify the SQL statement so it reads like this: SelectCommand=”SELECT Genre.Id, Genre.Name, Genre.SortOrder, COUNT(Review.Id) AS NumberOfReviews FROM Genre LEFT OUTER JOIN Review ON Genre.Id = Review.GenreId GROUP BY Genre.Id, Genre.Name, Genre.SortOrder”
You can type the entire SQL statement on a single line or break it up over multiple lines as I’ve done here. . 2
Switch to Design View and open the GridView control’s Smart Tasks panel. If the control gives you an error about a missing Id property, click the Refresh Schema link on the Smart Tasks panel and answer No to the questions about regenerating fields and keys to maintain the current layout of the controls. Click Edit Columns on the Smart Task panel to bring up the Fields dialog. Click the CommandField item in the Selected Fields list and then click the blue link at the bottom right of the dialog to convert the field to a TemplateField. This way the column is expanded into a template, which makes it easier to access the controls, such as the Delete link, it contains. Click OK to dismiss the Fields dialog.
3.
In Markup View, locate the delete link (the one with its CommandName set to Delete) and change its ID to DeleteLink:
546 ❘ Chapter 15 Working with Data — Advanced Topics
4.
Switch to Design View and open the Properties Grid for the GridView and switch to the Events tab. Set up an event handler for the RowDataBound event.
. 5
At the top of the Code Behind of the Web Form, add the following line of code: VB.NET
Imports System.Data C#
using System.Data;
6.
Inside the event handler that VWD created for you, add the following code: VB.NET
Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _ Handles GridView1.RowDataBound Select Case e.Row.RowType Case DataControlRowType.DataRow Dim myRowView As DataRowView = CType(e.Row.DataItem, DataRowView) If Convert.ToInt32(myRowView(“NumberOfReviews”)) > 0 Then Dim deleteLink As LinkButton = TryCast(e.Row.FindControl(“DeleteLink”), LinkButton) If deleteLink IsNot Nothing Then deleteLink.Enabled = False End If End If End Select End Sub C#
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e) { switch (e.Row.RowType) { case DataControlRowType.DataRow: DataRowView myDataRowView = (DataRowView)e.Row.DataItem; if (Convert.ToInt32(myDataRowView[“NumberOfReviews”]) > 0) { LinkButton deleteLink = e.Row.FindControl(“DeleteLink”) as LinkButton; if (deleteLink != null) { deleteLink.Enabled = false; } } break; } }
7.
Save changes to all open files and then request Genres.aspx in the browser. Notice how for genres that have reviews attached to them, the Delete link is now disabled as shown in Figure 15-8.
Handling Events ❘ 547
Figure 15-8
How It Works Although short, this exercise demonstrates a powerful way to hook into the different events of a control and change the presentation of the underlying control. To see how it works, take a look at the modified SQL code first: SELECT Genre.Id, Genre.Name, Genre.SortOrder, COUNT(Review.Id) AS NumberOfReviews FROM Genre LEFT OUTER JOIN Review ON Genre.Id = Review.GenreId GROUP BY Genre.Id, Genre.Name, Genre.SortOrder
This modified SQL statement gets all the columns from the Genre table but introduces a new column, called NumberOfReviews, which contains the number of reviews associated with each genre. It does this by executing the SQL COUNT function against the Id column of the Review table. Because the SQL statement is grouped on all unique columns in the Genre table, you get a unique record including the count for each genre record, whether or not reviews are associated, as shown in Figure 15-9, which displays the result of this query in VWD.
Figure 15-9
548 ❘ Chapter 15 Working with Data — Advanced Topics
When this query is executed, the GridView in the markup of the page makes use of the first three columns, just as it did in the previous version of this page. But you can access the fourth column as well, which is done in the Code Behind, in the RowDataBound event to be exact, which fires for each row after the GridView is done binding the data for a specific row: VB.NET
Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _ Handles GridView1.RowDataBound Select Case e.Row.RowType Case DataControlRowType.DataRow ... End Select End Sub C#
The RowDataBound event gets passed an instance of GridViewRowEventArgs, a class that provides information about the row and data that are being bound at this stage. One of the properties of this class is the Row that represents the actual row that is being added to the GridView. This row in turn contains a RowType enumeration property that you can test to see what kind of row is being added. This enumeration contains six different members that map directly to the different types of rows the GridView can contain: DataRow for normal and alternating rows, EmptyDataRow for empty data rows, Header and Footer for the header and footer rows that are placed at the top and bottom of the GridView, and so on. Because you need to change the appearance of an actual data row, the code in the Case block only fires for normal and alternating rows. Inside the Case block, the following code is executed: VB.NET
Dim myRowView As DataRowView = CType(e.Row.DataItem, DataRowView) If Convert.ToInt32(myRowView(“NumberOfReviews”)) > 0 Then Dim deleteLink As LinkButton = TryCast(e.Row.FindControl(“DeleteLink”), LinkButton) If deleteLink IsNot Nothing Then deleteLink.Enabled = False End If End If C#
DataRowView myDataRowView = (DataRowView)e.Row.DataItem; if (Convert.ToInt32(myDataRowView[“NumberOfReviews”]) > 0) {
Handling Events ❘ 549
LinkButton deleteLink = e.Row.FindControl(“DeleteLink”) as LinkButton; if (deleteLink != null) { deleteLink.Enabled = false; } }
The DataItem property contains a reference to the data item object that is being bound. When you are using a SqlDataSource control, the DataItem is presented as a DataRowView, a .NET object that encapsulates a row returned from the database. The DataItem is therefore cast to a DataRowView object and then it’s indexed — using myRowView(“NumberOfReviews”) in VB.NET and myRowView[“NumberOfReviews”] in C# — to get the count of reviews from the NumberOfReviews column. If the count is larger than zero, it means reviews are associated with this genre and the Delete link must be hidden. Earlier you converted the CommandField to a template field, which added an explicit declaration for the Delete link in your code:
Using FindControl you can then get a reference to the Delete link, convert it to a proper LinkButton, and set its Enabled property to false. Because this code is also called when a row in the GridView is in Edit mode (by clicking the Edit link) you need to check if deleteLink is null (Nothing in VB.NET) or not. In case you’re editing, the GridView row does not contain the DeleteLink (as the EditItemTemplate is active, and not the ItemTemplate) and therefore FindControl returns null. When you disable a LinkButton by setting Enabled to False as in this example, ASP.NET applies a CSS class of aspNetDisabled: Delete
You can then style this disabled link with the CSS class (which you find in Management.css that you added earlier) and give it a gray color: a.aspNetDisabled { color : #CCC; }
With this code, you can easily prevent errors that may occur when you try to delete a genre that has associated reviews. However, you may not always be able to prevent an error from occurring during a CRUD operation against a data source control. For example, you may try to delete a genre that initially didn’t have any reviews attached. However, right before you try to delete the genre, somebody else inserts a new review for it. When you then try to delete the genre you’ll get an error because the genre is now linked to a review. In such cases, the data source controls enable you to diagnose the error that occurred and then take the necessary measures, like providing feedback to the users informing them that their CRUD operation didn’t succeed.
550 ❘ Chapter 15 Working with Data — Advanced Topics
Handling Errors that Occur in the Data Source Controls In Chapter 18 you see a lot more about recognizing and handling errors that occur in your ASP.NET pages. That chapter demonstrates how to catch errors that may occur in your code, and then handle them by logging them or by informing the user. But because the data source controls expose error information as well, it’s interesting to look at data access errors in this chapter. Both the EntityDataSource and the SqlDataSource control give you information about errors (exceptions in .NET parlance) that may occur during one of the four CRUD operations. With the EntityDataSource, the three events that occur after the database has been updated (Inserted, Updated, and Deleted) all provide access to an instance of a class called EntityDataSourceChangedEventArgs, whereas the Selected event gets passed an EntityDataSourceSelectedEventArgs. With the SqlDataSource control, all four events accept an instance of SqlDataSourceStatusEventArgs. Figure 15-10 shows these three EventArgs classes and their properties.
Figure 15-10
These classes share two important properties: Exception and ExceptionHandled. The first contains the actual exception that occurred or Nothing (in VB.NET) or null (in C#) when everything goes according to plan and no error occurs. You can examine this error and take appropriate action. For example, you can inform the user that something went terribly wrong, or you can send an e‑mail to the site’s web master informing her about the error so appropriate follow up action can be taken. If you decide to handle the error in the event handler of the data source control, you should set the ExceptionHandled property of the object to True. This signals to the ASP.NET runtime that you
are aware of the exception and have dealt with it adequately. If you omit setting this property, the runtime forwards the exception, which is eventually displayed to the user. In the following exercise you see how to make use of the SqlDataSourceStatusEventArgs class in the page Genres.aspx. Rest assured, you can apply the exact same principles from this section to events that are raised by the EntityDataSource control as well.
Try It Out
Handling Errors When Deleting Rows
In this Try It Out you see how to deal with exceptions that occur in a GridView when deleting rows. You’ll temporarily remove the code that disables the Delete link so you can delete genres with associated reviews. You then display an error message when a user tries to delete a genre that still has reviews attached to it. This exercise mainly serves to demonstrate how to handle exceptions that may be thrown
Handling Events ❘ 551
by the data source controls. From an end user’s perspective, disabling the Delete link when it’s not appropriate as you did in an earlier exercise should take care of the problem in most circumstances, but there are still chances of someone else inserting a new review before you try to delete that genre. 1.
Open Genres.aspx from the Management folder.
2.
Switch to Design View and from the Toolbox drag a Label control onto the GridView. This places the Label that will hold an error message above the GridView. Change the ID of the Label to ErrorMessage and clear its Text property. (Right-click the Text property label in the Properties Grid and choose Reset. This removes the entire Text property and its value from the control’s markup.) Set its CssClass to ErrorMessage. Finally, set its EnableViewState property to False to ensure the label doesn’t maintain its text after postbacks. You should end up with this code: -1 Then Using myEntities As New PlanetWroxEntities() Dim review = (From r In myEntities.Reviews
558 ❘ Chapter 15 Working with Data — Advanced Topics
Where r.Id = _id Select r).SingleOrDefault() If review IsNot Nothing Then TitleText.Text = review.Title SummaryText.Text = review.Summary BodyText.Text = review.Body GenreList.DataBind() Dim myItem As ListItem = GenreList.Items.FindByValue(review.GenreId.ToString()) If myItem IsNot Nothing Then myItem.Selected = True End If Authorized.Checked = review.Authorized End If End Using End If End Sub End Class C#
using PlanetWroxModel; public partial class Management_AddEditReviewHandCoded : System.Web.UI.Page { int _id = -1; protected void Page_Load(object sender, EventArgs e) { if (!string.IsNullOrEmpty(Request.QueryString.Get(“Id”))) { _id = Convert.ToInt32(Request.QueryString.Get(“Id”)); } if (!Page.IsPostBack && _id > -1) { using (PlanetWroxEntities myEntities = new PlanetWroxEntities()) { var review = (from r in myEntities.Reviews where r.Id == _id select r).SingleOrDefault(); if (review != null) { TitleText.Text = review.Title; SummaryText.Text = review.Summary; BodyText.Text = review.Body; GenreList.DataBind(); ListItem myItem = GenreList.Items.FindByValue(review.GenreId.ToString()); if (myItem != null) { myItem.Selected = true; } Authorized.Checked = review.Authorized; } } } } }
Hand-Coding Data Access Code ❘ 559
If you don’t feel like typing all this code, remember you can find a copy of it in the Chapter 15 folder of the source for this book that you can download from www.wrox.com. However, in realworld applications you typically need to type this code as well, so rather than copying and pasting it now, you’re better off finding the most efficient way to enter code like this, letting IntelliSense do most of the work for you. . 6
Switch back to Design View and double-click the Save button to set up a handler for the Button control’s Click event. Then back in the Code Behind add the following code to that handler: VB.NET
Protected Sub SaveButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles SaveButton.Click Using myEntities As New PlanetWroxEntities() Dim myReview As Review If _id = -1 Then ‘ Insert new item myReview = New Review() myReview.CreateDateTime = DateTime.Now myReview.UpdateDateTime = myReview.CreateDateTime myEntities.AddToReviews(myReview) Else ‘ update existing item myReview = (From r In myEntities.Reviews Where r.Id = _id Select r).Single() myReview.UpdateDateTime = DateTime.Now End If myReview.Title = TitleText.Text myReview.Summary = SummaryText.Text myReview.Body = BodyText.Text myReview.GenreId = Convert.ToInt32(GenreList.SelectedValue) myReview.Authorized = Authorized.Checked myEntities.SaveChanges() Response.Redirect(“Reviews.aspx”) End Using End Sub C#
protected void SaveButton_Click(object sender, EventArgs e) { using (PlanetWroxEntities myEntities = new PlanetWroxEntities()) { Review myReview; if (_id == -1) // Insert new item { myReview = new Review(); myReview.CreateDateTime = DateTime.Now; myReview.UpdateDateTime = myReview.CreateDateTime; myEntities.AddToReviews(myReview); } else // update existing item { myReview = (from r in myEntities.Reviews
560 ❘ Chapter 15 Working with Data — Advanced Topics
Open the file Reviews.aspx from the Management folder and replace the two occurrences of AddEditReview.aspx to AddEditReviewHandCoded.aspx. You should find one in the HyperLinkField for the Title and one in the Insert New Review link at the bottom.
. 8
Save all pending changes by pressing Ctrl+Shift+S. Then right-click the page AddEditReviewHandCoded .aspx in the Solution Explorer and choose View in Browser. You should see a screen that enables you to insert a new review, as shown in Figure 15-14, which shows the page in Opera 10.
Figure 15-14
9.
Remember you set the AccessKey property and underlined some letters? Together, they can be used to easily access controls in the page. In many browsers, pressing Alt plus the associated letter puts focus on the connected control. In this example, pressing Alt+T puts the focus on the Title field. This makes it easy for keyboard fanatics or disabled people to use your form without a mouse. Not all browsers use Alt+Letter, though. Opera requires you to press Shift+Esc first before you can choose a letter whereas Firefox uses Shift+Alt+Letter. Notice that the underlining of letters is just visual decoration to give a cue to users so they know what shortcuts they can use (just like Windows programs do). Without the underlining, the shortcuts still work.
Hand-Coding Data Access Code ❘ 561
10. Enter a new review, choose a genre, and click the Save button (or press the access key for the Save button). You’re taken to the Reviews page again. Open your genre, locate your review, and click its title. You’re taken to AddEditReviewHandCoded.aspx where all form controls should already be filled in, ready to be edited.
How It Works You actually coded quite a lot in this exercise and didn’t use many of the ready-made controls, other than those to let the user enter some details and to create the list of genres. Although hand-coding often means more work, you do gain a lot of flexibility and — when done right — you end up with a page that’s a lot easier to maintain. In this example, the markup section of the page is much easier to maintain than the previous version that used a DetailsView. Gone are the endless attributes on the controls, gone is the duplication that existed between the InsertItemTemplate and the EditItemTemplate, and gone is the awkward code to handle the UpdateDateTime in the Code Behind. What remains is a simple table-based presentation of the necessary form controls. Just like the Contact form you created in Chapter 9, it’s easy to modify this page, add Validation Controls from Chapter 9, and use CSS to change the appearance of the page. What may be new is the AssociatedControlID that you set on the Label controls. When you assign a control ID to this property of the Label control, it ends up like this in the browser: Title
This in turn tells the browser to put the focus on the Title text box when you click the Title label. The AccessKey properties of the server controls have been assigned to their client side counter parts:
The browser also puts focus on the control when you press the associated access key.
NOTE Rather than formatting the access keys yourself, you can also use a jQuery plug-in for this. Take a look at the Access Key Highlighter plugin at http://plugins.jquery.com/project/AccessKeyHighlighter if you’re interested in this solution.
Next up is the code in the Code Behind. I’ll discuss saving the form for a new review in the database first. After that I’ll show you how to load an existing review from the database and prepopulate the form. When you fill in the form’s controls and click the Save button, the code in the SaveButton_Click method fires. This code first creates a new PlanetWroxEntities object with a using block to enable you to interact with the database through EF. Then when the _id variable does not contain an ID of an existing review (you see how the ID is retrieved later), a new Review instance is created and added to the Reviews collection of the entities object. This would be the case when you are creating a brand new review using the Insert New Review link.
562 ❘ Chapter 15 Working with Data — Advanced Topics
Because the review needs a CreateDateTime and an UpdateDateTime, this code sets both. Notice how the UpdateDateTime is filled with the CreateDateTime so both contain the exact same date and time indicating the item hasn’t been modified yet. If the _id variable did contain a review ID (which means an existing review is being edited and saved), it’s queried from the database with a LINQ to Entities query: VB.NET
myReview = (From r In myEntities.Reviews Where r.Id = _id Select r).Single() myReview.UpdateDateTime = DateTime.Now C#
myReview = (from r in myEntities.Reviews where r.Id == _id select r).Single(); myReview.UpdateDateTime = DateTime.Now;
Whether or not an ID was passed to this page, at this stage the myReview variable contains a review object. The remaining code then fills the review’s properties by retrieving them from the relevant form controls. For the genre, the code directly assigns the GenreId property instead of querying a complete Genre instance and assigning it to the Genre property of the Review instance. This works because the model has support for foreign key columns, as you learned in the previous chapter. Finally, when the object is fully set up, the code calls SaveChanges on the PlanetWroxEntities object. This eventually sends a SQL INSERT or UPDATE instruction to the database to tell it to insert a new or update the existing Review record. After that, the user is redirected to the Reviews.aspx page using Response.Redirect: VB.NET
Obviously, once you’ve saved a review in the database, you can edit it again. Once you click one of the existing reviews in the reviews list at Reviews.aspx you’re taken to the Add/Edit page with the ID of the review in the query string. For example, browsing to http://localhost:1049/Management/ AddEditReviewHandCoded.aspx?Id=28 enables me to edit the Death Magnetic album by Metallica. The page is able to detect the ID in the query string using this code in Page_Load: VB.NET
If Not String.IsNullOrEmpty(Request.QueryString.Get(“Id”)) Then _id = Convert.ToInt32(Request.QueryString.Get(“Id”)) End If C#
if (!string.IsNullOrEmpty(Request.QueryString.Get(“Id”))) { _id = Convert.ToInt32(Request.QueryString.Get(“Id”)); }
Because this _id variable is assigned a value in Page_Load it can be used to load an existing item to display in the form, but also to get the item from the database in the SaveButton’s Click event (which you saw earlier.) If there is an ID (_id is assigned a value other than -1) the code sets up a new PlanetWroxEntities instance inside a using block and queries the Review using the following LINQ to Entities query: VB.NET
Dim review = (From r In myEntities.Reviews Where r.Id = _id Select r).SingleOrDefault() C#
var review = (from r in myEntities.Reviews where r.Id == _id select r).SingleOrDefault();
Once the Review is found in the database, its properties are used to prepopulate the form controls: VB.NET
If review IsNot Nothing Then TitleText.Text = review.Title SummaryText.Text = review.Summary BodyText.Text = review.Body GenreList.DataBind() Dim myItem As ListItem = GenreList.Items.FindByValue(review.GenreId.ToString()) If myItem IsNot Nothing Then myItem.Selected = True End If Authorized.Checked = review.Authorized End If C#
if (review != null) {
564 ❘ Chapter 15 Working with Data — Advanced Topics
The code checks if review is not Nothing/null before it tries to access its properties. The chances of the review being null in this example are pretty small because you access the Add/Edit page by clicking an existing item in the Reviews page so you can be pretty sure the item is there. However, this is not always the case, especially not in public-facing pages. Your clients may have a bookmark for a page with a specific ID in the query string. If you then delete that item from the database and your users access the page using the old bookmark, the review can’t be found and a so called Null Reference exception occurs. The same defensive coding mechanism is used to preselect the genre in the drop-down list. In this case you can be sure the Genre still exists in the database because there’s a relationship between the Id column of the Genre table and the GenreId of the Review table. However, checking to make sure an item exists in a DropDownList control before you try to select it is a best practice and helps in avoiding other Null Reference exceptions. Because the DropDownList with genres hasn’t been populated at this stage, you need to call DataBind() first. This forces the EntityDataSource control to get the genres and add them to the DropDownList. Afterward, the code can successfully find and preselect the appropriate item. Finally, when you click the Save button for an edited item the exact same code is fired that was used to insert a new item. If you were using validation controls (and you really should, as you learned in Chapter 9), you need to check whether or not the page is valid before you proceed with saving the Review instance: VB.NET
Protected Sub SaveButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles SaveButton.Click If (Page.IsValid) Then Using myEntities As New PlanetWroxEntities() C#
protected void SaveButton_Click(object sender, EventArgs e) { if (Page.IsValid) { using (PlanetWroxEntities myEntities = new PlanetWroxEntities())
This is really all there is to adding and editing new reviews using your own code against the Entities Framework. I realize it may look a little funky at first because you need to reset your head, forget about smart controls and their many properties and events, and think in straight code. However, EF makes this pretty straightforward and most of it comes down to querying entities, copying values from or to an object’s properties, and calling SaveChanges to propagate the changes back to the database.
Caching ❘ 565
Clearly, this is just the beginning. There’s a whole lot more you can do once you start writing your own code, whether or not it targets EF. For more information get yourself of a copy of Programming Entity Framework Second Edition by Julia Lerman or Professional ASP.NET 4 in C# and VB from Wrox (ISBN: 978-0-470-50220-4).
In all the database examples you have seen so far, the code accesses the database for each and every request. Every time some data needs to be displayed, it’s retrieved fresh from the database. Clearly, this can be a waste of time and resources like CPU cycles, especially if the data hasn’t changed since the last time you accessed it. In the final section of this chapter you are introduced to a technique called caching that can greatly improve the responsiveness and performance of your application.
Caching Caching is one of the best and often easiest ways to improve the performance of an application. It’s also an option that is too often overlooked by developers. With caching, a copy of your data is stored in a location that can be accessed very quickly. The idea with caching is that fetching data from the cache should be faster than regenerating or fetching it from the original data source. Therefore, most caching solutions store data in memory, which is usually the fastest way to get the data. The .NET cache is no exception, and enables you to store frequently accessed data in a special location in the computer’s memory.
Request for data
Data in cache?
No
Get data from source and store it in the cache
Yes Return data from the cache Figure 15-15
Typically, the caching principle takes the route displayed in Figure 15-15. The application queries for some data — for example, a list of genres from the database. Instead of accessing the database directly, the cache is examined to see if it already contains the requested data. If it does, the data is returned from the cache directly. If it’s not stored in the cache yet, it is retrieved from the data source (such as a SQL Server database), a copy of the data is stored in the cache for later retrieval, and finally the data is returned to the calling code. Though caching is generally a great solution to improve your application, it has a few drawbacks that you need to be aware of. The following section explains a few common pitfalls you can run into when using caching. The section that follows then shows the different caching mechanisms you have available in your ASP.NET web applications.
Common Pitfalls with Caching Data When working with cached data you typically encounter two common problem areas: ➤➤
You end up with stale — or out-of-date — data.
➤➤
You assume an item is present in the cache when it isn’t.
566 ❘ Chapter 15 Working with Data — Advanced Topics
In the next sections you see how to avoid these two problems.
Avoiding Stale Data Stale data is cached data that no longer matches its original source. For example, when you cache the results of a query for all the genres in the database and use that data instead of getting it fresh from the database, a new genre inserted by another user may go unnoticed. To avoid stale data you need a way to invalidate the cache. With cache invalidation, an item is removed from the cache so it can be re-created on the next request. To invalidate cached data, you have a number of options at your disposal. First of all, you can choose to set a short cache duration. For example, you could cache the Genres list for, say, 10 minutes. If another user inserts a new genre during those 10 minutes, it won’t show up in your pages. However, after 10 minutes, the list is removed from the cache and re-created with fresh data the next time it is requested. You see how to use time-based caching later. Another option to invalidate the cache is by using a cache dependency. With a cache dependency, you create a relationship between the cached item and the original data source. When the underlying data source changes, the cached item is invalidated so it can be re-created the next time it is requested. You see how to use the CacheDependency later. You can also use a cache dependency when using a database like Microsoft SQL Server. This means that as soon as the data that is part of the cached query is changed, the cached item is invalidated. Database caching and invalidation is an advanced and broad subject. Scott Hanselman wrote about it in the chapter that deals with caching in Professional ASP.NET 4 in C# and VB, published by Wrox (ISBN: 978-0-470-50220-4).
Don’t Rely on the Data Being There To minimize memory consumption for an application, the caching mechanism in ASP.NET automatically removes old and infrequently used items from the cache from time to time. Therefore, you shouldn’t rely on items being in the cache. They may have been removed by the cache itself when the ASP.NET runtime determined that the item is not used often enough and is thus taking up precious space unnecessarily. The entire cache is also cleared when the web application or web server restarts (which happens when you make changes to the web.config file for example). But items can also be removed because of their dependencies. Therefore, you should never rely on the item being in the cache, even if you set it there yourself earlier in the application’s life cycle. Later in this chapter you see how to use the Cache API (application programming interface; the way you can interact with a program) to use the cache programmatically. Using the Cache API is not the only way to use the cache. The next section shows you the different ways of caching data in ASP.NET.
Different Ways to Cache Data in ASP.NET Web Applications You can deploy a few different caching strategies in your ASP.NET applications, including output caching, caching with the built-in data source controls, and programmatic caching. All three options are discussed in the remainder of this chapter.
Caching ❘ 567
Output Caching With output caching, the end result of a rendered page is cached. This means that the very first time a page is requested, its final result is added to the cache. Subsequent requests to the same page result in the same HTML being sent. This last sentence is important enough to be repeated: Subsequent requests to the same page result in the same HTML being sent. This means the page is not processed at the server again, and no custom code in the Code Behind will fire. The exact same HTML from the first request is simply returned on each subsequent request. Enabling output caching is extremely simple; just add an OutputCache directive below your Page directive, as shown below this C# @ Page directive (you use the same code if you’re using VB.NET, but your @ Page directive may look slightly different):
The Duration is the number of seconds you want to cache the page before ASP.NET creates a new copy of it. In the preceding example, the page is cached for a minute. The None value in the VaryByParam attribute tells .NET to cache a single version of the page, no matter what query string values you pass to it. Although this is fine for relatively static pages like the About page in the Planet Wrox site, this is typically not an ideal solution for dynamic pages. Imagine a dynamic page that shows the details for a review based on the query string that is being passed to the page. The first time you request the page you may browse to something like: http://localhost:12345/Reviews/ViewDetails.aspx?Id=23
ASP.NET generates a page showing review 23 and then caches the entire output of the page. So what happens when you then request this page? http://localhost:12345/Reviews/ViewDetails.aspx?Id=33
Instead of seeing the review with an ID of 33, you end up with the same review with an ID of 23! To overcome this problem ASP.NET enables you to cache specific versions of a page. For example, you can instruct the page to cache a copy of the page for each unique query string field that it retrieves. You do this by setting the VaryByParam attribute of the directive to a comma-separated list of possible query string or form values. ASP.NET will cache a copy of the page for each unique combination of the fields it finds. As an example, consider a page that accepts the ID of a review in the query string and then displays its details. To cache a copy for each unique review, you add Id to the VaryByParam attribute like this:
For a page showing the details of a specific review, this is perfect. For each unique review, ASP.NET keeps a cached copy. This means that the database will only be hit the very first time a specific review is requested; subsequent requests will be served from the cache. One of the problems with output caching is that it’s often an all-or-nothing scenario. Although it’s easy to cache different pages based on, for example, a query string value, you’ll need to write custom
568 ❘ Chapter 15 Working with Data — Advanced Topics
code to handle other situations like dealing with themes. When a page is requested and cached for the first time, the user’s theme is taken into account. If subsequent users have a different theme set they still see the page in the originally requested theme. Although you could programmatically influence this behavior, there is an easier solution available: instruct the data source controls to cache data for you as you’ll see in the next section. For scenarios where this is not a problem (such as when you’re not using themes), output caching is an excellent mechanism to improve the performance of your site.
Caching with Data Source Controls The biggest benefit of caching with the data source controls is that they only cache dynamic, database-driven data, and not the entire page. That enables you to keep other parts of the page dynamic, such as a banner module or a personalized greeting welcoming the user. Caching is supported by design on most of the data source controls, except for the SiteMapDataSource, LinqDataSource, and EntityDataSource controls. Caching with the data source controls is very easy: all you need to do is set the EnableCaching property and then specify a CacheDuration. The following code snippet shows a SqlDataSource control that caches its data for 10 minutes:
What’s cool about caching with the data source controls is that they are smart enough to see if you are making updates to the underlying data. So, when you have set up a SqlDataSource control to cache data for the SelectCommand for 20 minutes, but then make a change to the data by using the InsertCommand, UpdateCommand, or DeleteCommand, the cache is invalidated automatically. This only works when you execute the insert, update, or delete command against the exact same SqlDataSource. If you have one page that displays and caches a list with reviews (such as All.aspx, for example) and then have another page that is used to insert a new review (such as AddEditReview.aspx in the Management folder) this won’t work. After you have inserted a new review in the Management section of the site, it won’t show up in All.aspx until the cache has expired. Besides the CacheDuration and EnableCaching properties, the data source controls provide more caching options. Professional ASP.NET 4 in C# and VB, published by Wrox, gives you a detailed look at the advanced caching capabilities found in ASP.NET 4. Code-free caching with the data source controls is useful in many situations. However, the data source controls cannot be used in every situation. What if you want to cache the results of data you get from a completely different source? What if you want to cache data you receive in a hand-coded page targeting the Entity Framework, or the contents of a text or an XML file that you frequently need to access? For those cases, ASP.NET gives you programmatic access to the cache.
Programmatic Caching With programmatic caching, you can store items in the cache through VB.NET or C# code. Obviously, you can also access them again later. To store items, you use the Add or the Insert method or you index the Cache collection directly. The Add method is quite powerful (and complex) and enables you to specify a host of options that determine how long the item is cached, what priority it should have compared to other cached items, and based on what factors the item must be removed from the cache.
Caching ❘ 569
The Insert method, on the other hand, is much easier. It has a few short overloads that enable you to specify the cached item and associate it with a specific key. You saw an example of this earlier in this section on caching. Another overload also enables you to define dependencies that can be used to invalidate a cached item when the original source is changed. This is great for caching files that don’t change very often. You can read a file from disk, and store it in the cache with a dependency on the original file. You then keep reading the file from the cache instead of from disk. When the file on disk is changed, the cached item is invalidated automatically and you can read the original source file again and store it in the cache. The following example shows how to modify the ContactForm. ascx user control to store and get the item from the cache until it changes: VB.NET
If Page.IsValid Then Dim mailBody As String = TryCast(Cache(“ContactFormMailBody”), String) If String.IsNullOrEmpty(mailBody) Then Dim fileName As String = Server.MapPath(“~/App_Data/ContactForm.txt”) mailBody = System.IO.File.ReadAllText(fileName) Cache.Insert(“ContactFormMailBody”, mailBody, New CacheDependency(fileName)) End If mailBody = mailBody.Replace(“##Name##”, Name.Text) ... End If C#
if (Page.IsValid) { string mailBody = Cache[“ContactFormMailBody”] as string; if (string.IsNullOrEmpty(mailBody)) { string fileName = Server.MapPath(“~/App_Data/ContactForm.txt”); mailBody = System.IO.File.ReadAllText(fileName); Cache.Insert(“ContactFormMailBody”, mailBody, new CacheDependency(fileName)); } mailBody = mailBody.Replace(“##Name##”, Name.Text); ... }
Notice how a new CacheDependency (for which you need to bring the System.Web.Caching namespace into scope with an Imports/using statement) is created and passed to the Insert method. This CacheDependency expects the name of the file it is dependent on. As soon as you change the file on disk (using VWD or Notepad, for example), ASP.NET removes the item from the cache so it can be read from the original source file again the next time this code is executed. You remove items from the cache using the Remove method that accepts the key of the cached item. You define this key when inserting the item using either Add or Insert. To access the items in the cache, you have a few options available. First of all you can directly access the Cache collection:
570 ❘ Chapter 15 Working with Data — Advanced Topics
VB.NET
myReview = TryCast(Cache(myKey), Review) C#
Review myReview = Cache[myKey] as Review;
Here, the Cache collection is indexed using the key stored in the myKey variable. Additionally, you can use the Get method that expects the key: VB.NET
myReview = TryCast(Cache.Get(myKey), Review) C#
myReview = Cache.Get(myKey) as Review;
Because Get is a method, the C# example now also uses parentheses around the cache key, making both examples look more like each other. Finally, you can access items in the cache using the Item property that also accepts the key of the cached item. All three ways to access items in the cache always return an object. That means that if you know the type you are getting back from the cache, you should cast it to the appropriate type using TryCast in VB.NET or the as keyword in C# before you can use its properties. The previous two examples show you how the item from the cache is cast to a strongly typed Review object first. To give you an idea of how to use the cache programmatically, the next exercise shows you how to insert a review in the cache, so you don’t have to get it from the database every time you need it.
Try It Out
Using the Cache API
In this Try It Out exercise you see how to cache a Review instance you get from EF using a LINQ to Entities query so it can be retrieved later by its key (that contains the review’s ID).
1 .
Start by adding a new page called ViewDetails.aspx in the Reviews folder of your web application. Make sure it’s based on your custom template. There’s no need to set an explicit title, because it will be set programmatically. Instead, remove the Title=”“ attribute from the Page directive. There’s a funny and old bug in ASP.NET that causes programmatic modifications to the page’s Title not to stick when this attribute is set to an empty string in Markup View.
. 2
In Markup View, add three Label controls to the cpMainContent content placeholder and name the controls as follows: ➤➤
TitleLabel
➤➤
SummaryLabel
➤➤
BodyLabel
Delete the Text attribute and its value of all three labels.
Caching ❘ 571
3.
Wrap the TitleLabel label in an element, and set the CssClass property of the SummaryLabel control to Summary. You should end up with this code:
4. 5.
Switch to Design View and double-click the read-only area of the page to set up a handler for Page_Load.
Add an Imports/using statement at the top of the page for the PlanetWroxModel namespace as you’ve done before in AddEditReviewHandCoded.aspx and then add the following code to the Page_Load event handler that has been created for you: VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Dim reviewId As Integer = Convert.ToInt32(Request.QueryString.Get(“ReviewId”)) Dim myReview As Review = TryCast(Cache(“Reviews” + reviewId.ToString()), Review) If myReview Is Nothing Then Using myEntities As New PlanetWroxEntities() myReview = (From r In myEntities.Reviews Where r.Id = reviewId Select r).SingleOrDefault() If myReview IsNot Nothing Then Cache.Insert(“Reviews” + reviewId.ToString(), myReview, Nothing, DateTime.Now.AddMinutes(20), System.Web.Caching.Cache.NoSlidingExpiration) End If End Using End If If myReview IsNot Nothing Then TitleLabel.Text = myReview.Title SummaryLabel.Text = myReview.Summary BodyLabel.Text = myReview.Body Title = myReview.Title MetaDescription = myReview.Summary End If End Sub C#
protected void Page_Load(object sender, EventArgs e) { int reviewId = Convert.ToInt32(Request.QueryString.Get(“ReviewId”)); Review myReview = Cache[“Reviews” + reviewId.ToString()] as Review; if (myReview == null) { using (PlanetWroxEntities myEntities = new PlanetWroxEntities()) { myReview = (from r in myEntities.Reviews where r.Id == reviewId select r).SingleOrDefault();
572 ❘ Chapter 15 Working with Data — Advanced Topics
Open the page All.aspx from the Reviews folder and delete the GridView that you created in the previous chapter. Replace it with a simple Repeater control that contains a single HyperLink into your new details page:
7.
Switch to the Code Behind of the page and replace the last two calls that used the GridView so they end up using the Repeater control instead: VB.NET
Open Styles.css from the Styles folder and add the following CSS declaration to the end of the file: .Summary { font-style: italic; display: block; }
Caching ❘ 573
9.
Save all changes and then request All.aspx from the Reviews folder in your browser. Click the title of a review and you’re taken to ViewDetails.aspx with the ID of the requested review in the query string.
COMMON MISTAKES If you see the error message about an invalid page title, make sure you removed Title=”“ from the Page directive in ViewDetails.aspx. With the attribute set to an empty string, the title you set in Code Behind won’t stick and then your BasePage will raise an exception because the title is missing.
You should now see the details of the review displayed on the page. Press Ctrl+F5 or Ctrl+R to refresh the contents of the page. Although you don’t see the difference, the review now comes from the cache. If you want to confirm this is really the case, you can add a Label control to the ViewDetails.aspx page and then update it with a different text depending on whether or not the item was in the cache: VB.NET
Label1.Text = “In the cache” If myReview Is Nothing Then Label1.Text = “NOT in the cache” Using myEntities As New PlanetWroxEntities() C#
Label1.Text = “In the cache”; if (myReview == null) { Label1.Text = “NOT in the cache”; using (PlanetWroxEntities myEntities = new PlanetWroxEntities())
This code initially sets the Label’s Text to “In the cache”. However, if the item isn’t found, the Label is updated to reflect that observation.
COMMON MISTAKES If you ever get the error “Sequence contains no elements” on the page, make sure you’re passing a valid Review ID in the ReviewId query string parameter to the ViewDetails.aspx page. You usually get this error when you browse to the details page directly from within VWD using Ctrl+F5 rather than through All.aspx.
How It Works In the page ViewDetails.aspx you first added a few labels that hold relevant properties of the Review, such as its Title, Summary, and Body. You assigned the Label control for the summary a CssClass so you can influence its styling from your CSS files. The .Summary selector assigns an italic font and sets the display property to block, forcing the body text that follows on its own line.
574 ❘ Chapter 15 Working with Data — Advanced Topics
The code in the Code Behind then starts by looking at whether or not it can retrieve the item from the cache: VB.NET
Dim myReview As Review = TryCast(Cache(“Reviews” + reviewId.ToString()), Review) C#
Review myReview = Cache[“Reviews” + reviewId.ToString()] as Review;
As the key for the cached item the code uses a combination of the word Reviews and the Id of each item. This gives each review a unique key to be used for the cache. If the item cannot be found in the cache (possible because you’re loading the page for the first time or because ASP.NET removed it), the TryCast method in VB.NET and the as keyword in C# return Nothing/null. So, by checking the myReview variable for that value you can determine whether the item was in the cache. If it was, you’re pretty much done, but if it wasn’t you need to get it from the database using a LINQ to Entities query similar to those you’ve seen before. Note that the query uses the SingleOrDefault() operator to limit the query to a single Review instance because there should only be one by the given ID, or to Nothing/ null when the item could not be found. What you do when the item is not found is up to you; you can display an error message in a Label control informing the user that the item is no longer available, or you can redirect to the home page or another page in your site. After the item is retrieved from the database, it’s inserted into the cache with the following code: VB.NET
The first parameter of the Insert method is the cache key and the second parameter is the object you want to cache: the actual Review instance in this case. The third parameter defines the absolute expiration date: the date and time at which the item is considered outdated and has to be removed from the cache. In this example this date is constructed by adding 20 minutes to the current date and time, meaning the item will be cached for a maximum duration of 20 minutes. The final parameter can be used to extend the expiration time every time the item is accessed. This is a great way to cache frequently used items and ensures that items that are not used often are removed from the cache sooner. However, this example is using an absolute expiration date, which means you have to pass the constant value of System.Web .Caching.Cache.NoSlidingExpiration as the sliding expiration parameter because the two parameters are mutually exclusive. At this stage, if the item exists in the database, you have a valid Review instance, whether or not it came from the cache. This instance is then used to fill the Label controls in the page, and the page’s Title and MetaDescription properties: VB.NET
TitleLabel.Text = myReview.Title ...
Practical Data Tips ❘ 575
Title = myReview.Title MetaDescription = myReview.Summary C#
TitleLabel.Text = myReview.Title; ... Title = myReview.Title; MetaDescription = myReview.Summary;
Setting the Title and MetaDescription is good for your users and for the ranking of your page in search engines. The title is used when bookmarking a page, so a clear title helps the user find your page again. Search engines such as Google and Bing use the title in their evaluation of what the page is about. They use the text that you set in the MetaDescription (which ends up as a <meta name=”description” /> element in the section of the page’s HTML) to present the results to the user. This means the text you enter there is often your first point of contact with a user that uses a search engine. As such it’s an important piece of information. Rather than reusing the Summary property for this purpose, you could add an additional column (called SearchEngineDescription, for example) to the Reviews table in the database. You then need to bring this column into the ADO.NET Entity Data Model by right-clicking the EDMX model diagram in the Entity Designer and choosing Update Model from Database. Once you’ve added this property to the model, don’t forget to change the edit pages in the Management section (AddEditReview.aspx or AddEditReviewHandCoded.aspx) so they support this new property as well. Finally, in the ViewDetails.aspx page you can then assign its value to the MetaDescription property of the Page class. Besides the new MetaDescription property, the Page class has also been extended with a MetaKeywords property in ASP.NET 4. This property works more or less the same as the MetaDescription and enables you to set the keywords for the page. Although the importance of keywords to influence search engine ranking is heavily debated (most say they are not used by search engines at all), it can’t hurt to set them. You could add the keywords to the database and model by following the same steps as outlined for the SearchEngineDescription property. For a lot more tips on search engine optimization (SEO) considering getting a copy of Wrox’s Professional Search Engine Optimization with ASP.NET: A Developer’s Guide to SEO (ISBN: 978-0-470-13147-3.) The ViewDetails.aspx page is now a pretty well-performing page; the first time it loads the item is retrieved from the database and stored in the cache. On subsequent visits to the page the database is no longer accessed but the item is retrieved from the much faster cache.
Practical Data Tips Here are some practical tips on working with data in your ASP.NET web sites: ➤➤
Whenever you use the numerous style properties of the data-bound controls, consider using the CssClass property instead of setting the individual style properties directly.
576 ❘ Chapter 15 Working with Data — Advanced Topics
➤➤
The section about the control’s life cycle has an exercise that shows you how to display the various events and the order in which they occur. You could extend the example and write code for even more events. Additionally, you could add more controls to the page and handle their events as well to help you establish a solid understanding of those events. Because a good understanding of those events and their order is often critical in writing web applications, the time you put into this little research project is well spent.
➤➤
Whenever you are writing pages that access a database or other slow or scarce resources like files or web services, consider whether they can benefit from caching. Although it’s not that hard to add caching at a later stage, it’s best to put it in as early as possible.
➤➤
Consider hand-coding complex data access pages. Though more difficult to write at first, you’ll end up with pages that are easier to maintain in the long run.
Summary This chapter covered some of the more advanced topics on presenting data with the data controls that earlier chapters deliberately skipped to enable you to focus on the core data access concepts. The chapter started off with a good look at the numerous style elements that most data-bound controls have to influence their appearance. You then learned more about the numerous events that controls can fire. These events can be used to programmatically change the appearance of the controls. Therefore, a solid understanding of the page’s life cycle is important knowledge. The chapter closed with a discussion of the various caching capabilities that ASP.NET supports to help you improve the performance of your web sites. With caching, you have come to the end of this chapter, which showed you some advanced topics on handling data in an ASP.NET web application. The following chapter shows you how to protect some of this data — for example, the Management folder — from unauthorized users by implementing ASP.NET security.
Exercises
1.
Imagine you have a simple Web Form with a single Button on it. If you click the Button in the browser, it causes a postback and at the server its Click event is triggered. What happens first? The Page’s Load event or the Button control’s Click event?
2.
Right now when you insert or edit an item on AddEditReviewHandCoded.aspx you’re taken back to Reviews.aspx when you’re done. It would be nice if the genre for the new or updated review item would already be preselected in the drop-down list. What code do you need to write for this?
3.
What’s the proper way to avoid an exception that you handled in a data-bound control’s event in the Code Behind from being displayed in the page? Answers to Exercises can be found in Appendix A.
Summary ❘ 577
⊲⊲ What You Learned in This Chapter Access keys
A way to assign letters to HTML controls to put focus on these controls by pressing a key combination including the assigned letter
ASP.NET styles
Control properties that inherit from the Style class and that let you change the appearance of controls
Caching
A technique used to store copies of data in a location that is quicker to access than the original source in order to improve performance
Cache invalidation
A mechanism where items are removed from the cache when they are no longer valid
Exception
The .NET term for an error. Exceptions are discussed in great detail in Chapter 18
Foreign key constraint error
An error that occurs at the database level when you try to delete a record that other records depend on
MetaDescription MetaKeywords
These two properties on the Page class (both new in ASP.NET 4) enable you to set metadata for the page in the browser that can be used by search engines
Output caching
A form of caching where an entire page or a user control is cached to prevent it from being generated from scratch every time it’s accessed
Stale data
A cached copy of some data that no longer accurately represents the original data cache invalidation
16
Security in Your ASP.NET 4 Web Site What You Will Learn in This Chapter: ➤➤
Important terminology you’ll encounter when dealing with security
➤➤
The ASP.NET application services that drive the security model of ASP.NET
➤➤
How you can let users sign up for an account for your site
➤➤
How users can reset their passwords or request new ones
➤➤
How you can manage the users and roles in your database at development time
➤➤
How you can present different content to different users based on their access rights in the system
Until now you have been creating pages in your web site that are accessible to all visitors to your site. There is yet no way to block certain resources like ASPX files or even whole folders for specific users. That means, for example, that currently anyone can access your Management folder and start messing with the genres and reviews in the system. Clearly this is not something you’d want in a production web site. So, you need to think of a good security strategy to stop unwanted users from accessing specific content. You also need to look at a mechanism that enables users to sign up for a new account and at the same time enables you to designate certain users as managers of your web site and grant them special access rights. ASP.NET 4 ships with all the tools you need to create a solid and safe security mechanism. In this chapter you learn how to make use of these tools in your ASP.NET web site.
580 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
Before you start looking at how security is implemented in the ASP.NET Framework, you need to understand a few important terms that you’ll come across in every discussion on security.
Introducing Security Although security can be quite a complex subject, it often revolves around three straightforward questions: ➤➤
Who are you?
➤➤
How can you prove that?
➤➤
What are you allowed to do in the system?
Identity: Who Are You? An identity is what makes you, you. The answer to what an identity is depends on the context it is used in. As a citizen of a country, your identity revolves around your person, your official name and birth date, and maybe even a Social Security number. However, for a web site like p2p.wrox.com, Wrox’s community web site, your identity may be as little as your e‑mail address. No matter what you include in an identity, it is a way to refer to you. But how does anyone else know you? And how can they be sure it’s really you when you log on to a web site, for example? This is where authentication enters the game.
Authentication: How Can You Prove Who You Are? Authentication is about providing evidence about who you are. When you need to register for a library card, you may need to show your passport to prove that the name you register the card under really belongs to you. With a web site like p2p.wrox.com you need to provide an e‑mail address and a password. Together these two pieces form the evidence that proves your identity. Many other mechanisms are used for authentication, including high-tech fingerprint or iris scans, smart cards and tokens (where the evidence is stored on something tangible), and so on. However, in light of the discussion on security of ASP.NET web sites, this chapter sticks to a user name and password for authentication.
Authorization: What Are You Allowed to Do? Depending on who you are, a system grants you more or fewer privileges to access certain areas. Think about the highly secured headquarters of a national security agency in an action movie, for example. Even if the main character is allowed to enter the building, he is often not allowed to enter specific areas because he lacks the proper authorization (the fact that the hero eventually gains access in those movies using a two-minute hack in the system is beside the point here). To determine what a user is allowed to do, a system needs to know two things: the permissions for the current user and the authorization rules for the resource a user is trying to access.
Introducing Security ❘ 581
The permissions for the user are based on its user name (the identity it represents) and the roles (or security groups) the user is optionally assigned to. Similarly, resources can be opened up or blocked for specific users or roles. When there is a match between the current user and the access rules for the resource a user is trying to access, the user is granted access. If the user is blocked specifically, access is denied. Imagine a file that is only accessible to the user Tom and the group Developers. The user Tom can access that file, regardless of whether he is in the Developers role. At the same time, the user Charlotte must be in the Developers role in order to access the file. You see how to work with these concepts in the remainder of this chapter. A large part of these security concepts in ASP.NET are implemented with the so-called application services, discussed next.
An Introduction to the ASP.NET Application Services Versions of ASP.NET before ASP.NET 2.0 had some support for security. However, they lacked the high-level controls and concepts that ship with ASP.NET 2.0 and later. In ASP.NET 1.x applications you needed to write a lot of code to implement a solid security strategy. The downside of writing this code is that it was often pretty much the same in all your web sites. You were more or less forced to write the same code over and over again to implement a security mechanism. These problems were solved in ASP.NET 2.0, which shipped with the application services: a set of services you can use in your web application to support management of users, roles, profiles, and more. These services are still strongly present in ASP.NET 4. ASP.NET 4 ships with a number of application services, of which the most important ones are: ➤➤
Membership: Enables you to manage and work with user accounts in your system.
➤➤
Roles: Enables you to manage the roles that your users can be assigned to.
➤➤
Profile: Enables you to store user-specific data in a back-end database.
Figure 16-1 gives an overview of these services and shows how they are related to your web site and the underlying data stores that the services may use.
ASP.NET Services
Role Manager
Other Services
SqlMembershipProvider
SqlRoleProvider
Other Providers
Data Stores
Membership
Providers
ASP.NET 4 Web Applications/Web Sites
Sql Server
Figure 16-1
Active Directory
Oracle
Microsoft Access
Other Data Stores
582 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
At the top of the diagram you see the ASP.NET 4 web sites and web applications that represent the web sites that you build. These web sites can contain controls like the login controls (discussed next) that in turn can talk to the ASP.NET application services such as membership and profile. To create a flexible solution, these services don’t talk to an underlying data source directly, but instead talk to a configured provider. A provider is an interchangeable piece of software that is designed for a specific task. For example, in the case of the membership services, the membership provider is designed to work with users in the underlying data store. You can configure different providers for the same application service depending on your needs. ASP.NET ships with a SQL Server provider that enables your membership services to talk to a SQL Server database. This is an ideal provider for Internet-connected web sites like PlanetWrox.com. It also ships with an Active Directory provider that lets you create and manage users in Active Directory on Windows instead. This provider is best suited in closed networks, like an intranet. If you have your own custom data store you could write your own provider and plug it into your web site to replace one of the default providers with very little effort. The beauty of these providers and their underlying model is that you can swap them through configuration without any changes to your programming code. Each provider needs a data store — represented by the bottom part of the diagram in Figure 16-1 — and is written to work with a specific data store. For example, the SQL Server membership provider (to handle membership services such as creating users, logging in, and resetting passwords) and the SQL Server role provider (to handle role-related tasks) are designed to work with a Microsoft SQL Server database. In the remainder of this chapter, you see how to use the SQL Server membership provider and the SQL Server role provider. In the next chapter you work with the SQL Server profile provider. All three providers can be configured to use a single SQL Server database, making it easy to centralize all your user data. Ideally, you don’t deal with these providers directly. Under normal circumstances, the various pro‑ viders are configured for your web site at a central location. You then use these providers by talking to the application services. Although you could access these services directly from code, you often use the ASP.NET built-in login controls to do the hard work for you. These controls are discussed next.
Introducing the Login Controls The login controls that ship with ASP.NET 4 take away much of the complexity usually associated with writing a security layer in a web site. The available login controls effectively encapsulate all the code and logic you need to validate and manage users. These controls work by communicating with the configured provider through the application services, instead of talking to a database directly. To see how this works, the following exercise shows you how to create a simple Login and Sign Up page that enables new users to create an account and then log in. The section that follows then looks at the seven login controls that ship with ASP.NET 4.
Introducing the Login Controls ❘ 583
Try It Out
Creating Login and Sign Up Pages
In this Try It Out you extend the Login page that you created earlier. You also create a new page that enables a user to sign up for an account on the Planet Wrox web site. 1.
Open up the page Login.aspx that you created in Chapter 6 from the root of the site in Markup View. (If you don’t have that page, create it now, based on your custom template, and set its Title to Log in to Planet Wrox. Add an element with the same text in the cpMainContent placeholder.)
. 2
From the Login category of the Toolbox drag a LoginStatus control and drop it in the page after the h1 element.
3.
Switch to Design View and from the Toolbox drag a Login control and drop it on the LoginStatus, so it ends up right above it. Both controls are visible in Figure 16-2 (the LoginStatus appears as a small Login link below the Login control).
Figure 16-2
4.
Open the Properties Grid for the Login control and set the two properties shown in the following table: Property
Value
CreateUserText
Sign Up for a New Account at Planet Wrox
CreateUserUrl
SignUp.aspx
5.
In the root of the web site create a new Web Form called SignUp.aspx based on your custom tem‑ plate and give it a Title of Sign Up for a New Account at Planet Wrox.
6.
Switch the page to Design View and from the Toolbox drag a CreateUserWizard control into the main content area for the page. Save and close the page.
584 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
7.
Open the web.config file from the root of the site and add an element with its mode attribute set to Forms as a direct child of the <system.web> element. <system.web> ...
8.
Go back to Login.aspx, save all your changes, and press Ctrl+F5 to open that page in your browser. You are greeted with a login box as shown in Figure 16-3. Note that the login status below the Login control is currently set to Login (as a call-to-action) to indicate you are not logged in yet. If the text says Logout instead, verify that you set authentication to Forms in the web.config file.
Figure 16-3
9.
Try to log in by entering a random user name and password. Obviously, this fails because the account doesn’t exist. It may take a while before you see the result because ASP.NET is busy set‑ ting up the membership database.
1 0.
Follow the Sign Up link below the Login control to go to SignUp. aspx and then create an account by entering your personal details (see Figure 16-4). By default, the password needs to have a mini‑ mum length of seven characters and must contain at least one nonalphanumeric character. Note that numbers are not considered non-alphanumeric characters so you need to make sure your pass‑ word contains at least one character like # or $ or *. For example, Pa55word is not a valid password, but Pass##Word will be accepted. Note that the password is case-sensitive. Write down the user name and password you just entered, because you’ll need this account information again later.
Figure 16-4
For the security question and answer, you can make up your own question with an answer. You need to supply the answer to the security question in case you lose your password.
1 1.
Click the Create User button to have the account created for you. When the page reloads, you get a confirmation that the account was created successfully. Ignore the Continue button for now (you haven’t written any behavior for it yet), but click the Login item from the main Menu or TreeView (depending on the theme you currently have selected) in the web site instead. You are taken to Login.aspx again where the LoginStatus control below the Login control now indicates that you are logged in (see Figure 16-5). When you create a new account using the CreateUserWizard, you’re logged in automatically, although you can change that behavior by setting the LoginCreatedUser property of the control to False.
Figure 16-5
Introducing the Login Controls ❘ 585
12. Click the Logout link and you’ll be logged out, causing the LoginStatus to display the text Login again. In the Login control, enter the user name and password you entered in step 10 and click the Log In button. You’re logged in and redirected to the home page. On the main Menu or TreeView click Login to return to the Login page again. Note that the LoginStatus has changed and now shows Logout again, illustrating the fact that you successfully logged in. At this stage, being logged in doesn’t add much value; all you see is the LoginStatus change from Login to Logout. However, later in this chapter you see how to offer different content to logged-in users.
How It Works Besides adding and configuring a few ASP.NET Server Controls, you didn’t do any coding in this exer‑ cise. Still, you were able to implement a fully functional login procedure that enables a user to sign up for an account and then log in to the site. So how does all this work? As you learned earlier, the ASP.NET controls talk to the configured application services providers; a software layer that sits between the login controls and the SQL Server database that keeps track of the users. The very first time you try to log in (or use other login controls that require database access), the pro‑ vider checks if your application is using a database with the necessary database objects, such as tables. By default, it checks the database by looking at a connection string called LocalSqlServer. You won’t find this connection string in your web.config file though because it is defined in a file called machine .config located in the central .NET Framework folder. You learn more about this file later. The con‑ nection string in that file looks like this:
This is a connection string that targets SQL Server 2008 Express Edition, the free version of Microsoft SQL Server that you installed in Chapter 1. In Chapter 19 and Appendix B you see more examples of connection strings that target the full versions of SQL Server. In Chapter 13 you learned that a connection string that uses |DataDirectory| points to a database located in the App_Data folder of your web site. If the ASPNETDB.MDF database doesn’t exist at
the specified location the first time you use one of the login controls or the other application services, it is created for you automatically by the application services (that’s why there was a delay when you entered the first user name and password in step 9). To find out what the database looks like, go back to VWD, click the App_Data folder, and then click the Refresh button on the toolbar of the Solution Explorer. You should see the new database called ASPNETDB.MDF as shown in Figure 16-6.
Figure 16-6
586 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
Double-click the new database in the Solution Explorer to open it in the Database Explorer window. Expand items such as Tables and Views to get a list of database objects that have been added for you, visible in Figure 16-7, which shows the tables used for the application services. After this database has been created successfully, the login controls can use it. For example, when you create a new account using the CreateUserWizard control, records are inserted in the aspnet_ Membership and aspnet_Users tables. Similarly, when you try to log in, your user name and password are checked against these tables. To force the ASP.NET runtime to use forms-based authentication, (which the SQL Server membership provider uses under the hood) you need to set the mode attribute of the authentication element to Forms in the web.config file:
Figure 16-7
Other options for the mode attribute include Windows (where security is handled by Windows itself), Passport (using Microsoft’s Passport services), and None, which disables security altogether. In the remainder of this book, you use the Forms option exclusively because it’s the most common solution for Internet-facing web applications. The Remember Me Next Time option of the Login control is more forgetful than you may think. When you check this option you are logged in automatically the next time you visit the site, provided your authentication cookie hasn’t expired. The first time you log in, the server sets a cookie that is saved for future sessions. However, this cookie expires after 30 minutes, which means a user returning to the site after that period needs to re-authenticate. To extend the period that users remain logged in you need to set the timeout attribute of the element that itself is a direct child of the element in web.config. The timeout takes an integer value representing the timeout period in minutes. The following code sets the timeout to 24 hours (1440 minutes):
Lower timeout values are generally considered safer because they don’t provide unlimited or long-last‑ ing access, but longer timeout values are more user friendly because users don’t need to re-authenticate every time they visit the site. The database that ASP.NET creates for you in the App_Data folder makes it very easy to work with the various application services in a development scenario. However, when you need to roll out your appli‑ cation to a production server in your own network or with your Internet service provider (ISP) you may need more control over the database and the way it is accessed. Chapter 19, which deals with deploy‑ ment, and Appendix B, which deals with SQL Server configuration, show this in more detail.
Introducing the Login Controls ❘ 587
Now that you have seen how the login controls work in conjunction with the auto-generated SQL Server Express database, it’s time to look at the controls in the Login category of the Toolbox in more detail.
The Login Controls ASP.NET 4 ships with seven login controls, each serving a distinct purpose. Figure 16-8 shows the Toolbox with the seven login controls. In the sections that follow, each of these controls is explained in more detail.
Login As you saw in the previous exercise, the Login control enables a user to log in to the site. Under the hood the control talks to the configured member‑ Figure 16-8 ship provider through the application services to see if the user name and password represent a valid user in the system. If the user is validated, a cookie is issued that is sent to the user’s browser. On subsequent requests the browser resubmits the cookie to the server so the system knows it’s still dealing with a valid user. The different settings for the membership provider are all configured in the <membership /> element of the web.config file. You learn more about this element later in this chapter. To create a fully functional Login page, you only need the following control declaration:
However, in most situations you want to enhance the appearance and behavior of the control by set‑ ting one or more of the properties shown in the following table. Property
Description
DestinationPageUrl
Defines the URL the user is sent to after a successful login attempt.
CreateUserText
Controls the text that is displayed to invite users to sign up for a new account.
CreateUserUrl
Controls the URL where users are taken to sign up for a new account.
DisplayRememberMe
Specifies whether the control displays the Remember Me option. When set to False or when the check box is not checked when logging in, users need to re-authenticate every time they close and reopen the browser.
RememberMeSet
Specifies whether the Remember Me option is initially checked.
PasswordRecoveryText
Controls the text that is displayed to tell users they can reset or recover their password.
PasswordRecoveryUrl
Specifies the URL where users are taken to get their (new) password.
VisibleWhenLoggedIn
Determines whether the control is visible when the current user is logged in. True by default.
588 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
The authentication mechanism of ASP.NET by default assumes you have a page called Login.aspx in the root of your site that is used to let users log in. To be functional, the minimum that this page requires is a Login control. If you want to use a different page, you can specify its path in the element under like this:
With this configuration you tell the ASP.NET runtime to load the page MyLoginPage.aspx whenever a user needs to present her credentials. Note that on the Login page (configured in the loginUrl) the VisibleWhenLoggedIn property of the Login control has no effect. On the configured Login page, the Login control is always visible. If you want to hide it you can use a LoginView control, as you see in a later exercise. In addition to these properties, the Login control has a range of Text properties, such as LoginButtonText, RememberMeText, TitleText, and UserNameLabelText that are used to set the text that appears in the control and in its various child controls like the Button and Label controls that make up the user interface. Just as with the data-bound controls, the login controls have numerous style properties that enable you to tweak their appearance. You’re encouraged to check out the Styles category of the Properties Grid for the controls to see how you can set the various styling options. Remember, just as with the data-bound controls, you can move much of the styling information to skin and CSS files. The Login control also exposes a few events that you typically don’t need to handle, but that can come in handy from time to time. For example, the LoggedIn event fires right after the user has logged in and is a good place to send the user to another page dynamically if the DestinationPageUrl is not flexible enough.
LoginView The LoginView is a handy control that lets you display different data to different users. It enables you to differentiate between anonymous and logged-in users, and you can even differentiate between users in different roles. The LoginView is template driven and as such lets you define different tem‑ plates that are shown to different users. The following table describes the two main templates and the special RoleGroups element. Template
Description
AnonymousTemplate
The content in this template is shown to unauthenticated users only.
LoggedInTemplate
The content in this template is shown to logged-in users only. This template is mutually exclusive with the AnonymousTemplate. Only one of the two can be visible at any time.
Introducing the Login Controls ❘ 589
Template
Description
RoleGroups
This template can contain one or more RoleGroup elements that in turn contain a ContentTemplate element that defines the content for the specified role. The role or roles that are allowed to see the content are defined in the Roles attribute, which takes a comma-separated list of roles. The RoleGroups element is mutually exclusive with the LoggedInTemplate. That means that if a user is a member of one of the roles for the RoleGroup, the content in the LoggedInTemplate is not visible. Additionally, only content for the first RoleGroup that matches the user’s roles is shown.
The LoginView control itself doesn’t output any markup other than the content you define in the various child elements of the control, which means you can easily embed it between a pair of HTML tags like and
to create customized headings or list items. The following code snippet shows a LoginView control that defines content for three different users: anonymous visitors to the site, logged-in users, and users that have logged in and are members of the Managers role: Hi there visitor. Would you be interested in signing up for an account? Hi there visitor and welcome back to PlanetWrox.com. Hi there manager. You can proceed to the Management section.
You see how to create and configure roles later in this chapter.
LoginStatus As demonstrated in the previous Try It Out exercise, the LoginStatus control provides information about the current status of the user. It provides a Login link when the user is not authenticated and a Logout link when the user is already logged in. You control the actual text being displayed by set‑ ting the LoginText and LogoutText properties. Alternatively, you can set the LoginImageUrl and LogoutImageUrl properties to display an image instead of text. Finally, you can set the LogoutAction property to determine whether the current page refreshes if the user logs out, or whether the user is taken to another page after logging out. You determine this destination page by setting the LogoutPageUrl.
590 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
Besides these properties, the control is capable of raising two events, LoggingOut and LoggedOut, that fire right before and after the user is logged out.
LoginName LoginName is an extremely simple control. All it does is display the name of the logged-in user. To embed the user’s name in some text, such as You are logged in as Imar, you can use the FormatString property. If you include {0} in this format string, it will be replaced with the user’s
name. You see how this works in the next exercise, which has you modify the login and master pages for the site so they display relevant information about the user.
Try It Out
Working with the Login Controls
In this Try It Out you hide the Login control on the Login.aspx page when the user is already logged in and display a message instead. Additionally, you add text to the footer of the page that displays the name of the user together with an option to log out again. 1.
Open Login.aspx and switch to Design View. From the Login category of the Toolbox, drag a new LoginView control on top of the Login control so it’s placed right above it in the page.
2.
Open the Smart Tasks panel of the LoginView control and make sure that AnonymousTemplate is selected in the Views drop-down list, visible in Figure 16-9. Any content you put in the control will be placed in the AnonymousTemplate area, because that is now the active tem‑
plate for the control in Design View. 3.
Click the Login control once to select it and then press Ctrl+X to cut it to the clipboard. Click inside the small white rectangle that represents the LoginView to position your cursor in the control and then press Ctrl+V to paste the Login control into your LoginView.
4.
Open the Smart Tasks panel of the LoginView again and switch to the LoggedInTemplate using the Views drop-down list. Click inside the small white rectangle of the control again and type the text You are already logged in.
. 5
Switch to Markup View and look at the code. The Login control should be placed inside the AnonymousTemplate, and the text you typed should be displayed within the LoggedInTemplate tags:
Figure 16-9
You are already logged in.
Introducing the Login Controls ❘ 591
6.
Save and close the page, because you’re done with it for now.
. 7
Open the master page Frontend.master in Markup View and locate the element with an id of Footer at the bottom of the page. Remove the text Footer Goes Here and replace it with a new LoginName control by dragging it from the Toolbox into the element. Set its FormatString property to Logged in as {0} by typing in the code directly.
8.
From the Toolbox, drag a new LoginView control and drop it below the LoginName control, but still in the Footer . Switch to Design View and on the Smart Tasks panel of the LoginView choose LoggedInTemplate from the Views drop-down list, and then in the white rectangle for the active LoggedInTemplate, drag and drop a new LoginStatus control.
9.
Switch to Markup View again and wrap the code for the LoginStatus in a pair of parentheses. You should end up with the following code: ()
10. Save all your changes and request Login.aspx in your browser. Log in with the account and pass‑ word you created in a previous exercise (you may need to log out first by clicking the Logout link). If you don’t recall the user name and password, simply click the Sign Up link to create a new account. Note that as soon as you are logged in, the footer displays the text visible in Figure 16-10. Figure 16-10
1 1.
Click the Login item in the Menu or TreeView to go to the Login page. Instead of the Login control you should now see a message indicating you are already logged in.
1 2.
Click the Logout link in the footer at the bottom of the page. The page refreshes and displays the Login control again. Additionally, the text from the footer has now disappeared.
How It Works You started by adding a LoginView to the Login page to wrap the Login control and a text message. The Login control is shown when the user is not logged in, whereas the text is displayed for logged-in users only. The code in the footer of the master page contains a LoginName control that displays the name of the user that is logged in. It doesn’t display anything for anonymous users. To control the text being dis‑ played, you use the FormatString property:
At runtime, the {0} is replaced with the user’s name.
592 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
By default the LoginStatus you added displays a link to enable users to log in and log out. Because the Menu or the TreeView already contains a link to the Login page, the footer uses a LoginView again to only display the Logout text when the user is currently logged in. If you want to add a Login link as well, you can extend the LoginView with an anonymous template and an additional LoginStatus or remove the entire LoginView so the LoginStatus is visible to all users.
Besides the controls you have seen that enable a user to log in and that use the current user’s log-in status to show or hide relevant content, the Login category of the Toolbox contains three more con‑ trols that enable users to sign up for a new account on the site, to change an existing password, and to recover a lost password. These controls are discussed next.
CreateUserWizard You briefly saw CreateUserWizard at work in an earlier exercise. But the control has a lot more to offer than the standard behavior you saw in that exercise. To start with, the control has a long list of Text properties, such as CancelButtonText, CompleteSuccessText, UserNameLabelText, and CreateUserButtonText, that affect the text used in the control. All properties have good (English) defaults, but you can change them if they don’t suit your needs. The control has a bunch of properties that end in ImageUrl, such as CreateUserButtonImageUrl. These properties enable you to define images for various user actions instead of the default buttons that the control generates. If you set any of these properties to a valid ImageUrl, you also need to set the corresponding ButtonType. For example, to change the Create User button to an image, you need to set the CreateUserButtonImageUrl to a valid image and set CreateUserButtonType to Image. The default value for the ButtonType is Button, which renders standard buttons by default. You can also set these properties to Link to have them rendered as standard LinkButton controls. Additionally, the control exposes a number of useful properties that you can set to change its behav‑ ior and appearance, shown in the following table. Property
Description
ContinueDestinationPageUrl
Defines the page where users are taken when they click Continue after signing up.
DisableCreatedUser
Whether or not the user is marked as disabled when the account is created. When set to True, users cannot log in to the site until their account has been enabled. You see how to manually activate and deactivate user accounts later. Defaults to False.
LoginCreatedUser
Whether or not the user is logged in automatically after the account has been created. Defaults to True.
Introducing the Login Controls ❘ 593
Property
Description
RequireEmail
Determines whether or not the control asks the user for an e‑mail address. Defaults to True.
MailDefinition
Contains a number of subproperties that enable you to define the (optional) e‑mail that gets sent to users after they sign up.
You may notice that the control doesn’t have any properties to hide the security question and answer, or to change the strong password policy that requires users to type a password of at least seven characters. Because multiple controls need access to these settings, you need to configure them on the underlying provider. In the section “Configuring Your Web Application” later in this chapter, you see how this works. The CreateUserWizard control is able to send a confirmation e‑mail to users to inform them their new account was created successfully. This e‑mail message can also serve as a reminder of their user names and passwords. In the following exercise you see how to configure the MailDefinition ele‑ ment so the CreateUserWizard sends an e‑mail message to new users to confirm their account and send them their user name and password for future reference.
Try It Out
Sending Confirmation E-Mail with CreateUserWizard
For this exercise to work, you need to have configured the <system.net> element of the web.config file with a valid mail server name or local pickup folder. Refer to Chapter 9 if you don’t have these set‑ tings and don’t know how to configure them. 1.
Add a new Text File to the App_Data folder and call it SignUpConfirmation.txt.
. 2
Add the following text to the file and then save and close it: Hi , Thank you for signing up for a new account at www.PlanetWrox.com. To log in to the site, use the following details: User name: Your password:
We look forward to your contributions. The Planet Wrox Team
Take care when typing the UserName and Password placeholders. They are wrapped in a pair of server-side tags (), which are used to give special meaning to these placeholders. . 3
Open SignUp.aspx and make sure it’s in Design View. Then, on the Properties Grid of the CreateUserWizard control, locate the MailDefinition property and expand it. Click the BodyFileName property, click the ellipsis to browse for a file, and then select SignUpConfirmation.txt, which you created in the App_Data folder.
594 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
4.
Set the Subject property to Your New Account at PlanetWrox.com. When you’re done, the Properties Grid should look like Figure 16-11.
Figure 16-11
5.
Save all changes and request SignUp.aspx in your browser. Enter the required details for a new account and click Create User to sign up for an account. If you get an error about specify‑ ing a From address make sure you assigned a valid e‑mail address to the from attribute in the web.config file: <smtp deliveryMethod=”SpecifiedPickupDirectory” from=”[email protected]”>
Refer to Chapter 9 to see how to add this attribute. Make sure you enter a valid e‑mail address or the mail server may still reject it. If your mail server requires you to use SSL (for example, when you are using Gmail’s SMTP server), don’t forgot to set the enableSsl attribute of the element to true in web.config. Alternatively, you could write your own code in the SendingMail event of the control to send your message and override some settings of the MailMessage or the SmtpClient. You can access e.Message of the SendingMail event to get access to the message that is being sent. To set this up, switch SignUp.aspx to Design View, open the Properties Grid for the CreateUserWizard, switch to the Events tab, and set up a handler for the SendingMail event. Inside that handler, write the following code (you need to add an Imports/using statement for the System.Net.Mail namespace in order to bring the SmtpClient class in scope). The following code assumes you configured the web.config file to use Gmail’s mail server and configured a port number such as 587 or 465 in the port attribute of the element. If you keep having trouble getting this to work, check out the article on my web site that deals with this in more detail: http://tinyurl.com/ybx7qkh. VB.NET
Protected Sub CreateUserWizard1_SendingMail(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.MailMessageEventArgs) _ Handles CreateUserWizard1.SendingMail Dim myClient As New SmtpClient() myClient.EnableSsl = True myClient.Send(e.Message) e.Cancel = True End Sub
This is just an example to show you how you can change the way the mail is being sent — for example, by programmatically enabling SSL on the SmtpClient. If all you need to do is send e-mail using Gmail’s secure mail server, you're better off using just the settings in web.config instead, as described in Chapter 9.
6 .
After a while you should receive an e‑mail that contains the welcome text you typed in step 2. Figure 16-12 shows the message with the user name and password placeholders replaced with the details that were entered in step 5.
Figure 16-12
How It Works The CreateUserWizard comes with built-in functionality to send a confirmation message to the user. It doesn’t send the message until you specify the <MailDefinition> element. You use the BodyFileName property to point to a text file or an HTML file that is used as the e‑mail’s body. Within this body you can use the special placeholders and , which are replaced automatically with the actual user name and password that the user entered in the signup form. Although sending mail is normally taken care of automatically, it’s good to know that you can hook into the process and make changes to the message being sent or to the SmtpClient that is being used to send
596 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
the message. In this exercise, you saw how to hook into the SendingMail event and make changes to the SmtpClient, but other changes — to properties of the e.Message parameter, for instance — would have been acceptable as well.
NOTE It would be a nice addition if you could check whether a given user name already existed before the user tried to submit the form. You can use the techniques from previous chapters to easily accomplish this. For example, you could use a page method at the server to check whether a given name was already taken. You could then call this page method with some JavaScript as soon as the user moves away from the User Name field. For a step-by-step guide of this solution take a look at my web site at http://imar.spaanjaars.com/ QuickDocId.aspx?quickdoc=494
The PasswordRecovery control, discussed next, also supports a custom mail body and enables you to manually send the message in a hander for the SendingMail event.
PasswordRecovery The PasswordRecovery control enables users to retrieve their existing passwords (when the system supports it) or to get a new auto-generated password. In both cases, the password is sent to the e‑mail address that the user entered when signing up for an account. Most of the properties of the PasswordRecovery control should be familiar by now. It has a number of Text properties, such as GeneralFailureText (shown when the password could not be recov‑ ered) and SuccessText, that enable you to set the text that is displayed by the control. It also has properties that end with ButtonType, ButtonText, and ButtonImageUrl that enable you to change the look and behavior of the various action buttons of the control. You set the SuccessPageUrl to a page in your site if you want to send the user to another page when password recovery succeeds. As with the CreateUserWizard, the PasswordRecovery also has a MailDefinition element that can point to a file that you want to send as the mail body. You can use the same placeholders for the user name and password to customize the message. If you leave out the MailDefinition, the con‑ trol uses a default mail body, as you see in the next exercise.
ChangePassword The ChangePassword control enables existing and logged-in users to change their passwords. It has a host of properties to change things like text, error messages, and buttons, similar to the CreateUserWizard and PasswordRecovery controls. It also has a MailDefinition element that enables you to send a confirmation of the new password to the user’s e‑mail address.
Introducing the Login Controls ❘ 597
Try It Out
Implementing the Password Functionality
In this Try It Out you add PasswordRecovery and ChangePassword controls to the web site to enable users to change and recover their passwords. Because changing a password only makes sense for logged-in users, you add the ChangePassword control to its own page. In a later exercise in this chapter you pro‑ tect this page so only authenticated users can access it. 1.
Open up Login.aspx in Markup View and locate the closing tag inside the . Right after it, type two elements (use the br code snippet and press Tab to complete the element) to create some room below the Login control.
. 2
Drag a PasswordRecovery control from the Toolbox into the code editor, right after the two elements you added in step 1.
. 3
Between the opening and closing tags of the PasswordRecovery control add a <MailDefinition> element and then set the Subject of the e‑mail to Your New Password for PlanetWrox.Com. Your code should now look like this:
<MailDefinition subject=”Your New Password for PlanetWrox.Com”>
4.
Save your changes and close the file.
. 5
In the root of your site create a new Web Form based on your custom template and call it MyProfile.aspx. Set the Title of the page to My Profile.
. 6
Make sure you’re in Markup View and in the cpMainContent content placeholder create an element (type h1 followed by the Tab key) with its contents set to My Profile. Right below the heading type some text that explains that the My Profile page is used for things like changing pass‑ words. Wrap the text in a pair of
tags to denote a paragraph.
7.
Drag a ChangePassword control from the Toolbox and drop it after the closing
tag. You should end up with something like this: My Profile
The My Profile page allows you to make changes to your personal profile. For now, all you can do is change your password below.
8.
Open the file Web.sitemap from the Solution Explorer and add a new element in the About sec‑ tion. Let the url point to ~/MyProfile.aspx and set the title and description to My Profile. You should end up with this code: <siteMapNode url=”~/About/Default.aspx” title=”About” description=”About this site”> <siteMapNode url=”~/About/Contact.aspx” title=”Contact Us” description=”Contact Us” /> <siteMapNode url=”~/About/AboutUs.aspx” title=”About Us” description=”About Us” />
598 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
Save all changes and close all open files. Right-click Login.aspx in the Solution Explorer and choose View in Browser. Below the Login control you should now see the PasswordRecovery con‑ trol, visible in Figure 16-13. Note that if you were already logged in, you need to click the Logout link first.
1 0.
Enter your user name in the PasswordRecovery control and click the Submit button. You’re asked to enter the answer to the security question that you provided when you signed up for the account. Enter the answer and click Submit once more. You should get an e‑mail message with your new, auto-generated password. If your mail server requires special handling, follow the same steps as with the CreateUserWizard control by manually constructing an SmtpClient instance in the SendingMail event of the PasswordRecovery control.
Figure 16-13
11. Use this new password to log in to the site. When you’re logged in, choose My Profile from the Menu or the TreeView. The ChangePassword control visible in Figure 16-14 appears.
Figure 16-14
12. Enter the auto-generated password that was sent to you by e‑mail, type a new password that is eas‑ ier to remember, and then retype the same password. Finally, click Change Password. From now on, you can log in to the site using your new password.
Introducing the Login Controls ❘ 599
How It Works By default, your passwords are stored in a hashed format in the database, which means they cannot be retrieved. Hashing is an irreversible process that creates a unique fingerprint of your data. Because it’s irreversible, there is no way to re-create the password from the hash, which makes it safer to store in a database. When you log in, the password you enter is also hashed and then the two hashes are compared to see if you are allowed to enter. Because the original password cannot be retrieved, the PasswordRecovery control generates a new password for you. It then sends this password to the e‑mail address that is associated with the user name you entered. As the mail body it uses a standard template that contains the user name and the new password. To customize the mail body, you can point the BodyFileName of the MailDefinition to a text file that contains placeholders for the user name and password, just as you saw how to do with the CreateUserWizard.
You may have noticed that the login controls use a couple of defaults that you haven’t been able to change so far. For example, the CreateUserWizard and PasswordRecovery controls always ask the user for a security question and answer as an additional layer of security. You also need to enter strong passwords with a minimum length of seven characters. You can change these settings for the entire application through the web.config file.
Configuring Your Web Application Earlier in this chapter I discussed how the default connection string called LocalSqlServer is defined in the machine.config file so that it’s effective by default in all web applications that you build. The same is true for many of the other settings that are relevant to the ASP.NET application services, like member‑ ship, roles, and profile. The machine.config contains the following settings for the membership services: <membership> <providers>
600 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
This provider configuration features a number of interesting attributes that are described in the fol‑ lowing table. Attribute
Description
connectionStringName
Points to the name of the connection string for the application. Defaults to the LocalSqlServer connection you saw earlier.
enablePasswordRetrieval
Determines whether users are able to retrieve their current password. This option cannot be set when passwordFormat is Hashed (see passwordFormat).
enablePasswordReset
Determines whether a user is able to request a new password.
requiresQuestionAndAnswer
Determines whether controls like CreateUserWizard and PasswordRecovery have the user enter a security question and answer.
applicationName
Provides the unique name of the application.
requiresUniqueEmail
Determines whether the system allows duplicate e‑mail addresses for user accounts. When set to True, each user must provide a unique user name and a unique e‑mail address.
passwordFormat
Determines the way passwords are stored in the database. It supports the following formats: Clear: Passwords are stored as plain text. Encrypted: Passwords are encrypted in a reversible format that enables the system to retrieve the clear text representation of the password again. Hashed: Passwords are encrypted with an irreversible, one-way algorithm. When the passwordFormat is Hashed, users cannot retrieve their original
passwords anymore. They can only request a new, auto-generated password. maxInvalidPasswordAttempts
Specifies the number of times a user can enter an invalid password or invalid security answer before their account is locked.
minRequiredPasswordLength
Determines the minimum length of the password.
minRequiredNonalphanumericCharacters
Determines the minimum number of nonalphanumeric characters that must be included in the password.
Introducing the Login Controls ❘ 601
Attribute
Description
passwordAttemptWindow
Determines the time frame in minutes during which invalid password attempts are counted.
passwordStrengthRegularExpression
Enables you to specify a custom regular expression to enforce a strong password.
To change these settings for the Planet Wrox application you could modify the machine.config directly. However, I strongly encourage you not to do this. Because this file applies to your entire machine, you may end up with a lot of unwanted side effects, or even render ASP.NET in a broken state. Instead, you should reconfigure the <membership> element in the web.config file, and change the necessary attributes for the current application only. In the following Try It Out you see what it takes to reconfigure the membership provider for the Planet Wrox application.
Try It Out
Configuring Membership
In this short exercise you see how to override the default behavior for the membership in the Planet Wrox site. First you see how to obtain a copy of the membership settings from the central configuration file. You then modify these settings to remove the security question and answer option and to change the rules for the password. 1.
Locate the file machine.config, which by default you’ll find in C:\Windows\Microsoft.NET\ Framework\v4.0.30128\Config. If you installed Windows in a different folder or drive, be sure to change the path accordingly. If you’re using a 64 bit version of Windows, the Framework folder is called Framework64 instead. Don’t worry if your system administrator doesn’t allow you to access this file on your machine. If you can’t access it, you can type the code from step 6 directly in the web .config file. Also, don’t worry if the version number of the framework following v4.0 is slightly different; you can have a more recent version of the .NET than the one I used to write this book. Use Windows Explorer to find out the exact folder name.
. 2
Open the file with Notepad and locate the <membership> element under <system.web> near the end of the file.
. 3 . 4
Copy the entire <membership> element to the clipboard. Go back to VWD, open web.config, and paste the <membership> element right before the element but still within the <system.web> element. Except for some different formatting (you could have the settings all on one line), you should end up with the configuration settings you saw at the beginning of this section.
. 5 6.
Change minRequiredPasswordLength to 6 and set requiresQuestionAndAnswer to false. Right before the element, add a self-closing element. When you’re done, your configuration settings should look like this: <membership> <providers>
602 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
7.
Save all your changes and request SignUp.aspx in the browser. Note that the security question and answer no longer appear in the Sign Up form as they did in Figure 16-4 at the beginning of this chapter.
. 8 . 9
Fill in the form, but for the password fields type something short like pass. Click the Create User button. Note that the control now forces you to enter a password with a minimum length of six characters as you defined in web.config by showing the correct error mes‑ sage below the control, shown in Figure 16-15.
Figure 16-15
If you still see the secret question and answer text boxes, you may need to restart your browser, or even VWD.
1 0.
Enter a password of at least six characters with at least one non-alphanumeric character like # or % and click the Create User button again. This time your password is accepted and the account is created.
The Role Manager ❘ 603
How It Works The configuration settings for ASP.NET work in a hierarchical manner. This means that settings defined at a high level (for example, in machine.config) are applicable to all web applications and web sites on your computer. The Planet Wrox web site inherits the settings defined in machine.config, including those for the membership provider. To change the behavior in a single web site, you need to create a copy of the original settings and then modify the necessary attributes. However, in order to do that, you first need to remove the original settings. You do this with the element: <providers> element is considered empty, allowing you to add the same provider again, but this time with different settings. Although this seems like a lot of work to change just a few settings, you’ll find the ability to override the settings in your own web site to be of much more use when you need your application to talk to a different database or database server. Appendix B shows you how to do this.
So far you have seen how to let users sign up for an account so they can log in. But how can you differentiate between the different users in the system? How can you block access to specific folders such as the Management folder for unauthorized uses? You do this with the role manager, another application service that ships with ASP.NET.
The Role Manager Although it’s nice that your users can now sign up and log in to your site, it would be a lot more use‑ ful if you could differentiate among your users. That would enable you to grant access rights to one or just a few users to access the Management folder so only they can change your reviews and genres. With the Role Manager that ships with ASP.NET this is pretty easy to do. The Role Manager enables you to assign users to different roles. You can then use these roles to open or block specific functionality in your site. For example, you can block access to the Management folders for all users except for those in the Managers role. Additionally, you can display different content based on the roles users are in with the LoginView as you saw earlier.
Configuring the Role Manager As with membership, the settings for the Role Manager are placed in config files. However, the Role Manager is not turned on by default, so you need to do that explicitly in the web.config file in the element that should be placed inside the <system.web> element in web.config:
604 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
In the next exercise you see how to enable the roles with a management tool that is accessible from within VWD so you can enable roles without modifying the web.config by hand. After you have enabled roles, you can assign users to different roles in a number of ways: ➤➤
Using the Web Site Administration Tool, generally referred to as the WSAT
➤➤
Using IIS (the Windows web server) on recent Windows editions (you see more about this in Chapter 19)
➤➤
Programmatically using the Role Manager API (application programming interface)
Managing roles using the Role Manager API is beyond the scope of this book. If you want to learn more about it, get a copy of Wrox’s Professional ASP.NET 4 in C# and VB (ISBN: 978-0-47050220-4) or take a look at the Bug Base chapter in my book ASP.NET 2.0 Instant Results (ISBN: 978-0-471-74951-6). Although that book targets ASP.NET 2.0, you’ll see that many of the concepts still apply to ASP.NET 4. The Web Site Administration Tool (WSAT) is used for a lot more than managing roles alone, and is discussed in detail in the next section. It is only available from your local machine and is available as a menu shortcut in VWD. As such, it’s great for setting up the initial users and roles during devel‑ opment, but it isn’t suitable for managing users in a production environment.
Managing Users with the WSAT The WSAT tool ships with VWD and is available from the Website menu. The tool is used for the following tasks: ➤➤
Managing users
➤➤
Managing roles
➤➤
Managing access rules — for example, to determine what user can access which files and folders
➤➤
Configuring application, mail, and debug settings
➤➤
Taking the site offline so users can’t request any pages and get a friendly error message instead
Some of the changes you make with the WSAT are persisted in the web.config file for the applica‑ tion. Other settings, like users and roles, are stored in the database for the configured provider. In the next exercise you see how to start and use the WSAT. You see how to create a new role and a new user account, and how to assign that user to the role.
Try It Out
Using the WSAT to Manage User Accounts and Roles
To protect your Management folder from users that are not authorized to access it, you need to create a role that is allowed to access this folder. After you have set up this role, you can grant all users in that role access to the folder while keeping all other users out. In this Try It Out you learn how to create the
The Role Manager ❘ 605
Managers role and assign a user to it. In a later exercise you see how to limit access to the Management folder to Managers only.
1 .
From within VWD, choose Website ➪ ASP.NET Configuration. Your browser opens and displays the Web Site Administration Tool, shown in Figure 16-16.
Figure 16-16
2.
In the top-right corner you see a Help link that takes you to a help file describing how you can use the tool. Right below the logo of the application you see four tabs: Home, Security, Application, and Provider. The Home tab takes you back to the start page you see in Figure 16-16. The Application tab is used to configure different application settings and the Provider tab enables you to reconfigure the chosen provider for the application. In this exercise, all that’s important is the Security tab, so go ahead and click it. You should see the screen displayed in Figure 16-17. The bottom part of the screen is divided into three parts: Users, Roles, and Access Rules. You see how to use the Users and Roles in this exercise. The Access Rules is used to block or open up specific parts of your web site to users or roles. You won’t see how to use it in this chapter, but instead you’ll learn how to change some of these settings in web.config directly in a later exercise.
. 3
Make sure that under Users you see the Create User and Manage Users links. If you don’t see them, but you see a note about Windows authentication instead, click the Select Authentication Type link, then select From the Internet, and finally click Done. Your screen should now look like Figure 16-17.
. 4
In the Roles section, click the Enable Roles link. The page reloads and now offers a link with the text Create or Manage Roles. Click that link to open the Create New Role page visible in Figure 16-18.
606 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
Figure 16-17
Figure 16-18
5.
Enter Managers as the new role name and click the Add Role button. You should see the new role appear. Click the Back button at the bottom-right of the page to return to the main Security page.
6.
Click the Create User link under the Users section. You’re taken to a page that enables you to enter the details for a new user and assign that user to the Managers role at the same time. Type Manager as the user name. As a password, enter something that meets the password rules you
The Role Manager ❘ 607
configured earlier. A password like Manager##123 will do. Enter your e‑mail address and don’t forget to check the Managers role name in the list of roles on the right. . 7
Click Create User to add the user to the system and then click Continue on the confirmation page. At the bottom of the page click the Back button so you reach the main Security page.
. 8
On the Security page click the Manage Users link. You are taken to a page that shows a list of all available users in the system, shown in Figure 16-19.
Figure 16-19
From here you can edit, enable, disable, or delete existing users. For example, if you previously set DisableCreatedUser to True in the CreateUserWizard, you can enable the user here by checking the check box in front of the user name, visible in Figure 16-19. You can change the roles that are assigned to the user by clicking the Edit Roles link. Also, using the filter controls and the alphabet above the user list, you can quickly search for a specific user in case you’re dealing with a lot of user accounts. . 9
To see where your user and role ended up, close the browser and go back to VWD. On the Solution Explorer, double-click the ASPNETDB.MDF database in the App_Data folder to open it in the Database Explorer window. Then expand the Tables node, right-click the aspnet_Roles table, and choose Show Table Data. The role you created in step 5 should be listed. Open up some of the other tables such as aspnet_Membership and aspnet_UsersInRoles and inspect the data they contain. In the first table you should see the user account you created in steps 6 and 7, and the lat‑ ter table contains a relationship between the new role and user account.
608 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
How It Works Just like the login controls, the WSAT eventually talks to the provider you configured in the web.config file. In the case of the Planet Wrox application it means it talks to the AspNetSqlMembershipProvider, which in turn talks to a SQL Server Express database identified by the LocalSqlServer connection string. The users and roles you create are stored in the various aspnet_* tables in the database. Users that you create using the WSAT end up in the exact same location as those created with the CreateUserWizard control. In fact, the WSAT uses a CreateUserWizard to create new user accounts. This means that any user you enter in the WSAT is able to log in using your standard Login.aspx page. In a later exercise, you use the Manager account you created in this exercise to log in to the site and access the Management folder. In order to use the role you created in this exercise you have a few options at your disposal. First, you can use the role name to block access to specific folders in your web application through settings in the web.config file. Secondly, you can use the role in controls like the LoginView to present different con‑ tent to different users. Finally, you can use the Role API to check whether the current user is in a spe‑ cific role. This gives you fine control over the content or functionality you can offer to certain privileged users.
You see how to block access to the Management folder and modify the LoginView in the next sec‑ tion; using the Role API is discussed in a later exercise.
Configuring the Web Application to Work with Roles On the Security page of the WSAT you saw a section called Access Rules. This part of the tool enables you to block or open up resources in your site. It enables you to define rules such as “This folder is blocked for anyone except for users in the Managers role,” or “Anyone can access this file, except for the users in the Members role and the Joe account.” The tool is quite intuitive to use, so it isn’t difficult to set up the different rules. However, it has one downside: it stores the security set‑ tings in separate web.config files, one for each subfolder you configure. This makes it somewhat difficult to get an overview of all the different security settings. Fortunately, ASP.NET also enables you to configure the same settings in the main web.config using elements. A element has a path attribute that points to a file or folder you want to con‑ figure differently. You can use the element for many (but not all) other settings from web.config as well (for example, you could set the theme attribute of the <pages> element for the Management folder in the main web.config). For the following exercise, you’ll only set the child ele‑ ments of to those related to security.
Try It Out
Blocking Access to the Management Folder
Obviously, you don’t want just anyone to mess with the reviews and genres that you have posted on your web site. Therefore, it’s important to block access to the Management folder to anyone except site managers assigned to the Managers role. In this exercise you see how to modify web.config to block the folder so only the user account you assigned to the Managers role earlier can access this folder and the files it contains.
The Role Manager ❘ 609
1.
Open the web.config file at the root of the site. Scroll all the way down to the closing tag and right before it type a element. Add a path attribute to the element and set its value to Management. Note that IntelliSense kicks in to help you complete the element and find the attribute. Complete the configuration by entering the following settings: <system.web> <deny users=”*” />
2.
Save and close the web.config file.
3.
Open the main master page for the site (Frontend.master) in Design View and scroll down to the end of the file. Select the LoginView control and open its Smart Tasks panel. At the top of the panel, click the Edit RoleGroups link, shown in Figure 16-20.
Figure 16-20
4.
In the dialog box that opens, click the Add button to insert a new RoleGroup and then set the Roles property of this group to Managers, as shown in Figure 16-21.
Figure 16-21
610 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
5.
Click OK to insert the RoleGroup and return to Design View.
6.
Still on the Smart Tasks panel of the LoginView, choose RoleGroup[0] - Managers from the Views drop-down list. This switches the current template of the control to the RoleGroup for Managers, so you can add content that is only visible to Managers.
. 7
From the Standard category of the Toolbox drag a HyperLink control and drop it into the LoginView. Using the Properties Grid, set the Text property of this HyperLink to Manage Site and set the NavigateUrl to ~/Management/Default.aspx. (You can use the URL picker for the HyperLink by clicking the small button with the ellipsis on it.) Switch to Markup View and after the closing tag of the HyperLink control type the word or followed by a LoginStatus control that you can drag from the Toolbox, or copy from the existing code in the LoggedInTemplate. When you are ready your LoginView should contain the following code: ... Manage Site or ...
8.
Save all your changes and then request the home page (Default.aspx) for the site in your browser. Verify that you are currently not logged in (check the footer of the page and if necessary click the Logout link).
9.
Click the Login link on the Menu or TreeView and then log in with the Manager account you created earlier in this chapter. Make sure you don’t check the Remember Me option. The page refreshes and now shows the Manage Site link in the footer of each page (see Figure 16-22).
Figure 16-22
If you don’t see the Manage Site and Logout links in the footer region, close all browser windows, go back to the WSAT (using Website ➪ ASP.NET Configuration in VWD), and ensure the account you’re using is assigned to the Managers role. Additionally, you may need to stop the built-in develop‑ ment web server by using the Windows tray bar, and then open the Login page again. 10. Click the Manage Site link to open the Management section of the web site. Copy the current URL of the page from the browser’s address bar to the clipboard (it should be something like http:// localhost:49666/Management/Default.aspx). Click the Back button of your browser to go back to the home page and then click the Logout button in the footer. Close all open browser win‑ dows and open a new instance of your browser again. (You can do this from the Windows Start menu or from your desktop if you have a shortcut there, or you can right-click a page in VWD and choose View in Browser.)
The Role Manager ❘ 611
11. Paste the address you just copied in the address bar of the browser window and press Enter. Instead of going to an address like: http://localhost:49666/Management/Default.aspx
you are taken to the Login page: http://localhost:49666/login.aspx?ReturnUrl=%2fManagement%2fDefault.aspx
Note that the page you initially requested (Management/Default.aspx) is now appended to the query string. The forward slashes (/) in the address have been encoded to their URL-safe counterpart — %2f — automatically. Log in with your Manager account and you should see the Management section appear again.
How It Works There are a couple of interesting things you need to look at in this exercise. First, look at the settings you added to the web.config file to limit access to the Management folder: <system.web> <deny users=”*” />
When the ASP.NET runtime processes the request for a page, it checks the various configuration files to see whether the current user is allowed to access that resource. For requests to files in the Management folder, it encounters the rule set in the element. It starts scanning the various rules (allow and deny elements with roles or users attributes to specify the users or roles that are affected by the rule) and as soon as it finds a rule, it stops the scanning process and applies that rule. If no rule is satis‑ fied, access is granted! Therefore, it’s important to end the rule with a deny rule to block all other users that haven’t been granted access previously. When an unauthenticated user logs in, the first rule won’t match because the anonymous user is not a member of the Managers role. The user is then denied access because of the deny rule that blocks all users, indicated by the asterisk (*). After you logged in as a Manager and requested the same resource, the rule set was scanned again. The runtime then found the allow element that grants access to the Managers role and immediately let you in. The final rule that blocks access to all other users was not even checked. In addition to specific roles or user names and the asterisk (*) to refer to all users, you can also use the question mark (?) to refer to unauthenticated or anonymous users. So, for example, to let any logged-in user access the Reviews folder regardless of the role they are in, and block access to all other users, you can add the following element to your configuration file: <system.web> <deny users=”?” />
612 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
This denies access to all users that are not logged in. Because of the default rule that grants access to the resource if the current user is not matched by an earlier rule, all logged-in users can successfully access files in the Reviews folder. You can specify multiple roles or user names in the roles and user attributes by separating them with a comma. It’s important to understand how the RoleGroups element of the LoginView works. Although you can specify multiple RoleGroup elements that may all apply to a certain user, only the first that matches is displayed. Consider a user called Lucas assigned to the role WebMasters and to the role Managers and a web page with the following LoginView:
With this code, the user Lucas only sees the content of the first RoleGroup, even though he is also assigned to the WebMasters role.
Programmatically Checking Roles Although it’s easy to use the LoginView control to change the content a user is allowed to see at run‑ time, this isn’t always enough. There are times where you need programmatic control over the data you are presenting based on someone’s role membership. You can access information about roles for the current user in a number of ways. First of all, you can access the IsInRole method of the User property from the current page or user control like this: VB.NET
If User.IsInRole(“Managers”) Then ‘ This code runs for Managers only End If C#
if (User.IsInRole(“Managers”)) { // This code runs for Managers only }
The Role Manager ❘ 613
Alternatively, you can access the Roles class that contains a number of static methods that you can access directly. The following code is functionally equivalent to the previous example: VB.NET
If Roles.IsUserInRole(“Managers”) Then ‘ This code runs for Managers only End If C#
if (Roles.IsUserInRole(“Managers”)) { // This code runs for Managers only }
The biggest difference between the two is that for the latter option to work, you need to have the Role Manager enabled. Because that’s the case in the Planet Wrox web site, you can choose any of these two methods. For custom solutions, where you are not using a role provider but your own cus‑ tom solution, you can only use the first code construct. Besides the IsUserInRole method, the Roles class contains a lot of other methods that enable you to work with roles programmatically. For example, you can create and delete roles, assign users to and remove users from roles, and you can get a list of users that are assigned to a certain role. For more information about the Roles API, check out the MSDN documentation at http://tinyurl.com/ RolesAPI or pick up a copy of Professional ASP.NET 3.5 Security, Membership, and Role Management with C# and VB by Bilal Haidar (Wrox, ISBN: 978-0-470-37930-1). Although the book targets ASP.NET 3.5, you’ll find that most topics discussed in that book still apply to ASP.NET 4. In the following exercise you learn how to modify the photo albums page so users logged in as Managers are able to delete pictures from a photo album. Other users won’t be able to delete a pic‑ ture because the Delete button will be hidden for them.
Try It Out
Checking Roles with IsUserInRole at Runtime
This Try It Out uses a programmatic check for the user’s role to hide or show the Delete but‑ ton. Although you could recreate this example by using a LoginView with different templates and RoleGroups, this exercise uses custom code to make it easier to modify the code later on so that owners of a specific photo album can delete their own pictures as well. For now, the Edit Photo Album link you add is visible to all users, but you see how to fix that in the next chapter.
1 .
Open Default.aspx from the PhotoAlbums folder in Markup View and right below the closing tag of the ListView control enter two HTML breaks (you can use the br snippet) followed by a HyperLink control. Set its ID property to EditLink and its Text property to Edit Photo Album. You assign the NavigateUrl in the next step.
614 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
2.
Switch to Design View, select the ListView control, open up its Properties Grid and switch to the Events tab. Double-click DataBound to set up a handler for that event. Inside the handler that VWD created, add the following code: VB.NET
Protected Sub ListView1_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListView1.DataBound If Not String.IsNullOrEmpty(DropDownList1.SelectedValue) Then EditLink.NavigateUrl = String.Format( “~/ManagePhotoAlbum.aspx?PhotoAlbumId={0}”, DropDownList1.SelectedValue) EditLink.Visible = True Else EditLink.Visible = False End If End Sub C#
Open up ManagePhotoAlbum.aspx in the root of the site and switch it to Design View. Select the ListView, and open its Properties Grid. Switch to the Events tab and double-click the ItemCreated event to set up an event handler for that event.
. 4
At the top of the Code Behind file add an Imports/using statement for the System.Web.Security namespace: VB.NET
Imports System.Web.Security C#
using System.Web.Security;
5.
Add the following code to the event handler that VWD created: VB.NET
Protected Sub ListView1_ItemCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewItemEventArgs) _ Handles ListView1.ItemCreated Select Case e.Item.ItemType
The Role Manager ❘ 615
Case ListViewItemType.DataItem Dim deleteButton As Button = CType(e.Item.FindControl(“DeleteButton”), Button) deleteButton.Visible = Roles.IsUserInRole(“Managers”) End Select End Sub C#
Save all your changes and then request Default.aspx from the PhotoAlbums folder in your browser by pressing Ctrl+F5. If you’re not logged in, click the Login link in the main Menu or TreeView, log in with the Manager account you created earlier in this chapter, and return to the Gig Pics page.
. 7
Choose one of the photo albums from the drop-down list. The page reloads and shows the pictures in the photo album.
. 8
Click the Edit Photo Album link at the bottom of the page. Figure 16-23 shows how each picture is now associated with a Delete button that deletes the picture when clicked. Click the Delete button to confirm that it works.
Figure 16-23
616 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
9.
Click the Logout link in the footer again and go to the Login page. Log in with an account you created earlier in this chapter that is not a member of the Managers role. Go back to the Gig Pics page, choose a photo album from the list and click the Edit Photo Album link. This time, you don’t get a Delete button, because the account you’re logged in with is not assigned to the Managers role.
How It Works Most of the code in this exercise shouldn’t be new to you. You have seen how you can delete items with the EntityDataSource and ListView controls. You also learned how to handle the ItemCreated and other events and search for controls in an item using FindControl. What’s new in this example is the way you check whether the current user is a member of the Managers role: VB.NET
Dim deleteButton As Button = CType(e.Item.FindControl(“DeleteButton”), Button) deleteButton.Visible = Roles.IsUserInRole(“Managers”) C#
The IsUserInRole method returns a Boolean that indicates whether the current user is a manager. When the method returns True, it means that the Visible property of the Button is set to True. When the method returns False, the button is hidden and the user is not able to delete pictures from the photo album.
Practical Security Tips The following list provides some practical security tips: ➤➤
Although the concept of security is introduced quite late in the chapter, you shouldn’t see it as an afterthought. To ensure you create a solid and secure application you should keep security in mind from the very early stages of your web site development. Deciding whether you want to have areas that are only accessible to certain users, and whether you are going to force users into getting an account for your site before they get access is best done as early as possible. The later in the process you introduce these concepts, the more difficulties you’ll face when integrating this functionality.
➤➤
Try to group resources like ASPX pages under folders that represent roles in your system. Take, for example, the Management folder in the Planet Wrox web site. All pages related to the management of your site are packed together in a single folder, making it very easy to block the entire folder with a single element in the web.config file. When the files you want to protect are scattered throughout your web site, you’ll need more time to configure the application, and you’ll end up with a cluttered view of the active security settings.
Summary ❘ 617
➤➤
When you create roles to differentiate between users on your web site, try to limit the num‑ ber of different roles your system has. You’ll find that your system becomes much easier to manage with only a handful of logically grouped roles than with a large number of roles with only one or two users in them.
Summary Security in your ASP.NET site can be implemented with several techniques, including Windows authentication (where the web server takes care of authentication) or third-party authentication (where an external service like Microsoft Passport takes care of validating the user). Last, you can use Forms authentication, which is the de facto standard for many of today’s ASP.NET web sites. In general, authentication encompasses three important concepts: identity, authentication, and authorization. Together, they determine who you are and what you are allowed to do. The ASP.NET Membership provider enables you to create and manage users in a central database using handy controls such as the CreateUserWizard, PasswordRecovery, and Login. Users and roles are managed with the WSAT, so you can assign user accounts to different roles as you saw in this chapter. You can then open specific resources in your web site to members in a cer‑ tain role using simple elements in the web.config file. The various login controls enable you to customize the content that users get to see. In the next chapter you discover how to take this one level further, by creating dynamic pages that adapt based on the user that is accessing them.
Exercises
1.
What’s the difference between authentication and authorization?
2.
Right now the Management folder is blocked for all users except those in the Managers role. What change do you need to make to the web.config file if you also want to open up the folder for the user John and all people in the Editors role?
3.
Imagine you have a web site that features a Login page with a single Login control. What change to the Login control do you need to make to send users to MyProfile.aspx in the root after they log in?
4.
What’s the difference between the LoginView and LoginStatus controls? When would you use each one? Answers to Exercises can be found in Appendix A.
618 ❘ Chapter 16 Security in Your ASP.NET 4 Web Site
⊲⊲ What You Learned in This Chapter Application services
A set of ASP.NET services that you can access from your web application to handle tasks such as membership, role and profile management
Authentication
The process of proving your identity to a system
Authorization
The process of determining the permissions a user has in a system
Login controls
The set of security controls that ship with ASP.NET and enable you to sign up, log in, recover your password and more
Membership provider
One of the ASP.NET application services that handles membership related tasks (including creating of users, logging in and more)
Permissions
Determine the operations a user in the system is allowed to carry out
Provider model
A model where an interchangeable piece of software is used for certain application tasks. Through configuration you can assign a different piece of software that handles the same tasks (but in a different way)
Role manager
One of the ASP.NET application services that handles role related tasks including creating roles, assigning users to roles and checking their role membership
17
Personalizing Web Sites What You Will Learn in This Chapter: ➤➤
Details about the Profile feature that ships with ASP.NET
➤➤
How to create and consume a user’s profile in a web site
➤➤
How you can recognize your users and how to serve them customized content
➤➤
How you can access the profile of other users in the site
There is only one thing that beats good content on the Web — good personalized content. In the era of information overload and the huge amount of competitive sites, it’s important to know your visitors and understand the possibilities you have to present them personalized content. With a good personalization strategy you can create a web site that lives up to your users’ expectations by presenting them with exactly the data they are looking for. Personalization is useful for many different scenarios. For example, on a sports site you use personalized con‑ tent to highlight activities from the user’s favorite team. On a site that deals with program‑ ming, you can personalize content by showing users examples in their preferred programming language(s) only. On a news web site, you can let users choose one or more news categories (World, Local, Sports, Business, Financial, and so on) and target the content you show them based on these preferences. You can take this one step further by sending them e‑mail updates when a new article is posted in one of those categories. However, personalization goes further than just storing personal preferences and adapting the content to these preferences. With personalization you can also keep track of other user details, such as a name, date of birth, visits to the site, items users bought in an online shop, and so on. You can then use these details to further personalize web pages, creating a closer relationship with your visitors. In the Planet Wrox web site, personalization is implemented simply yet effectively. The main Reviews page is designed to only show the reviews for those music genres that the user is
620 ❘ Chapter 17 Personalizing Web Sites
interested in. To see all the available reviews, users can still visit the All.aspx page but by visiting the personalized page, they only see reviews in genres they really like. Additionally, users can enter personal details about themselves, such as a first and last name, and a short biography. These details will be shown on the Photo Albums details page so you know who uploaded a particular photo album. To enable you to add personalization features to a web site, ASP.NET 4 ships with an application service called Profile. With the Profile service you can store data for a particular user with very few lines of code. By the end of this chapter, you’ll have enough knowledge about the personalization features brought by Profile to create dynamic and personalized web sites.
Understanding Profile The ASP.NET Profile is another application service that ships with ASP.NET. It enables you to store and retrieve information about users to your site that goes beyond basic information such as an e‑mail address and password that users can enter during sign-up. With Profile, you can store infor‑ mation such as a first and last name, a date of birth, and much more, as you see later in this chap‑ ter. By keeping track of the user to which that data belongs, ASP.NET is able to map that data to a user the next time she visits your site, whether that be minutes or weeks later. The cool thing about Profile is that it enables you to store data for registered users as well as anonymous users. So, even if your visitors haven’t signed up for an account, you can recognize them and store information about them. You access the information in a user’s profile through a clean API with virtually no code. All you need to do is define the information you want to keep track of in the central web.config file and the Profile feature takes care of the rest. All interaction with the database to retrieve or store profile information in the database is handled automatically for you. Enabling Profile in your web application is a simple, two-step process:
1.
Define the information you want to store for a user in the web.config file. Based on this information, the ASP.NET runtime generates and compiles a class for you on the fly that gives you access to the properties you defined. It then dynamically adds a property called Profile to the pages in your web site, so you can easily access it from every page in your site.
2.
In your application you program directly to this generated class to get and store the profile information for the current user.
The ASP.NET Profile by default is connected to a logged-in user, although you can also save profile data for unauthenticated users as you see later in this chapter. Because data about the logged-in user is stored in a cookie by default, your users need to have browsers that support cookies for the ASP.NET Profile feature to work correctly.
Understanding Profile ❘ 621
In the following section you see how to define profile properties in web.config, and how to access them in your web pages.
NOTE It’s important to realize that the built-in Profile feature works only with Web Site Projects and not with Web Application Projects. For a discussion on the difference between the two, refer to Chapter 2. If you find that none of the examples in this chapter seem to work, check that you haven’t accidentally created a Web Application Project. The simplest way to check is look at the Code Behind file of a Web Form. If you see two Code Behind files (one named after the page with a .cs or .vb extension and one with an additional Designer extension) you have created a Web Application Project. In that case, get yourself a copy of the Chapter 16 folder that is part of the source that comes with this book and use that as the starting point for this chapter.
Configuring the Profile You define a profile for your web site in the web.config file by creating a <profile> element as a direct child of the <system.web> element. Between the <profile> tags you need to create a <properties> element that is used to define the properties you want to expose from your Profile object. Two types of properties exist: simple properties and complex properties, referred to as profile groups.
Creating Simple Profile Properties You define simple properties as direct children of the <properties> element using an ele‑ ment. The following example demonstrates how to create a property that can be used to hold a user’s first name and one to hold a date of birth. The FirstName property can be accessed and set for authenticated and anonymous users, whereas the DateOfBirth property is accessible only to logged-in users: <system.web> ... <profile> <properties>
Because properties are by default of type System.String, there’s no need to define an explicit type on the property for simple properties like a first name. However, for other types like a DateTime, a Boolean, an Integer, or your own types, you need to define the type explicitly using the type attri‑ bute and its fully qualified name including its namespace as shown for the DateOfBirth property. The following table lists the most common attributes of the element that influence the proper‑ ties of a profile.
622 ❘ Chapter 17 Personalizing Web Sites
Attribute
Description
name
Defines the name of the property, such as FirstName, DateOfBirth, and so forth.
type
Sets the full .NET type name of the property, such as System.String, System.Boolean, System.DateTime, and so on.
allowAnonymous
Specifies whether the property can be written to for anonymous users. The default is False. When you set this attribute to True, you also need to enable anonymousIdentification, discussed later in this chapter.
defaultValue
Defines the default value for the property if it hasn’t been set explicitly. When you leave out this attribute, the profile property has as its default value the default value for the underlying type (for example null for a String, 0 for an Int32, and so on).
readOnly
Specifies whether the profile property can be changed at runtime. The default is False, which means you can read from and write to the property.
Besides simple properties, you can also create profile groups that enable you to group other simple properties together.
Creating Profile Groups Profile groups serve two distinct purposes: first, they enable you to logically group related proper‑ ties. For example, you can create a group called Address that in turn has properties like Street, PostalCode, and City. Groups also enable you to have properties with the same name, but located in a different group. For example, you can have two groups called VisitAddress and PostalAddress that both feature properties like Street and PostalCode, making it easier for a developer using your Profile object to find the relevant information. To create a profile group you add a element to the <properties> element of your profile and then specify a name. The element then contains one or more simple properties. The following example shows a profile group for a PostalAddress: <properties>
You can have multiple groups within the <properties> tags, but you can have only one level of groups. This means that you can’t nest a element in another or element.
Understanding Profile ❘ 623
Using Non-standard Data Types In addition to the data types listed earlier such as String, DateTime, and Integer, you can also use your own types you have defined (in the App_Code folder, for example). As with the built-in .NET types, you need to refer to your type using its fully qualified name that includes the namespace and the class name. Imagine you have a type called Preference that con‑ tains various properties (implemented as automatic properties in this example) related to the user’s preference. To include this type in the profile you need to wrap it in a namespace first: VB.NET
Namespace PlanetWrox Public Class Preference Public Property FavoriteColor As String ‘ Other properties go here End Class End Namespace C#
namespace PlanetWrox { public class Preference { public string FavoriteColor { get; set; } // Other properties go here } }
You then refer to the type in an element as follows:
A situation where you need a different syntax to refer to a type in the profile setup is when you are using generics. Chapter 5 discusses how to use generics to store role names using a List of strings: VB.NET
Dim roles As New List(Of String) ... roles.Add(“Members”) C#
List<string> roles = new List<string>(); ... roles.Add(“Members”);
To give your profile a property that is of a generic List type, you need to use some special syntax. The following setting in web.config creates a profile property called FavoriteGenres that stores the user’s favorite genres as a List (Of Integer) in VB.NET and as a List in C#:
624 ❘ Chapter 17 Personalizing Web Sites
The first part of the type attribute looks quite normal. The List class lives in the System. Collections.Generic namespace so it makes sense that you need to specify that here as well. However, right after the class name (List) you see `1. This is not a typo, but the .NET way to refer to generic types in plain text. To define a property that is based on a generic type you need to use the back tick (`) followed by a 1. The back tick is usually found to the left of the 1 key on your key‑ board. The `1 is then followed by a pair of square brackets that contains the actual type you want to use for the list. The type specified in this profile property is identical to its VB.NET and C# coun‑ terparts that are defined as follows: VB.NET
Dim FavoriteGenres As New List(Of Integer) C#
List FavoriteGenres = new List();
You see how to make use of this and other profile properties in the following exercises. First, you learn how to configure Profile in web.config in the next Try It Out. Later exercises show you how to work with these properties, and how to use the various methods of the List class.
Try It Out
Creating a Profile
In this Try It Out you see how to create a profile that is capable of storing a user’s first and last name, a date of birth, a short biography, and a list of IDs of the user’s favorite genres. This list is later used to show only the reviews that match the user’s interest. 1.
Open the web.config file from the root of the site and locate the opening <system.web> tag.
. 2 3.
Add a new <profile> element as a direct child of <system.web>. Complete the <profile> element so it ends up looking like this: <system.web> <profile> <properties>
4.
Save the web.config file by pressing Ctrl+S. As soon as you save the file, a background process starts to generate a class file that is used for the profile. After the class file has been created and compiled successfully, you can access it programmatically through the Profile property of the Page class.
Understanding Profile ❘ 625
5.
To test the profile, open the file MyProfile.aspx that you created in the previous chapter in Design View. Double-click the page to set up an event handler for the Load event and add the following code containing your own first and last name: VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Profile.FirstName = “Your first name here“ Profile.LastName = “Your last name here“ End Sub C#
protected void Page_Load(object sender, EventArgs e) { Profile.FirstName = “Your first name here“; Profile.LastName = “Your last name here“; }
As soon as you type the dot (.) after Profile, an IntelliSense list appears, showing you the available profile properties (see Figure 17-1). . 6
Figure 17-1
When you are finished typing the code, save and close the file because you’re done with it for now.
COMMON MISTAKES If nothing shows up when pressing the dot, choose Build ➪ Build Web Site from the main menu or press Ctrl+Shift+B. This forces VWD to start a recompilation of the application, including the special class for the Profile property. You see more about compilation in Chapter 19. After a delay of a few seconds, the properties should now appear in the IntelliSense list for the Profile property of the Page class. If they still don’t show up, check the Error List (choose View ➪ Error List from the main menu to open up the Error List) to verify that you didn’t make any mistakes in the web.config file and make sure you are using a Web Site Project and not a Web Application Project.
7.
Switch back to the web.config file and scroll all the way to the end. Create a copy of the element that blocks access to the Management folder for unauthorized users and paste it right below the existing element. Then modify the copy so it blocks access to the file MyProfile.aspx in the root of the site to all unauthenticated users. You should end up with these settings: <system.web> <deny users=”?”/>
626 ❘ Chapter 17 Personalizing Web Sites
8.
In the Solution Explorer, right-click the file MyProfile.aspx and choose View in Browser. You can only view this file when you’re logged in; if you weren’t logged in previously, you are taken to Login.aspx first. Login with the user name and password you created in the previous chapter and click Login. You’re taken back to MyProfile.aspx. Although you don’t see anything new in the page, the code in Page_Load has run and has created a profile for you in the database.
9.
To see this profile, close your browser and go back to Visual Web Developer. Open the Database Explorer (the Server Explorer in paid versions of VS) and expand the Tables element of the ASPNETDB.MDF database. Locate the table aspnet_Profile, right-click it, and choose Show Table Data. You should see something similar to Figure 17-2.
Figure 17-2
This figure shows the profile data for a single user. The first and last name you entered in step 5 are stored in the column PropertyValuesString. Because of the special format this data is stored in, you shouldn’t modify this data manually. Instead, you should use Profile to change the under‑ lying data.
How It Works When you define profile properties in web.config, the ASP.NET runtime creates a class for you in the background. This class, called ProfileCommon, gives you access to the strongly typed properties such as FirstName, LastName, and FavoriteGenres. The ProfileCommon class is then made acces‑ sible to the Page through its Profile property. ProfileCommon inherits from ProfileBase, the base class defined in the .NET Framework that contains the behavior to access the profile in the database by talking to the configured provider, the ASP.NET Profile provider. The provider in turn takes care of all the hard work of persisting the data in the configured database. Just as the Membership and Roles providers you saw in previous chapters, the Profile provider by default uses the database defined in the LocalSqlServer connection string. Appendix B teaches you how to change this behavior and have the provider use a different database. To define properties you use elements with a name attribute and an optional type if the property is of another type than System.String. For example:
This property sets up a list that can store Integer values to hold the user’s favorite music genres. You see how to use this property in a later exercise.
Understanding Profile ❘ 627
After you have set up the profile in web.config and the background class has been compiled, you can access the profile in code. For example, you can now set properties such as FirstName through code: VB.NET
Profile.FirstName = “Your first name here“ C#
Profile.FirstName = “Your first name here“;
Although not used in this exercise, you access properties in a group in pretty much the same way. All you need to do is prefix the property name with the group name and a dot. Given the example of a PostalAddress, you would store the street for that address like this: VB.NET
Profile.PostalAddress.Street = “Some Street” C#
Profile.PostalAddress.Street = “Some Street”;
Changes made to the profile are saved automatically for you during EndRequest, an event that fires very late during the ASP.NET page life cycle. This way, you can change the profile during many of the stages of the life cycle without having to worry about explicitly saving the profile manually. In Figure 17-2 you can see how a single record is used to store the entire profile. The first column con‑ tains the unique ID of the user to which the profile belongs. The second column contains a list of prop‑ erty names that are saved for the current user, together with a starting index of the value and a length. For example, for the last name you see: LastName:S:4:10
This states that the value for the LastName property, which is stored in the PropertyValuesString col‑ umn, starts at position 4 (the fifth character because zero-based positions are used) and has a length of 10 characters. The letter between the LastName and the start index defines the column the data can be found in: S means the PropertyValuesString column, whereas B indicates the PropertyValuesBinary column, which is used to store complex objects in a binary format. This dense format enables the Profile provider to store many different properties in a single column, which eliminates the need to mess with the database schema any time the profile changes.
You learn more about reading from and writing to the profile in the following section.
Using the Profile As you saw in the previous section, writing to the profile is easy. To change a property like FirstName, all you need is a single line of code. The profile keeps track of the changes you have made to it, and, if necessary, automatically saves the changes during EndRequest. Reading from the
628 ❘ Chapter 17 Personalizing Web Sites
profile is just as easy; all you need to do is access one of its properties. The following snippet shows how to fill a TextBox with the first name from the profile: VB.NET
FirstName.Text = Profile.FirstName C#
FirstName.Text = Profile.FirstName;
Retrieving properties in a group is almost identical. To access the Street property discussed in a previous example, you need this code: VB.NET
Accessing the FavoriteGenres property is slightly different. Because this property is a collection, you shouldn’t access it directly. Instead you use its methods and properties to get data in and out. The following example clears the entire list first, and then adds the IDs of two genres to it: VB.NET
The following exercise shows you how to store basic data in the user’s profile. You see a real-world implementation of using the FavoriteGenres list in a later exercise.
Try It Out
Storing Basic User Data in the Profile
In this Try It Out you modify the Profile page so users can save their first and last name, birthday, and a short biography in their profile. 1.
Open MyProfile.aspx again and switch to Code Behind. Remove the two lines of code in Page_Load that set the first and last name.
2.
Switch to Design View and, right above the ChangePassword control, add an HTML table of five rows and three columns by choosing Table ➪ Insert Table.
3.
In the second column of each of the first four rows, drag TextBox controls and rename them, from the first to the last row, FirstName, LastName, DateOfBirth, and Bio by setting their ID attribute. Figure 17-3 shows you where the TextBox controls should be placed exactly.
Understanding Profile ❘ 629
4.
5.
In the first column of each of the first four rows, drop Label controls and set their properties as follows so each label is associated with a TextBox in the same row. ID
Text
AssociatedControlID
FirstNameLabel
First name
FirstName
LastNameLabel
Last name
LastName
DateOfBirthLabel
Date of birth
DateOfBirth
BioLabel
Biography
Bio
In the second cell of the fifth row, drag a Button, and set its ID to SaveButton and its Text to Save Profile. Design View should look like Figure 17-3.
Figure 17-3
6.
7.
In the last column of each of the first three rows, drag RequiredFieldValidator controls. Set their properties as follows, so each validator lines up with a TextBox in the same row. Remember: you can set the Display property for all controls at once by selecting the controls while pressing the Ctrl key. ControlToValidate
Display
ErrorMessage
FirstName
Dynamic
First name is required.
LastName
Dynamic
Last name is required.
DateOfBirth
Dynamic
Date of birth is required.
Next to the validator for the DateOfBirth box, drag a CompareValidator and set its properties as follows: Property
Value
Display
Dynamic
ErrorMessage
Please enter a valid date.
ControlToValidate
DateOfBirth
Operator
DataTypeCheck
Type
Date
630 ❘ Chapter 17 Personalizing Web Sites
8.
Set the TextMode of the Bio control to MultiLine and set its Width and Height properties to 300px and 75px, respectively.
9.
Modify the text above the table to indicate that users can now do more than just change their pass‑ word alone. Your Design View should look like Figure 17-4.
Figure 17-4
10. Double-click the Save Profile button and in the Click event handler that VWD added for you, write the following bolded code: VB.NET
Protected Sub SaveButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SaveButton.Click If Page.IsValid Then Profile.FirstName = FirstName.Text Profile.LastName = LastName.Text Profile.DateOfBirth = DateTime.Parse(DateOfBirth.Text) Profile.Bio = Bio.Text End If End Sub C#
11. In the Page_Load event handler of the same page, add the following code, which fills in the text box controls with the data from the profile when the page loads: VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load If Not Page.IsPostBack Then FirstName.Text = Profile.FirstName LastName.Text = Profile.LastName DateOfBirth.Text = Profile.DateOfBirth.ToShortDateString() Bio.Text = Profile.Bio End If End Sub C#
12. Save all changes and request the page in the browser. If you’re required to log in first, enter your details and click the Login button. You should see the My Profile page reappear with the data you entered for the first and last names in the previous Try It Out already filled in. Change that data into your details and click the Save Profile button. 13. Close your browser and request MyProfile.aspx again. Note that your changes have been per‑ sisted between the two browser sessions.
How It Works Much of what you have seen in this exercise should be familiar by now. The page contains a num‑ ber of TextBox controls that are validated using RequiredFieldValidator and CompareValidator controls. Additionally, the Label controls are hooked up to their respective TextBox controls using the AssociatedControlID property. This makes it easy to put focus on the controls in the browser because clicking a Label now puts the cursor in the associated TextBox. With the tricks you learned in Chapter 15 you make the controls even more accessible by setting the AccessKey properties. When you click the Save Profile button, the values are retrieved from the four TextBox controls and stored in the profile. When the page loads the first time, the reverse of this process takes place: the controls are pre-filled with the values from the profile. To avoid overwriting the data that the user has
632 ❘ Chapter 17 Personalizing Web Sites
entered, the code only gets the data from the profile when the page initially loads, and not during a postback: VB.NET
If Not Page.IsPostBack Then FirstName.Text = Profile.FirstName .... End If C#
if (!Page.IsPostBack) { FirstName.Text = Profile.FirstName; ... }
Although the example itself is pretty trivial, it lays out a nice foundation for a more advanced sce‑ nario using the List of integers to store the user’s preference for certain music genres. You can then use this list of favorite genres to limit the list with reviews to those the user is really interested in. You see how to store the user’s preference in Profile in the following exercise; a later exercise shows you how to use the saved data again.
Try It Out
Storing Genre Preferences in the Profile
In this Try It Out you learn how to fill the FavoriteGenres property of the user profile. In order to let the user choose their favorite genres, you’ll display a CheckBoxList that is hooked up to an EntityDataSource that retrieves the available genres. When the user saves the data, the items that the user checked are then stored in the profile.
1 .
In MyProfile.aspx, add a table row above the one with the Save Profile button. To do this, make sure you’re in Design View, right-click an empty spot in the row with the button, and choose Insert ➪ Row Above from the context menu that appears. Alternatively, click the cell to put the cursor in it and press Ctrl+Alt+up arrow.
2.
In the first cell of the new row, drag a Label and set its Text to Favorite genres.
. 3
In the second cell drag a CheckBoxList control from the Standard category of the Toolbox and set its ID to PreferenceList.
4.
Hook up the CheckBoxList control to a new EntityDataSource by clicking Choose Data Source on its Smart Tasks panel. Choose from the data source drop-down list, then choose the Entity data source type and click OK. In the Configure Data Source wizard for the new data source control, choose PlanetWroxEntities as the Named Connection, click Next, and choose Genres as the EntitySetName. In the Select list, check only the items for the Id and Name properties. Your Configure Data Source dialog box now looks like Figure 17-5.
Understanding Profile ❘ 633
Figure 17-5
5.
Click Finish to close the dialog box. Back in the Data Source Configuration Wizard for the CheckBoxList control, choose Name as the data field to display and Id as the data field for the value. If these items don’t appear in the drop-down lists, click the blue Refresh Schema link at the bottom of the dialog first. Your screen now looks like Figure 17-6.
Figure 17-6
634 ❘ Chapter 17 Personalizing Web Sites
Click OK to close the dialog. . 6
Back in Design View, click the EntityDataSource that was just added and press F4 to open its Properties Grid. Locate the OrderBy property and enter it.Name to sort the list of genres alpha‑ betically. Switch to Markup View and confirm that the code for the EntityDataSource control and the CheckBoxList looks as follows:
7.
In Design View, click the CheckBoxList control once, open its Properties Grid, and switch to the Events tab. Double-click the DataBound event and add the following code in the Code Behind to preselect the items in the list based on the user’s profile settings: VB.NET
Protected Sub PreferenceList_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles PreferenceList.DataBound For Each myItem As ListItem In PreferenceList.Items Dim currentValue As Integer = Convert.ToInt32(myItem.Value) If Profile.FavoriteGenres.Contains(currentValue) Then myItem.Selected = True End If Next End Sub C#
protected void PreferenceList_DataBound(object sender, EventArgs e) { foreach (ListItem myItem in PreferenceList.Items) { int currentValue = Convert.ToInt32(myItem.Value); if (Profile.FavoriteGenres.Contains(currentValue)) { myItem.Selected = true; } } }
8.
Extend the SaveButton_Click handler with the following code so it also saves the user’s preferred genres: VB.NET
Profile.Bio = Bio.Text ‘ Clear the existing list Profile.FavoriteGenres.Clear()
Understanding Profile ❘ 635
‘ Now add the selected genres For Each myItem As ListItem In PreferenceList.Items If myItem.Selected Then Profile.FavoriteGenres.Add(Convert.ToInt32(myItem.Value)) End If Next C#
Profile.Bio = Bio.Text; // Clear the existing list Profile.FavoriteGenres.Clear(); // Now add the selected genres foreach (ListItem myItem in PreferenceList.Items) { if (myItem.Selected) { Profile.FavoriteGenres.Add(Convert.ToInt32(myItem.Value)); } }
9.
Save all your changes, request the Profile page in your browser, and log in when required. You should see the list of genres displayed in the browser, each one preceded by a check box. Select a couple of your favorite genres and click the Save Profile button. Browse to another page and choose My Profile again from the main Menu or TreeView. The genres you selected should still be selected in the page, as shown in Figure 17-7.
Figure 17-7
How It Works Earlier you defined the FavoriteGenres property in the profile as a generic list that can hold integer values. Because this property is a List, you do not assign values to it directly; instead, you use its methods like Add and Clear to add and remove items. Because each genre ID should be stored in the
636 ❘ Chapter 17 Personalizing Web Sites
list only once, the list is cleared before the selected items are added again when the user clicks the Save Profile button to remove any selection made earlier: VB.NET
Profile.FavoriteGenres.Clear() C#
Profile.FavoriteGenres.Clear();
Then when the list is empty, the IDs of the selected genres are added: VB.NET
For Each myItem As ListItem In PreferenceList.Items If myItem.Selected Then Profile.FavoriteGenres.Add(Convert.ToInt32(myItem.Value)) End If Next C#
foreach (ListItem myItem in PreferenceList.Items) { if (myItem.Selected) { Profile.FavoriteGenres.Add(Convert.ToInt32(myItem.Value)); } }
This code loops through all the items in the CheckBoxList. The Selected property determines whether the user has selected the item in the Profile page. If it has been selected, the value of the genre is retrieved, converted to an Integer (an int in C#), and then added to the FavoriteGenres list using the Add method.
That’s really all you need to store complex data like a list of favorite genres in the user’s profile. All you need to do is add a bunch of numbers to a list. The .NET runtime then takes care of persisting the Profile in the database and making it available again in subsequent pages. Of course, the list with favorite genres isn’t really useful until you actually make use of it in the site. In the next exercise you see how to use the list to limit the list of Reviews that users initially see when they visit the default Reviews page.
Try It Out
Using Profile in the Reviews Page
Currently your site has two pages in the Reviews folder that are capable of displaying reviews: AllByGenre.aspx and All.aspx. In this Try It Out, you modify the page Default.aspx so it displays yet another list of reviews. However, this time the list with reviews is limited to those belonging to the genres that the user has selected in the My Profile page. When anonymous users see the page, they get a message that they haven’t set their favorite genres yet.
1 .
From the Reviews folder open Default.aspx in Markup View.
Understanding Profile ❘ 637
2.
Inside the control for the cpMainContent placeholder, add the following code that creates a nested Repeater with each selected genre as a heading, followed by a list of reviews belonging to that genre:
Below you find a list with reviews for your favorite music genres.
Sorry, no reviews were found. You either didn’t set your favorite genres or you may need to log in first.
You can change your genre preferences here.
You can create the Repeater controls manually by writing the necessary code, or you can drag and drop them from the Data category of the Toolbox. The inner Repeater contains a HyperLink control that points to the ViewDetails.aspx page that you created in Chapter 15. 3.
Double-click the page in Design View to set up a Load handler. Add an Imports/using statement at the top of the page for the PlanetWroxModel namespace and add the following code to the han‑ dler that VWD created for you: VB.NET
Imports PlanetWroxModel ... Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Using myEntities As PlanetWroxEntities = New PlanetWroxEntities() If Profile.FavoriteGenres.Count > 0 Then Dim favGenres = From genre In myEntities.Genres Order By genre.Name Where Profile.FavoriteGenres.Contains(genre.Id) Select New With {genre.Name, genre.Reviews} GenreRepeater.DataSource = favGenres GenreRepeater.DataBind()
638 ❘ Chapter 17 Personalizing Web Sites
End If GenreRepeater.Visible = GenreRepeater.Items.Count > 0 NoRecords.Visible = GenreRepeater.Items.Count = 0 End Using End Sub C#
using PlanetWroxModel; ... protected void Page_Load(object sender, EventArgs e) { using (PlanetWroxEntities myEntities = new PlanetWroxEntities()) { if (Profile.FavoriteGenres.Count > 0) { var favGenres = from genre in myEntities.Genres orderby genre.Name where Profile.FavoriteGenres.Contains(genre.Id) select new { genre.Name, genre.Reviews }; GenreRepeater.DataSource = favGenres; GenreRepeater.DataBind(); } GenreRepeater.Visible = GenreRepeater.Items.Count > 0; NoRecords.Visible = GenreRepeater.Items.Count == 0; } }
4.
Save all your changes and request the page in the browser. If you selected one or more genres in the Profile page previously, and reviews are available for those genres, you should see a list similar to Figure 17-8.
Figure 17-8
Understanding Profile ❘ 639
If you haven’t set any preferred genres, or you’re not logged in, you get the message shown in Figure 17-9.
Figure 17-9
By clicking the link in the message you are taken to the My Profile page so you can set or change your preferred genres. Unauthorized users are asked to log in or sign up for an account before they can access the Profile page.
How It Works The code in the Code Behind executes a LINQ to EF query that retrieves all the reviews that belong to the user’s favorite genres. For anonymous users, the list of favorite genres will be empty so they always get to see the message about setting their preferences in the Profile page. To avoid an unnecessary call to the database, the query is only executed when the user has selected at least one preferred genre by checking the Count property of the FavoriteGenres list. The nested Repeater you added to the Default.aspx page in the Reviews folder looks similar to the code for the AllByGenre.aspx page that has a Repeater that contains a BulletedList control. Just as in that page, the nested Repeater gets its data from the outer Repeater with the DataSource attribute: ...
The nested Repeater then uses the list of Reviews to build up the hyperlinks that take you to the details page:
The HyperLink control gets its Text from the Review instance that it’s bound to and uses its Id to build up the NavigateUrl. The ToString method is used on Eval(“Id”) to convert the value to a string before it’s concatenated to the string that contains the URL. This is done to avoid type
640 ❘ Chapter 17 Personalizing Web Sites
conversions in Visual Basic where Eval(“Id”) normally results in a number that you can’t concatenate to a string directly. To see how these controls get their data, you need to look at the Code Behind that uses a LINQ query targeting the Entity Framework: VB.NET
Dim favGenres = From genre In myEntities.Genres Order By genre.Name Where Profile.FavoriteGenres.Contains(genre.Id) Select New With {genre.Name, genre.Reviews} C#
var favGenres = from genre in myEntities.Genres orderby genre.Name where Profile.FavoriteGenres.Contains(genre.Id) select new { genre.Name, genre.Reviews };
Except for the highlighted line of code and the variable name, this LINQ query is identical to the one used in AllByGenre.aspx. What makes this example special is the where clause that limits the number of reviews to those that the user is really interested in. Note how the Contains method of the generic List class is used here. Although at first it may seem that all genres and reviews are retrieved from the database into the ASPX page and then compared with the values in the profile property called FavoriteGenres, the reverse is actually the case. The Entity Framework is smart enough to collect all the IDs from the FavoriteGenres property first and then include them in the SQL statement that is sent to the database to fetch the requested genres and reviews. This means that filtering of the requested genres takes place at the database level, and not in the ASPX page. This in turn means that fewer records are transferred from the database to the ASPX page (only those that are really needed), which results in a better performance. The profile property FavoriteGenres returns an empty list, rather than throwing an exception for anonymous users. So, even users with no profile can safely view this page. Instead of seeing any reviews, they get a message stating they haven’t set their genre preferences yet, or that they need to log in first. In the end of the Page_Load handler, some code determines whether to show or hide the Repeater and the NoRecords control: VB.NET
If after data binding the outer Repeater its Items collection is still empty, it means no genres were found for the current user. If that’s the case, the entire Repeater is hidden and the PlaceHolder is shown. However, if the Count property of the Items collection is larger than zero, the Repeater is made visible and the PlaceHolder is hidden.
Understanding Profile ❘ 641
In Chapter 14 you created a page called NewPhotoAlbum.aspx that lets users insert new Gig Pics albums. The current implementation of this page has a few shortcomings. First of all, anyone can insert a new album. There’s no way to block anonymous users from creating a new album and uploading pictures. Secondly, only Managers can remove pictures from an existing photo album. It would be nice of the owner of an album could also remove her own pictures. Now that you know more about security and personalizing web pages, this is pretty easy to implement, as you see in the following exercise.
Try It Out
Letting Users Manage Their Own Photo Albums
In this Try It Out you see how to block the NewPhotoAlbum.aspx and ManagePhotoAlbum.aspx pages from unauthenticated users. Additionally, you see how to record the name of the user who created the photo album and use that name later on to enable users to alter their own photo albums.
1 .
Open the Database Explorer, expand the PlanetWrox database, and locate the PhotoAlbum table. Right-click it and choose Open Table Definition. Add a new column called UserName, set its data type to nvarchar(256), and leave the Allow Nulls option selected. (There are already photo albums in this table without a valid UserName, so you can’t make the column required at this stage unless you delete these photo albums and their related pictures from the database first.) Save the changes and close the table designer.
2.
Open the ADO.NET Entity Data Model file PlanetWrox .edmx from the App_Code folder, right-click an empty space in the designer, and choose Update Model from Database. Wait until VWD has analyzed your database and click Finish. The UserName column in the database now shows up as a property of the PhotoAlbum class (see Figure 17-10). Save your changes and close the file.
. 3
Figure 17-10
Open the web.config file and below the existing elements add the following two elements to block access to the two referenced files for anonymous users: <system.web> <deny users=”?” /> <system.web> <deny users=”?” />
642 ❘ Chapter 17 Personalizing Web Sites
Save your changes and close the web.config file. 4.
Open NewPhotoAlbum.aspx in Design View, locate the EntityDataSource control, and set up an event handler for its Inserting event using the Events tab of the Properties Grid. Add the follow‑ ing code to the handler that VWD created for you: VB.NET
Protected Sub EntityDataSource1_Inserting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.EntityDataSourceChangingEventArgs) _ Handles EntityDataSource1.Inserting Dim myPhotoAlbum As PhotoAlbum = CType(e.Entity, PhotoAlbum) myPhotoAlbum.UserName = User.Identity.Name End Sub C#
From the PhotoAlbums folder open Default.aspx and switch to its Code Behind.
. 6
At the top of the file add an Imports/using statement for the PlanetWroxModel namespace and then extend the DataBound event handler with the following code that shows the Edit link when the current user is either a Manager or the owner of the photo album: VB.NET
Protected Sub ListView1_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListView1.DataBound If Not String.IsNullOrEmpty(DropDownList1.SelectedValue) Then Dim photoAlbumId As Integer = Convert.ToInt32(DropDownList1.SelectedValue) Using myEntities As PlanetWroxEntities = New PlanetWroxEntities() Dim photoAlbumOwner As String = (From p In myEntities.PhotoAlbums Where p.Id = photoAlbumId Select p.UserName).Single() If User.Identity.IsAuthenticated And (User.Identity.Name = photoAlbumOwner _ Or User.IsInRole(“Managers”)) Then EditLink.NavigateUrl = String.Format( “~/ManagePhotoAlbum.aspx?PhotoAlbumId={0}”, DropDownList1.SelectedValue) EditLink.Visible = True Else EditLink.Visible = False End If End Using Else EditLink.Visible = False End If End Sub
Understanding Profile ❘ 643
C#
protected void ListView1_DataBound(object sender, EventArgs e) { if (!string.IsNullOrEmpty(DropDownList1.SelectedValue)) { int photoAlbumId = Convert.ToInt32(DropDownList1.SelectedValue); using (PlanetWroxEntities myEntities = new PlanetWroxEntities()) { string photoAlbumOwner = (from p in myEntities.PhotoAlbums where p.Id == photoAlbumId select p.UserName).Single(); if (User.Identity.IsAuthenticated && (User.Identity.Name == photoAlbumOwner || User.IsInRole(“Managers”))) { EditLink.NavigateUrl = string.Format( “~/ManagePhotoAlbum.aspx?PhotoAlbumId={0}”, DropDownList1.SelectedValue); EditLink.Visible = true; } else { EditLink.Visible = false; } } } else { EditLink.Visible = false; } }
7.
Open the Code Behind of ManagePhotoAlbum.aspx in the root. Add the following code to a Page_Load handler. If the handler isn’t there yet, double-click the page in Design View to have VWD set one up for you. VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Dim photoAlbumId As Integer = Convert.ToInt32(Request.QueryString.Get(“PhotoAlbumId”)) Using myEntities As PlanetWroxEntities = New PlanetWroxEntities () Dim photoAlbumOwner As String = (From p In myEntities.PhotoAlbums Where p.Id = photoAlbumId Select p.UserName).Single() If User.Identity.Name photoAlbumOwner And Not User.IsInRole(“Managers”) Then Response.Redirect(“~/”) End If End Using End Sub
644 ❘ Chapter 17 Personalizing Web Sites
C#
protected void Page_Load(object sender, EventArgs e) { int photoAlbumId = Convert.ToInt32(Request.QueryString.Get(“PhotoAlbumId”)); using (PlanetWroxEntities myEntities = new PlanetWroxEntities ()) { string photoAlbumOwner = (from p in myEntities.PhotoAlbums where p.Id == photoAlbumId select p.UserName).Single(); if (User.Identity.Name != photoAlbumOwner && !User.IsInRole(“Managers”)) { Response.Redirect(“~/”); } } }
8.
Because the entire page is now blocked for users without the proper permissions, there’s no longer the need to hide the individual buttons in the ListView control. This means you can remove the code for the ListView1_ItemCreated event handler. If you’re using C#, don’t forget to remove the handler definition from the ListView’s control in Markup View as well.
9.
Save the changes to all open files (press Ctrl+Shift+S) and request NewPhotoAlbum.aspx in your browser. If necessary, log in with an account you created earlier.
10. Enter a new name for the photo album and click Insert. At this stage, the photo album is saved, together with your user name. Proceed by adding a few images to your photo album.
1 1.
Click Gig Pics from the main Menu or TreeView and choose the new photo album you just created from the drop-down list. After the page has reloaded, your new photo album should be displayed, together with the Edit Photo Album link at the bottom of the screen. Clicking the link takes you to ManagePhotoAlbum.aspx, which lets you add or remove pictures in your photo album.
12. Click Logout in the footer of the page. Then go to the Gig Pics page again and choose your new photo album from the drop-down list. Note that the Edit Photo Album link is now no longer visible.
How It Works You started this exercise by adding a column for the user’s name to the PhotoAlbum table. With this column, you can keep track of the user who created the photo album, giving you the opportunity to display data related to the user together with a photo album. When you run the Update Wizard by choosing Update Model from Database, changes in the database (such as adding a column to a table) are reflected in the model. In the New Photo Album page, you used this new property by assigning it the name of the current user with this code: VB.NET
myPhotoAlbum.UserName = User.Identity.Name
Other Ways of Dealing with Profile ❘ 645
C#
myPhotoAlbum.UserName = User.Identity.Name;
The Page class has a User property that represents the user associated with the current request. This user in turn has an Identity that contains the user’s Name. The Name is then assigned to the UserName property of the PhotoAlbum instance, which is retrieved from e.Entity. At this stage, the name is successfully stored in the database, together with the rest of the photo album. What’s left is doing something useful with this name. The first place where you use this name is in the default page of the PhotoAlbums folder. There you used the following LINQ to EF query to retrieve the UserName for a photo album: VB.NET
Dim photoAlbumOwner As String = (From p In myEntities.PhotoAlbums Where p.Id = photoAlbumId Select p.UserName).Single() C#
string photoAlbumOwner = (from p in myEntities.PhotoAlbums where p.Id == photoAlbumId select p.UserName).Single();
This code uses the Single method to retrieve the UserName for a single photo album; the one specified in photoAlbumId. The remainder of the code then determines the visibility of the Edit link if the cur‑ rent user is logged in and an owner of the photo album or a member of the Managers group. This way, both owners and all Managers can change existing photo albums. The code in ManagePhotoAlbum.aspx performs a similar check to stop unauthorized users from accessing the page directly.
Other Ways of Dealing with Profile In the final section of this chapter you see two other useful ways of dealing with the Profile feature in ASP.NET. First, you see how to use Profile for anonymous users followed by a discussion on accessing the profile of a user other than the current user.
Anonymous Identification The Profile feature is extremely easy to configure, yet very powerful. All you need to do to give logged-in users access to their profiles is create a few elements in web.config and the ASP.NET runtime takes care of the rest. But what about anonymous users? What if you wanted to store data for your visitors who haven’t signed up for an account or aren’t logged in yet? For those users you need to enable anonymous identification. With anonymous identification, ASP.NET creates an
646 ❘ Chapter 17 Personalizing Web Sites
anonymous user in the aspnet_Users table for every new visitor to your site. This user then gets a cookie that is linked to the anonymous user account in the database. On every visit, the browser sends the cookie with the request, enabling ASP.NET to associate a user, and thus a profile, with the user for the current request. To enable an anonymous profile, you need to do two things: turn on anonymous identification, and modify some or all profile properties to expose them to anonymous users. Enabling anonymous identification is done with the following element in web.config, directly under <system.web>:
The enabled attribute turns on the feature, and the cookieName attribute is used to give the appli‑ cation a unique cookie name used to store the user’s ID at the client. After you have turned on anonymous identification, the next step involves modifying properties under the <profile> element and setting their allowAnonymous attribute to true:
This profile property can now be accessed through code for anonymous users as well. If you try to set a profile property without the allowAnonymous attribute set to true for a user that is not logged on, you’ll get an error. It’s up to you to only write to these properties from pages that are accessible only to logged-in users. Reading from a property works just fine although you’ll get empty values or the defaults you specified in web.config. Once you have enabled profile properties for anonymous users, reading from and writing to them is identical to how you deal with normal profile properties. In the “Exercises” section at the end of this chapter, you find code to modify the current theme selector so it uses Profile for anonymous and logged-in users.
Cleaning Up Old Anonymous Profiles You may wonder what is happening with an anonymous user’s profile when the associated user signs up for an account and becomes a registered user. The answer is: nothing. The old profile is discarded and the user gets a new profile that is associated with the registered account. Fortunately, this is easy to fix. Whenever a user changes from an anonymous to an authenticated user (that is, when they log in), ASP.NET fires the Profile_OnMigrateAnonymous event that you can handle. You handle this event in a special file called Global.asax (of which you’ll see more in the next chapter), which is used for code that handles application- or session-wide events. Inside an event handler for this event you can access two profiles for the same user: the old, anonymous profile that is about to get detached from the user and the new profile that is associated with the user who is currently logging in. You can then delete the old user account and its related profile data because you can deal with the new profile from now on. Although not used in the Planet Wrox web site, this event handler is a
Other Ways of Dealing with Profile ❘ 647
perfect place to copy anonymous profile data from the old profile to the new one, as demonstrated by the following code: VB.NET
Public Sub Profile_OnMigrateAnonymous(ByVal sender As Object, ByVal args As ProfileMigrateEventArgs) Dim anonymousProfile As ProfileCommon = Profile.GetProfile(args.AnonymousID) ‘ Copy over anonymous properties only Profile.AnonymousProperty = anonymousProfile.AnonymousProperty ProfileManager.DeleteProfile(args.AnonymousID) AnonymousIdentificationModule.ClearAnonymousIdentifier() Membership.DeleteUser(args.AnonymousID, True) End Sub C#
public void Profile_OnMigrateAnonymous(object sender, ProfileMigrateEventArgs args) { ProfileCommon anonymousProfile = Profile.GetProfile(args.AnonymousID); // Copy over anonymous properties only Profile.AnonymousProperty = anonymousProfile.AnonymousProperty; ProfileManager.DeleteProfile(args.AnonymousID); AnonymousIdentificationModule.ClearAnonymousIdentifier(); Membership.DeleteUser(args.AnonymousID, true); }
Note that this code uses Profile.GetProfile(args.AnonymousID) to get an instance of the previ‑ ous, anonymous profile of the user. This gets a reference to the profile of the user before they logged in. The code then continues to copy over the existing, anonymous profile properties from the old to the new profile. In this example, only one property — called AnonymousProperty — is copied. However, you can modify the code to copy more properties. Note that there is no point in copying over properties that are not accessible by anonymous users. Those types of properties cannot have been set previously, so there’s nothing to copy. The final three lines of code then delete the old profile, clear the anonymous user ID from the cookie and, finally, delete the old, anonymous user account from the database. When this code has finished, the old profile is migrated successfully to the new profile, and all the old profile stuff has been suc‑ cessfully deleted from the database and the user’s cookies. The ProfileManager class — which lives in the System.Web.Profile namespace that you need to import for the previous example to work — provides you with more useful methods to work with Profile. For example, you can use DeleteInactiveProfiles to delete profiles for users who have been inactive for a certain amount of time. For detailed information about the ProfileManager class look at this MSDN web page: http://tinyurl.com/ManageProfile.
648 ❘ Chapter 17 Personalizing Web Sites
Looking at Other Users’ Profiles The examples you have seen so far use Profile to access data for the current user. However, what if you need to display data for a different user? For example, what if you wanted to display a user’s biography below a Gig Pics album? You won’t be able to use the Profile property of the Page class in this case directly because it provides information about the current user, not about the user who created the photo album. To solve this problem, the ProfileCommon class, the base class of the Profile property of the Page class, comes with a GetProfile method. The GetProfile method retrieves an existing profile from the database if the name passed to it exists, or it creates a brand new profile if it doesn’t exist yet. For example, to get the Manager’s profile you can use this code: VB.NET
Dim theProfile As ProfileCommon = Profile.GetProfile(“Manager”) C#
With the Profile instance created, you can access its properties as you are used to. The following code assigns the Bio property of the Manager to the Text property of a Label control: VB.NET
BioLabel.Text = theProfile.Bio C#
BioLabel.Text = theProfile.Bio;
Being able to read someone else’s profile is extremely useful. You can use it to show some of the properties of the profile to other users, as you see in the last exercise of this chapter. However, you can also use similar code to update other users’ profiles. For example, you could create a page in the Management section that enables you to manage the profiles of the users that registered at your site. When you do modify other users’ profiles, be sure to call the Save method when you’re done. As you learned earlier, changes to the profile are normally persisted in the database automatically. However, this applies only to the profile of the current user. To change and persist the previously retrieved Manager’s profile, you can use this code: VB.NET
theProfile.Bio = “New Bio for the Manager account here“ theProfile.Save() C#
theProfile.Bio = “New Bio for the Manager account here“; theProfile.Save();
In the following exercise you put some of this into practice when you show the name of the user who created a specific photo album, together with the biography of the user.
Other Ways of Dealing with Profile ❘ 649
Try It Out
Working with Other Users’ Profiles
The Default.aspx page in the PhotoAlbums folder displays the pictures in a specific photo album. You can’t see which user created the photo album, so that would be a nice new feature. And to further improve the page, you can also display the user’s biography on the page. In this Try It Out you see how to implement both features. 1.
From the PhotoAlbums folder open the page Default.aspx in Markup View. Scroll down and locate the two breaks and the HyperLink you added earlier. Just before the breaks and the HyperLink con‑ trol, drag a PlaceHolder control from the Toolbox and set its ID to PhotoAlbumDetails. Inside this PlaceHolder drag two Label controls and then manually modify the code so it ends up like this: Photo Album Details Created by: About this user:
2.
Switch to the Code Behind of the page (press F7) and locate the DataBound event handler for the ListView control. Right after the first Else (else in C#) block that hides the HyperLink control when the user doesn’t have the necessary permissions, add these lines of code that retrieve the pro‑ file for the user who created the photo album and then update the relevant labels: VB.NET
EditLink.Visible = False End If If Not String.IsNullOrEmpty(photoAlbumOwner) Then Dim ownerProfile As ProfileCommon = Profile.GetProfile(photoAlbumOwner) UserNameLabel.Text = photoAlbumOwner BioLabel.Text = ownerProfile.Bio PhotoAlbumDetails.Visible = True Else PhotoAlbumDetails.Visible = False End If C#
Save all your changes and open the page in your browser.
. 4
From the drop-down list, choose a photo album you created and you should see the photo album details appear. If you don’t see them, make sure you selected a recent photo album from the list. Because you added the UserName column to the database at a later stage, some of the photo albums don’t have a user associated with them. If the Photo Album Details section remains hidden, create a new photo album and add one or more pictures to it. This ensures you have at least one photo album with the UserName property. If you now select the photo album from the list, you should see the Photo Album Details, as displayed in Figure 17-11.
Figure 17-11
How It Works Much of the code in this exercise has been discussed before. After adding a few Label controls in the Photo Album page, you retrieved the profile for the owner of the album with this code: VB.NET
Dim ownerProfile As ProfileCommon = Profile.GetProfile(photoAlbumOwner) C#
This code gets a reference to an existing profile using GetProfile. The class that is returned is of type ProfileCommon; the underlying data type of the Profile property. When you have the reference, work‑ ing with it is almost identical to working with normal profiles. The only difference is that you must call Save to persist any changes made to the profile in the database as you saw earlier.
Summary ❘ 651
Practical Personalization Tips The following list provides some personalization tips: ➤➤
Don’t try to access the profile in the Login page, because it isn’t available yet. The profile is instantiated early in the page’s life cycle, so when a Login control authenticates you in a Login page, it’s too late to associate the user’s profile with the current request. Use the GetProfile method of ProfileCommon instead or redirect to another page.
➤➤
Carefully consider what to store in Profile and what is better stored in your own database tables. Although the single-record structure that ASP.NET uses to store your profile offers you a quick and convenient solution, it’s not the most efficient solution, especially not with large amounts of data. Don’t try to store complete reviews or even photo albums in Profile, but use your own database tables instead.
➤➤
The current implementation of Profile makes it difficult to query data from the aspnet_ Profile database in your own queries. For example, it’s difficult to answer queries like “Give me all users that prefer the Rock genre” because all the data is stored in a single col‑ umn. To work around these issues, you can use a different Profile provider that you can download from the Sandbox section of the official ASP.NET web site at www.asp.net/ downloads/sandbox/.
Summary In this chapter you learned how to use the Profile feature that ships with ASP.NET 4 to store userrelated data. You can use Profile to keep track of data for authenticated but also for anonymous users. Setting up a profile is a pretty straightforward operation. You need to create a <profile> element in the web.config file with a <properties> child element and then add one or more properties using elements. To group related properties you use the element. When you have set up the profile, you access its properties through the Profile property of the Page class. This always accesses the profile for the current user. Any changes you make to this profile are persisted for you automatically at the end of the ASP.NET life cycle. By design, profile properties are accessible only to logged-in users. However, you can easily change this by turning on anonymous identification. To access the profile of a user other than the one associated with the current request, you can use the GetProfile method. Any changes made to this profile are not persisted automatically, so you must call Save to send the changes to the database. Now that your pages contain more and more code, chances are that bugs and problems will creep into your application. In the next chapter you learn how to use exception handling to avoid those problems from ending up in the user interface. You also learn how to debug your code, so you can fix problems before they occur.
652 ❘ Chapter 17 Personalizing Web Sites
Exercises
1.
The favorite theme feature you created earlier would be a great candidate for a profile property. What code would you need to add to the profile in web.config to make this possible?
2.
To set the favorite theme in the BasePage, you need to access the profile in a special way. Instead of accessing the Profile property on the Page class, you access it through the HttpContext like this: VB.NET
Dim myProfile As ProfileCommon = CType(HttpContext.Current.Profile, ProfileCommon) C#
Given this code, how can you rewrite Page_PreInit so it gets the preferred theme from the profile instead of from a cookie?
3.
What else do you need to change to finalize storing the theme in the profile instead of a custom cookie? Answers to Exercises can be found in Appendix A.
Summary ❘ 653
⊲⊲ What You Learned in This Chapter Anonymous identification
The ASP.NET feature that enables you to track users to your site, even if they haven’t signed up for an account or are not logged in
ASP.NET Profile
The ASP.NET application service that enables you to store and retrieve information about users to your site
ASP.NET Profile provider
The ASP.NET provider responsible for storing and retrieving profile related data
EndRequest
An event fired by the application in which the changes to the profile are persisted in the database
OnMigrateAnonymous
An event fired by the ASP.NET Profile feature that you can handle in Global.asax to copy anonymous properties into the new profile
Personalization
The process of targeting users with customized content based on their preferences or other information
Profile groups
The mechanism that enables you to group related profile properties
18
Exception Handling, Debugging, and Tracing What You Will Learn in This Chapter: ➤➤
How to write code that is able to detect and handle errors that occur at runtime, at the same time shielding your users from the error details
➤➤
How to detect errors that occur on your production machine so you can take countermeasures
➤➤
What debugging is and what debugging tools VWD offers
➤➤
What tools you can use to gain information about your system and code while it’s running in a development or production environment
You can’t make an omelet without breaking eggs and you cannot write code without creating bugs. No matter how hard you try and how good you are, your code will contain problems that affect the behavior of your web site. Of course you should strive to minimize the impact of these bugs, aiming for a “zero bug count.” To aid you in this, the ASP.NET runtime and Visual Web Developer provide you with a number of tools. First of all, the languages supported by .NET implement exception handling, a methodology to identify and handle errors that occur at runtime. By handling these errors, you can present your users a friendly error message. At the same time you can log these errors, giving you a chance to fix them before they re-occur. In this chapter you see how exception handling works, and how to log your errors. Before your code goes into production you need to write and debug it first. To help you debug your code, VWD comes with a rich toolset that includes ways to step through your code line
656 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
by line, look at your variables and objects at runtime, and even change them as you go. The toolset also provides you with valuable information about the execution path your code follows: the path that your application takes through your code, following methods, event handlers, If and Else statements, and so on.
Exception Handling Whenever you write code, there is a chance things won’t turn out as expected, resulting in code that does not compile, a crash, or otherwise unexpected behavior of your application. Things can go wrong for a large number of reasons: you introduce a typo in your code, the database server you’re connecting to at runtime suddenly goes down, you got your logic mixed up and accidentally deleted all records from a database table instead of just one, you try to delete a record from a database table that still has associated records, you try to write a file to a folder without proper permissions, your users enter incorrect data, and so forth. To understand these problems and think of ways to anticipate, avoid, and handle them, you first need to understand the different types of errors that may occur in your web site. Once you understand the main differences, the remainder of this section is spent discussing ways to prevent and solve them.
Different Types of Errors You can broadly categorize errors into the following groups: ➤➤
Syntax errors: Errors that are caused by typos, missing keywords, or otherwise incorrect code.
➤➤
Logic errors: Errors in applications that seem to run fine but that produce unexpected or unwanted results.
➤➤
Runtime errors: Errors that cause the application to crash or behave unexpectedly at runtime.
Each of these categories is discussed in the following sections, together with information on avoiding and fixing them.
Syntax Errors Syntax errors, or compile errors, are the easiest to find and fix because they happen during development. The IDE tells you when an error occurs and often prevents you from running the application while it still contains errors. Syntax errors are caused by simple typos, missing or duplicate keywords and characters, and so on. The following examples all show errors that are caught at development time by the compiler. A compiler is a program that turns the human-readable code you write in VB.NET or C# into machine-readable code that can be executed. VB.NET
// Required parameter for the // Write method is missing
if (i > 10)
// Missing opening brace or // extraneous closing brace
// Do something here }
Compile errors are always displayed in the Error List (accessible through the View ➪ Error List menu) that is shown in Figure 18-1.
Figure 18-1
You can force the compiler to give you an up-to-date list of all the compilation errors in your site. To do this, from the main menu choose Build ➪ Rebuild Web Site. To go to the location where the error occurred so you can fix it, double-click the error in the Error List. To cycle through the errors and the code where the error occurs, click an error in the Error list and press F8 to go to the next error.
Logic Errors Logic errors are often harder to find because they compile fine but only happen during the execution of your code. Consider the following buggy example: VB.NET
Dim fromAddress As String = “[email protected]” Dim toAddress As String = EmailAddress.Text myMessage.From = New MailAddress(toAddress) myMessage.To.Add(New MailAddress(fromAddress)) C#
658 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Although it’s easy to see what the problem is in this example (the To and From addresses are mixed up), it may be harder to find in a file with 250 lines of code. Additionally, because the compiler happily accepts your mistake, you won’t notice the error until you see a message in your Inbox that you thought you sent to your visitors. The best way to track down and solve logic errors is using the built-in debugging capabilities of VWD. You get an in-depth look at these tools later in the chapter.
Runtime Errors Runtime errors occur at runtime, which makes them incredibly difficult to track. Imagine you have a site that contains a bug that’s hidden somewhere in a page. You haven’t found it yet, but one of your visitors did and she gets a nasty error message (more on those later) instead. What can you do? Probably not much, because there’s a fair chance your visitor won’t even inform you about the mistake. So, it’s important to have a good error handling strategy in place that enables you to avoid errors when possible, and that handles them gracefully and optionally logs relevant information for you when they occur. The following section deals with detecting and handling errors, or exceptions in .NET parlance; later in this chapter, you learn how to log errors and shield your users from ugly pages with detailed exception messages.
Catching and Handling Exceptions Normally, when serious exceptions occur, the user is presented with an error message. For example, if you try to send a message to a mail server that isn’t up and running, or doesn’t allow you to connect to it, you’ll get an Exception type that provides details about the error. By default, this exception bubbles up all the way to the user interface where it’s presented as a so-called Yellow Screen of Death, a reference to Windows’ “Blue Screen of Death” that you get when Windows crashes. You see a real example of this error in the next exercise. Obviously, it’s a lot better if you can anticipate the exception and write some code that prevents it from appearing in the user interface. You could, for example, display a nicely formatted message to users instead, informing them the message could not be sent at this moment. Fortunately, support for these kinds of scenarios is integrated deeply in the .NET programming languages such as C# and Visual Basic .NET. In these languages, you can make use of Try Catch Finally blocks (try catch finally in C#) where code that could potentially throw an exception is wrapped in a Try block. When an exception occurs, the remainder of the code in the Try block is skipped and some code in a Catch block can be run to deal with the error. You can have multiple Catch blocks that all deal with specific exceptions, but only one will fire, as you see in a later exercise. A Try or a Catch block can be followed by a Finally block. Code in a Finally block is always fired, regardless of whether an exception occurred and as such is an ideal location for some clean-up code.
Exception Handling ❘ 659
Both Catch and Finally blocks are optional, although you always need at least one of them. The code in the following example tries to send an e‑mail and then sets the Text property of a Label to the value of the variable userMessage. The userMessage variable is assigned a value in either the Try block (when the code executed successfully) or in the Catch block (when an error occurred). Either way, this userMessage is always assigned to the Label in the Finally block: VB.NET
Dim userMessage As String = String.Empty Try mySmtpClient.Send(myMessage) userMessage = “Message sent” Catch ex As Exception userMessage = “An unknown error occurred.” Finally Message.Text = userMessage End Try C#
In this code example, the Catch block is set up to handle an exception of type System.Exception, the base class of all exceptions in the .NET Framework. This exception is sent to (or caught by) the Catch block in the ex variable. Because this example doesn’t use the ex variable, you could leave it out altogether: VB.NET
Catch userMessage = “An unknown error occurred.” End Try C#
660 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
The ability to specify an Exception type is useful when you think your code can encounter more than one exception. In that case, you can have multiple Catch blocks for different Exception types. The following code is able to handle a specific SmtpException that may occur during the mail sending operation, while it’s also capable of catching all other exceptions using its generic Catch block: VB.NET
Try mySmtpClient.Send(myMessage) Catch smtpException As SmtpException Message.Text = “Sorry, an error occurred while sending your message.” Catch ex As Exception ‘ Something else went wrong. End Try C#
try { mySmtpClient.Send(myMessage); } catch (SmtpException smtpException) { Message.Text = “Sorry, an error occurred while sending your message.”; } catch (Exception ex) { // Something else went wrong. }
The order of the exception handling blocks is important. .NET scans the list of Catch blocks from top to bottom and only fires the code in the first block that matches a specific type of exception. In the preceding example, when an SmtpException occurs (which is a subclass of Exception) it will be caught by the Catch block that handles exceptions of type SmtpException. Although an SmtpException is also an Exception, the code in the last Catch block won’t be fired anymore because only the first matching Catch block is handled. Therefore, if you reverse the order of the catch blocks in this example, the more generic Exception block would be executed, and the code in the SmtpException block would never run. In the following exercise you see how to use Try and Catch in your code.
Try It Out
Handling Exceptions
In this Try It Out you see how to write exception-handling code to catch problems with incorrect e‑mail addresses. This is quite a common problem because users often enter an incorrect e‑mail address. For example, they may forget the at symbol (@) or enter a comma instead of a period. When trying to send an e‑mail with an invalid From or To address your code will crash if you don’t take the proper precautions. You’ll try out the Try Catch code in a separate page in the Demos folder so you can closely watch its behavior. When you understand how it works, you’ll modify the ContactForm.ascx user control and incorporate the exception handling code there. The reason the code is not applied to the user
Exception Handling ❘ 661
control directly is because it uses an Ajax UpdatePanel that shields users from the dirty details of an exception by default.
1 .
Create a new file in the Demos folder and call it ExceptionHandling.aspx. Base the page on your custom template and set its Title to Exception Handling Demo.
. 2 3.
Add a Label control to the main content area and set its ID to Message. Switch to the Code Behind and add either an Imports or a using statement for the System.Net .Mail namespace to the top of the file: VB.NET
Imports System.Net.Mail C#
using System.Net.Mail;
4.
Switch to Design View and set up an event handler for the Load event of the page by doubleclicking the read-only area of the page. Add the following code to the event handler. Notice how this code is almost identical to the code you added in the ContactForm.ascx user control, so you can save yourself some typing by copying parts of the code from that file. Notice how the From address is invalid because it doesn’t contain an at symbol (@). This is done deliberately to trigger an exception. VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Dim myMessage As MailMessage = New MailMessage() myMessage.Subject = “Exception Handling Test” myMessage.Body = “Test message body” myMessage.From = New MailAddress(“you.yourprovider.com“) myMessage.To.Add(New MailAddress(“[email protected]“)) Dim mySmtpClient As New SmtpClient() mySmtpClient.Send(myMessage) Message.Text = “Message sent” End Sub C#
662 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Don’t forget to change the two e‑mail addresses to your own, and don’t forget to introduce a mistake in at least one of them. Refer to Chapter 9 to learn more about setting up the web.config file for sending e-mail. 5.
Press Ctrl+F5 to open up the site in your browser. You should see the “Yellow Screen of Death” with the error message, displayed in Figure 18-2.
Figure 18-2
Note that the Exception Details section tells you that a System.FormatException occurred. . 6
Go back to VWD and wrap the code that assigns the addresses and sends the message in a Try Catch block: VB.NET
Try myMessage.From = New MailAddress(“you.yourprovider.com“) myMessage.To.Add(New MailAddress(“[email protected]“)) Dim mySmtpClient As New SmtpClient() mySmtpClient.Send(myMessage) Message.Text = “Message sent” Catch Message.Text = “An error occurred while sending your e‑mail. Please try again.” End Try C#
try { myMessage.From = new MailAddress(“you.yourprovider.com“); myMessage.To.Add(new MailAddress(“[email protected]“)); SmtpClient mySmtpClient = new SmtpClient(); mySmtpClient.Send(myMessage); Message.Text = “Message sent”; } catch { Message.Text = “An error occurred while sending your e‑mail. Please try again.”; }
Exception Handling ❘ 663
Notice how the code still contains an invalid e‑mail address. . 7
Save your changes and request the page in your browser again. You should now see a user-friendly error message, visible in Figure 18-3.
Figure 18-3
The exception that is thrown is now caught in the Catch block. Instead of getting an error page with all the technical details of the exception, the user now gets a friendly message explaining something went wrong. . 8
Go back to your code and fix the e‑mail address: VB.NET
Save your changes and request the page again. You’ll now get a message indicating that the message was sent successfully.
1 0.
Open ContactForm.ascx from the Controls folder, switch to its Code Behind, and wrap the code that creates and sends the message in the following Try Catch block: VB.NET
Try Dim myMessage As New MailMessage() ... System.Threading.Thread.Sleep(5000) Catch Message.Text = “An error occurred while sending your e‑mail. Please try again.” Finally Message.Visible = True End Try
664 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Notice how the line that sets the Message control’s Visible property to True is now in a Finally block. This way, the Label is made visible, regardless of whether an error occurred. From now on, whenever an error occurs during the sending of the e‑mail, your users will get a normal error message instead of the full error detail page that .NET displays by default.
How It Works When an exception occurs at runtime, .NET checks if the code is wrapped in a Try Catch block. If that’s the case, it scans the list of Catch blocks for an Exception type that matches the exception being thrown. Only the first Catch block that matches the Exception is being called; all remaining Catch blocks are ignored. Code in the Try block following the line that caused the exception is not executed anymore. In this exercise, the code was set up to handle the general Exception type. Because this type is the base type of all exceptions, it will be fired for all exceptions, regardless of their type. As you learned before the exercise, you can have multiple Catch blocks, each one dealing with a specific type of exception.
With these Try Catch Finally blocks you can write code that helps you deal with errors that you think might happen in your code. It’s always a good practice to wrap code that might throw an error in a Try Catch block so you can deal with it gracefully. Some examples of code that may throw an exception include: ➤➤
Sending e‑mail: The mail server may be down, or you may not have the permissions to access it.
➤➤
Accessing a database: The server may be down, you may not have permissions, you may get an error due to foreign key constraint violations as you saw in Chapter 15, and so on.
➤➤
Trying to write an uploaded file to disk: The disk may be full, you may not have the necessary permissions to write to disk, or you are providing an invalid file name.
Although Try Catch blocks are great to avoid exceptions from bubbling up to the user interface, you should use them with care, because they come at a cost. A Try Catch block is generally slower
Exception Handling ❘ 665
than code without it, so you shouldn’t use a Try Catch block for errors you can avoid otherwise. Consider the following example that divides two numbers: VB.NET
Dim value1 As Integer = Convert.ToInt32(ValueBox1.Text) Dim value2 As Integer = Convert.ToInt32(Value2Box.Text) Try result = value1 / value2 ResultLabel.Text = result.ToString() Catch ex As DivideByZeroException ResultLabel.Text = “Sorry, division by zero is not possible.” End Try C#
int value1 = Convert.ToInt32(ValueBox1.Text); int value2 = Convert.ToInt32(ValueBox2.Text); try { result = value1 / value2; ResultLabel.Text = result.ToString(); } catch (DivideByZeroException ex) { ResultLabel.Text = “Sorry, division by zero is not possible.”; }
In this example, the code is set up to expect a DivideByZeroException. This exception is thrown when value2 contains the value 0. Although at first it seems like a good idea to implement exception handling here, it’s actually much better to write code that checks for this value before the division is carried out, instead of letting an exception occur: VB.NET
If (value2 0) Then result = value1 / value2 ResultLabel.Text = result.ToString() Else ResultLabel.Text = “Sorry, division by zero is not possible.” End If C#
if (value2 != 0) { result = value1 / value2; ResultLabel.Text = result.ToString(); } else { ResultLabel.Text = “Sorry, division by zero is not possible.”; }
666 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Of course it would even be better if you had placed a CompareValidator on the page, making sure that ValueBox2 could never contain the value zero. Chapter 9 explains how to use this control. Although Try Catch blocks are useful to catch exceptions that you anticipate, what about errors you don’t? How can you deal with unexpected errors? Because they are unexpected, you won’t know when they occur, so it’s difficult to write code to handle them. To solve this problem, the next section shows you how you can globally catch and log unhandled exceptions and send information about them by e‑mail. This way, you know they occurred, giving you, the page developer, a chance to fix them before they happen again.
Global Error Handling and Custom Error Pages To shield your users from the technical details of the exception, you should provide them with a user-friendly error page instead. Fortunately, ASP.NET enables you to define custom error pages: ASPX pages that are shown to the user when an exception occurs. You can map different types of errors (server errors, page not found errors, security problems, and so forth) to different pages. You define the error page or pages you want to show in the customErrors element of the web.config file. A typical element looks like this: <customErrors mode=”On” defaultRedirect=”~/Errors/Error500.aspx” redirectMode=”ResponseRewrite”> <error statusCode=”404” redirect=”~/Errors/Error404.aspx” /> <error statusCode=”500” redirect=”~/Errors/Error500.aspx” />
The mode attribute determines whether or not a visitor to your site gets to see a detailed error page. The attribute supports the following three values: ➤➤
On: Every visitor to your site always sees the custom error pages when an error occurs.
➤➤
Off: The custom error page is never shown and full error details are displayed on the page.
➤➤
RemoteOnly: The full error details are shown to local users (browsing the site from the same
machine as the site runs on), while all other users get to see the custom error page. This setting allows you to see error messages on your site during development, while your users are always presented with the custom error page. Within the opening and closing tags of the customErrors element you define separate <error /> elements, one for each HTTP status code you want to support. The previous configuration defines two custom pages: one that is shown when the requested page could not be found (a 404 status code) and one that is shown when server errors occur (a 500 code). For all other HTTP status codes you haven’t defined explicitly, the defaultRedirect attribute is used to determine the custom error page. The redirectMode attribute determines the way the new page is shown to the user and is discussed later in this chapter. Although custom error pages shield your users from the exception details, they don’t help in informing you that an exception occurred. All these pages do is hide the real error and show a page with
Exception Handling ❘ 667
a custom error message instead. To be notified about these exceptions, you need to write some code that looks at the exception and then sends you an e‑mail with the details. Alternatively, you could write code that inserts the error details in a database, or writes them to a text file. ASP.NET offers you a handy, central location to write code that is triggered when an exception occurs. You write this code in a special event handler called Application_Error inside the Global.asax file, a special file (note the .asax extension) that lets you write code that is triggered in response to events that are applicable site-wide, such as the start of the application, or the start of a request for a resource. Inside this event handler you can collect relevant data about the exception, stick it in an e‑mail message, and send it to your own Inbox. This gives you detailed information about exceptions that occur on your site, aiding in fixing the problem as soon as possible. You see how to write this code in the next exercise.
Try It Out
Handling Exceptions Site Wide
In this Try It Out you learn how to write code in the Global.asax file to send the exception message by e‑mail. Additionally, you see how to create global error pages that are shown to your user in case of an error.
1 .
Right-click the web site in the Solution Explorer and choose Add New Item. In the list with templates, select Global Application Class. Leave its name set to Global.asax and click Add.
. 2
At the top of the code of the file, right after the Application directive, add the following Import statement. Note that when adding an Import statement in Markup View, both VB.NET and C# use the keyword Import, rather than Imports and using that you normally use in Code Behind: VB.NET
C#
3.
Inside the Application_Error handler that should already be present in the Global.asax file, add the following highlighted code that is triggered whenever an unhandled exception occurs in your site. If the handler isn’t there, make sure you type all the code from the following snippet, including the parts that are not bolded: VB.NET
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) If HttpContext.Current.Server.GetLastError() IsNot Nothing Then Dim myException As Exception = HttpContext.Current.Server.GetLastError().GetBaseException() Dim mailSubject As String = “Error in page “ & Request.Url.ToString() Dim message As String = String.Empty message &= “<strong>Message ” & myException.Message & “ ” message &= “<strong>StackTrace ” & myException.StackTrace & “ ” message &= “<strong>Query String ” &
668 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Request.QueryString.ToString()& “ ” Dim myMessage As MailMessage = New MailMessage(“[email protected]“, “[email protected]“, mailSubject, message) myMessage.IsBodyHtml = True Dim mySmtpClient As SmtpClient = New SmtpClient() mySmtpClient.Send(myMessage) End If End Sub C#
Don’t forget to change the two e‑mail addresses that are passed to the MailMessage’s constructor. The first address represents the sender’s address, and the second one holds the recipient’s address. . 4 . 5
Save all your changes and close the Global.asax file. Next, open up the web.config file and as a direct child of <system.web> add the following customErrors element: <system.web> <customErrors mode=”On” defaultRedirect=”~/Errors/OtherErrors.aspx” redirectMode=”ResponseRewrite”> <error statusCode=”404” redirect=”~/Errors/Error404.aspx” />
Save and close the configuration file. . 6 . 7
Create a new folder in your web site and call it Errors. Inside this new folder, create two new Web Forms and call them Error404.aspx, and OtherErrors.aspx respectively. Make sure both of them are based on your custom template so they are using the main master page and inherit from BasePage. If you followed the exercises in the previous chapter and now use Profile to store the user’s favorite theme, refer to the Common
Exception Handling ❘ 669
Mistakes section at the end of this exercise to learn about the pitfalls of using the Master Page and BasePage for your custom 404 error page. 8.
Set the Title of Error404.aspx to File Not Found. Inside the content placeholder for the main content add the following markup: File Not Found
The page you requested could not be found. Please check out the Homepage or choose a different page from the menu.
The Planet Wrox Team
9.
Switch to Design View, double-click the page to set up a Page_Load handler, and add the following code to it: VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load Response.Status = “404 Not Found” Response.StatusCode = 404 End Sub C#
10. Open the generic OtherErrors.aspx page, set its Title to An Error Occurred, and enter the following content: Un unknown error occurred
An error occurred in the page you requested. The error has been logged and we’ll fix it ASAP.
The Planet Wrox Team
Because this page will be used for all possible errors except a 404 error, there’s no point in setting an explicit Status or StatusCode.
1 1.
Save the changes to all open files by pressing Ctrl+Shift+S and then close them. Right-click Default.aspx in the Solution Explorer and choose View In Browser. Once the page has finished loading, request a nonexistent page like DefaultTest.aspx by changing the address bar of the browser to something like http://localhost:1208/DefaultTest.aspx. Obviously, the DefaultTest.aspx page does not exist, so you get an error. But instead of a detailed error page, you should now get the error page you defined and created in this Try It Out, shown in Figure 18-4.
670 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Figure 18-4
COMMON MISTAKES If, instead of this error message, you get a generic “File not found” exception page, check the syntax of the customErrors section in the web.config file. Additionally, check that you can successfully request the actual error pages ( Error404.aspx and OtherErrors.aspx ) by directly requesting them in the browser. If they contain an error (for example, if you forgot to set the page title) they cannot be used as custom error pages. You also get the generic error page when you followed the exercises in the preceding chapter and rewrote the preferred theme selector to use Profile. Because of the way a 404 error is handled internally, you cannot use Profile in the error page or the master page it’s based on. To work around this problem, recreate the Error404.aspx page as a standard Web Form not using your master page and BasePage and things should work fine. You could also set the redirectMode attribute in the web.config to ResponseRedirect but from a search engine optimization point of view this is not recommended.
Besides the error page in the browser, you should also get a message by e‑mail that provides more details about the error. Figure 18-5 shows the message you get when you request a page that does not exist.
Figure 18-5
Exception Handling ❘ 671
How It Works Two important parts are worth examining in this exercise. The first part is the way the ASP.NET runtime hides the Yellow Screen of Death with the error details from the user with the use of custom error pages. This serves two purposes. First, it helps you protect potentially private data like passwords or information about database connections that may end up in the error message. Second, it shields your users from cryptic error messages they probably don’t understand anyway and gives you the chance to display a good-looking error page instead that integrates with the site’s look and feel. The only thing you need to do to make this work is enable custom errors in the web.config file and provide one or more pages you want to display for the errors that may occur in your site. The configuration element lets you set up different pages for different exceptions: <customErrors mode=”On” defaultRedirect=”~/Errors/OtherErrors.aspx” redirectMode=”ResponseRewrite”> <error statusCode=”404” redirect=”~/Errors/Error404.aspx” />
When .NET encounters a 404 exception (when you request a page that cannot be found), you are shown the contents of the Error404.aspx page. The name and content of this page are completely up to you, giving you the option to provide your own explanation to the user about what went wrong. Note that this only works for file types that are registered with ASP.NET, like .aspx files. It won’t work out of the box for .html files or images when you’re not using the Integrated Pipeline mode of IIS, Microsoft’s web server. You see more about the Integrated Pipeline mode in the next chapter. This exercise contains two key elements to improve Search Engine Optimization (SEO). First, notice how the redirectMode is set to ResponseRewrite. The other option is ResponseRedirect. Remember the difference between Server.Transfer and Response.Redirect from Chapter 7? These two settings are based on the same principles. If you set redirectMode to ResponseRedirect, the browser (and thus a search engine) is redirected to the error page. The error page then returns a 404 code so the search engine thinks that the error page itself could not be found. If, however, you set the setting to ResponseRewrite, the originally requested page results in a 404 error code while the contents of the Error404.aspx page are streamed to the browser. This enables search engines to correctly update their indexes. The only downside of the ResponseRewrite setting is that you can’t use Profile in the page or a master page it is based on. As you saw earlier, the best work around is to create a page that is not based on your custom template. The second part that improves SEO is the code in the Code Behind of Error404.aspx. That code sets the HTTP status code to 404, to indicate the page could not be found on the server. Without these two lines of code, search engines won’t understand the page doesn’t exist and will keep trying to index it. Notice that only the 404 error code is redirected to its own page. All other exceptions cause the generic OtherErrors.aspx to be loaded. You can, however, add multiple <error /> elements to the <customErrors> element, each one for a different status code. For a list of HTTP status codes, check out this knowledge base article: http://support.microsoft.com/default.aspx/kb/943891.
672 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
The other main part of this exercise is the code in Global.asax that fires when an unhandled exception occurs. In that case, the event handler Application_Error is triggered. Within this handler, you can retrieve the exception that occurred with this code: VB.NET
If HttpContext.Current.Server.GetLastError() IsNot Nothing Then Dim myException As Exception = HttpContext.Current.Server.GetLastError().GetBaseException() C#
if (HttpContext.Current.Server.GetLastError() != null) { Exception myException = HttpContext.Current.Server.GetLastError().GetBaseException();
To get at the root exception that caused the problem in the first place, you need to call GetBaseException() on the Exception that is returned by Server.GetLastError(). This Exception instance, stored in the myException variable, then gives you access to useful properties such as Message and StackTrace. In this exercise, the StackTrace displayed in the error e‑mail contains information that really isn’t of any interest to you. However, with other exceptions, such as one thrown by an incorrect configuration of the mail server or a division by zero exception, the StackTrace gives you information about the file that generated the error, the method that caused it, and even the line number in the code, making it easy to find the error and fix it. The remainder of the code creates an e‑mail message with the error details. It also adds information about the query string with this code: VB.NET
Knowledge of the query string helps in debugging a problem if values from the query string are used. You could extend the code in Application_Error and add other useful information such as cookies and form collections. For more information about accessing these kinds of collections, pick up a copy of Professional ASP.NET 4 published by Wrox. Alternatively, look into ELMAH — the Error Logging Modules and Handlers project at http://code.google.com/p/elmah/, an open source project run by Atif Aziz that is aimed at catching and logging exceptions. The beauty of the ELMAH project is that it’s extremely simple to integrate in your site (no programming required, you just need to add a few lines of configuration code to your web.config file). I have been using it for most of my web projects in the past couple of years, and it has helped me find many bugs that otherwise would have gone unnoticed.
The Basics of Debugging ❘ 673
Although the ability to handle and log exceptions at runtime is useful, it’s of course better to prevent them from happening in the first place. To write solid code, with as few bugs as possible, you need good tools to help you understand the execution of your code so you can debug it. VWD comes with excellent debugging tools that aid you in this process. You see what these tools are and how to use them in the next section.
The Basics of Debugging Debugging is the process of finding and fixing bugs in your code. Although that may sound easy, it often isn’t. Some bugs are very obvious and easy to spot and thus easy to fix. Others are much harder to find and require knowledge about the execution environment of your program. The debugging tools that ship with Visual Web Developer help you understand this execution environment by giving you direct access to the inner workings of your program or web page. Debugging with VWD is like snapping your fingers to stop the time. When you do that, everything halts, except for you, so you can walk around in your code, investigate variables, look into objects, try out methods, and even execute new code. To tell VWD where to halt, you need to set one or more breakpoints in your code. When the code under the breakpoint is about to be executed, VWD stops the execution of the application (usually a web page, a user control, or code in the App_Code folder) and then puts focus back on VWD so you can diagnose the code and its environment. You set a breakpoint by pressing F9 on the line of code where you want execution to halt. Instead of the F9 shortcut key, you can also click the margin of the code, where the big dot appears in Figure 18-6, or you can choose Debug ➪ Toggle Breakpoint from the main menu. Pressing F9, clicking the same spot in the margin, or choosing the menu item again toggles the presence of the breakpoint. To clear all breakpoints in your entire web site, press Ctrl+Shift+F9. To give you an idea of how debugging works, and what it can do to help you, the following exercise shows you the basic operations of debugging. Later parts of this chapter give you a detailed look at the numerous debugging tools and windows that ship with VWD.
Try It Out
Debugging Your Application
In this Try It Out you debug the Calculator page you created in a previous chapter. If you don’t have the file, refer to Chapter 5 or download the code for this chapter from www.wrox.com. The debugging exercises in this chapter assume you are using Internet Explorer as your browser. If you are using a different default browser, such as Firefox or Opera, the debugging experience will be largely the same although you may find that VWD does not always get the focus automatically while breaking into your code.
1 .
Open the page CalculatorDemo.aspx from the Demos folder and switch to Code Behind.
674 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
2.
Click the first line of code in the CalculateButton_Click handler that checks the length of the text in the two TextBox controls. Then press F9 to set a breakpoint. The line gets highlighted, as shown in Figure 18-6, and a colored dot appears in the margin of the Document Window.
Figure 18-6
3.
Press F5 (instead of Ctrl+F5 which you have been using so far) to open the web site in your browser and start the debugging process. Alternatively, choose Debug ➪ Start Debugging from the main menu. If you get the dialog box in Figure 18-7, click OK to have VWD modify the web.config file for you.
Figure 18-7
Depending on your browser’s setup, you may be confronted with the dialog box shown in Figure 18-8.
Figure 18-8
The Basics of Debugging ❘ 675
You may get this dialog box if you’re running Internet Explorer 7 or earlier and you haven’t set up IE to allow debugging client-side script, something you need in later exercises in this chapter. If you get this dialog box, follow the instructions it displays and then click the Yes button when you return to VWD. If you configured Internet Explorer according to the instructions, you won’t see this dialog box again. Don’t worry if you don’t get this dialog box at all; you only get it when you need to change something in your setup. . 4
The page should load normally, showing you the two TextBox controls, the DropDownList, and the Button.
COMMON MISTAKES If you get an error stating that the page title is not valid, close your browser, return to VWD, give the page a title, save your changes, and press F5 again.
Enter 5 in the first text box, 7 in the second, and then click the Calculate button. Instead of getting the answer in your browser, you are now taken back to VWD. If you’re not taken back to VWD directly, you may need to switch to it manually. You’ll see the taskbar icon of VWD flash to get your attention. . 5
In VWD, the line with the breakpoint is now highlighted in yellow. Additionally, you see a yellow icon in the document margin to indicate this line of code is about to be executed. However, before it does, you get a chance to look at your controls, variables, and other elements that make up the execution environment. To see the values you entered in the TextBox controls, hover your mouse over the Text properties in the highlighted lines. You’ll see, as shown in Figure 18-9, a small tooltip appear that displays the value you entered.
Figure 18-9
6.
Hover your mouse over some of the variables in the code like result and value1. Note that you won’t get a tooltip, because the code hasn’t reached the point where these variables are declared. As far as the debugger is concerned, they don’t exist.
. 7
To advance in the code, press F10. This steps over the selected line, executing it. Keep pressing F10 until the line that declares the value2 variable is highlighted. When you now hover your mouse over value1, the tooltip appears, indicating that value1 now contains the value 5.0.
676 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
8.
Hover your mouse over the SelectedValue property in the Select Case (in VB.NET) or switch statement (in C#). Note that the tooltip shows you the value you selected in the drop-down list (the plus symbol). Even if this line of code hasn’t been executed, the SelectedValue has been assigned a value earlier so you can look at it here.
9.
Press F10 a few more times until you reach the line that assigns a value to the ResultLabel control. Hover your mouse over the result variable (you may need to highlight the result variable first with your mouse if you are using VB.NET) and note that it displays the number 12, the outcome of the calculation, shown in Figure 18-10.
1 0.
Figure 18-10
Finally, press F5. By pressing this key, the code continues until it finds the next breakpoint. Because you haven’t defined another breakpoint in your code, the remainder of the code in the Click event handler is executed and the result is displayed in the page.
How It Works Whenever you press Ctrl+F5 to view a page in a browser as you have done up until this chapter, nothing special happens. VWD simply opens the browser, which then requests the page from the built-in web server. However, when you press F5 instead, VWD gets in debugging mode and respects the breakpoints you have set in your code. Whenever a breakpoint is hit, execution of the code is stopped so you can look at the code and its execution environment that gives you access to variables, controls, methods, and much more. Note that the code on the line with the breakpoint has not been executed at this point. You need to step through it, using F10, F11, or F5, for example, to execute it. Before you can debug your code, you need to configure the application to support it. You do this by setting the debug attribute of the compilation element in web.config to true:
If you’re using Visual Basic, you may see two additional attributes on this element: strict and explicit. By default, strict is set to false, which means Visual Basic will do silent casts and conversions for you. The explicit attribute is set to true, which means you need to declare all your variables before you can use them. For most scenarios, these defaults are fine. Whenever you start debugging and the debug attribute of the is set to false, you get the dialog box shown in Figure 18-7 offering to turn it on for you. To avoid the overhead this setting brings, you should always set it to false on a production server. In this exercise you also learned how to use the data tips, the small tooltip windows that appear when you hover your mouse over selected variables. For simple types, such as an Integer or a String, all you see is the variable’s value. For complex types, such as results returned from a LINQ query, you get a much richer data tip, providing you with a lot more detail.
Useful as the debugging data tips may seem, they are only a small part of the rich debugging features. In the next section you get an overview of all the debugging tools that ship with VWD.
Tools Support for Debugging ❘ 677
Tools Support for Debugging With a number of shortcut keys and menu items, VWD lets you move around the code that you are debugging, giving you the option to execute code line by line or larger blocks of code at once. Additionally, the IDE provides you with a lot of windows that enable you to diagnose and change the execution environment, including the values of variables at runtime. You see how to move around code first, which is followed by a discussion on the numerous debugging windows.
Moving around in Debugged Code When your code has halted on a breakpoint, you can use a number of keyboard shortcuts to determine what to do next. The following table lists the most common shortcuts. Key
Description
F5
Press this key to start debugging, as demonstrated in the previous exercise. When pressed during debugging, the code will continue until the next breakpoint is hit, or until all code is finished executing.
F11
Press this key to execute the current line and step into a member that’s being called if possible. You see how this works later.
F10
Press this key to execute the current line without stepping into the code that is being called, unless that code itself contains a breakpoint.
Shift+F11
Press this key combination to complete the code in the current code block and return to the code that initially called it.
Shift+F5
Press this key combination to stop debugging. This closes the browser and returns you to VWD.
Ctrl+Shift+F5
Press this key combination to restart the debugging process.
In addition to these keyboard shortcuts, you can also use the Debugging toolbar shown in Figure 18-11, which offers similar functionality. Show Next Statement Break All Restart
Step Over
Step Out
Show Threads in Source
Start Debugging Stop Debugging
Step Into
Hexadecimal Debug Display Windows
Figure 18-11
This toolbar should appear automatically when you start debugging, but if it doesn’t, right-click an existing toolbar and choose Debug.
678 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
While you are debugging your code in VWD, you have a number of debugging windows at your disposal, which are discussed in the following section.
Debugging Windows The numerous debugging windows enable you to watch the variables in your application and even change them during runtime. Additionally, they provide you with information about where you are in the application and what code was previously executed. All this information helps you understand the execution flow of your application. You access all the debugging windows through the Debug ➪ Windows menu option. Not all of them are available in the Express edition of Visual Web Developer. Also, to access most of the windows, your application must be in debug mode first. The next sections show you the different windows you have available. In the exercise that follows, you get a chance to work with them so you understand how to use them and why they are useful.
Watching Variables Knowing the values of your variables is critical to understanding your application. To help you with this, VWD offers three debugging windows that provide you with useful information. All these windows support changing the value of your variables at runtime, enable you to use data tips to dig deeper into the objects, and enable you to copy and paste data so you can reuse it somewhere else.
The Watch Window This is probably the most important window for you to keep an eye on. It enables you to watch all your variables and dig into them. Figure 18-12 shows the Watch window that is currently watching the value1 variable used in the Calculator page while the value2 variable is being added to the list.
Figure 18-12
You can add your variables to the Watch window in a few different ways. First of all, you can click the Watch window once and then start typing a variable name. You can then press Ctrl+Space to bring up the IntelliSense list, making it easy to complete the variable’s name. Alternatively, you can right-click a variable in the Document Window and choose Expression: ‘VariableName’ ➪ Add Watch. Depending on your version of Visual Web Developer, the Add Watch menu option may be available directly on the right-click menu of the Document Window. And finally, you can highlight a variable in the Document Window and then drag it into the Watch window. When your variables are in the Watch window, you can change their values to influence the execution of your code. For example, you could change the value of the value1 variable to a different
Tools Support for Debugging ❘ 679
number, changing the outcome of the calculation. To change a value, double-click it in the Value column of the Watch window. Alternatively, right-click the watched variable and choose Edit Value. Besides showing variables’ values, you can also use the Watch window to execute code. For example, you could call ToString() on the value1 variable to see what its string representation looks like. To do this, double-click the variable name in the Watch window so it becomes editable and then add .ToString() as shown in Figure 18-13.
Figure 18-13
You are not limited to calling ToString in the Watch window. Any expression that has a result can be executed here. However, the Immediate window, discussed later, is much more appropriate for executing code on the fly. Besides the Watch window, the Autos and Locals windows are available. They work more or less the same as the Watch window.
The Autos Window The Autos window is available only in the commercial versions of Visual Studio and not in the Express edition. Because it’s so similar to the Watch window, this isn’t really a problem. The Autos window shows the variables used by the current and previous code statements and is updated automatically as you step through the code.
The Locals Window The Locals window is also similar to the Watch and Autos windows, but the Locals window shows all variables that are currently in scope (can be “reached”) by the code that is currently executing. This is a useful window, because it shows you all relevant variables without requiring you to add them manually.
Other Windows Besides windows to watch variables, VWD has a few other useful windows available.
The Breakpoints Window The Breakpoints window gives you an overview of all breakpoints you have set in code throughout your entire web site. Unfortunately, this window is not available in the Express edition, so you have to find breakpoints manually by looking at the actual code.
Call Stack Window The Call Stack window provides you with information about the order in which your code has been executed or called. Each call from one piece of code into another is placed on a stack of calls that you can navigate. It looks a bit cryptic at first, but once you understand how it works, it enables you to jump through your code quite easily. Figure 18-14 shows the Call Stack window inside the Add method of the Calculator class.
680 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Figure 18-14
In the highlighted line you can see that Calculator.Add is the currently active code. Right below it you see CalculateButton_Click, the event handler in the Calculator page that called the Add method. Double-clicking a line in the Call Stack window takes you to the appropriate code location.
Immediate Window The last interesting debugging window is the Immediate window. This window lets you execute code as if you had written it in a page. You can use this window to test expressions, see what values a function returns, and so on. For example, when you are in debug mode, you can enter the following command in the Immediate window: VB.NET
? New Calculator().Add(3, 4) C#
? new Calculator().Add(3, 4);
The question mark is used to output to the Immediate window. The code then instantiates a new Calculator instance and directly passes the values 3 and 4 to its Add method. The code will be executed and the Add method returns 7, which is then printed in the Immediate window. This window is great for quickly testing out code. Instead of writing code you want to test in a page, you can type it directly in the Immediate window and see its output. In the following Try It Out, you see these debugging windows at work.
Try It Out
Extensive Debugging
In this exercise you look at all the debugging windows that have been discussed earlier. Because a lot of windows and options are available, you won’t see a detailed discussion of every step in the process. Instead, you’re encouraged to experiment. Try adding more variables to the Watch window, type your own code in the Immediate window, and so on. Experimenting is the best way to discover the large number of debugging capabilities in VWD. 1.
Open the Code Behind of CalculatorDemo.aspx again and press Ctrl+Shift+F9 to clear all previously set breakpoints. Click Yes to confirm the deletion.
. 2
Click the line that declares the variable value1 and press F9 to set a breakpoint. Your Document Window should look similar to Figure 18-15, which shows the Document Window for the C# project.
Tools Support for Debugging ❘ 681
Figure 18-15
3.
Press F5 or choose Debug ➪ Start Debugging from the main menu to start debugging the application. Enter the number 5 in the first text box, ensure that the plus sign is selected in the drop-down list, and enter 7 in the second text box. Then click the Calculate button and VWD breaks at the breakpoint you set in the previous step. If you aren’t taken there automatically, switch back to VWD manually.
. 4
Hover your mouse over the OperatorList variable that is used a few lines below the current breakpoint and notice how VWD displays a data tip with a plus (+) symbol in front of it. This means you can expand the item to get detailed information about the variable. Figure 18-16 displays the expanded data tip.
Figure 18-16
If you are using VB.NET, you may need to double-click the OperatorList variable in the Document Window first to select it, and then hover your mouse over it. Note that you can expand other items (like the DropDownList control’s base class) to get at other relevant data. If you’re using C#, go ahead and expand the base item. Once you do that, you’ll see properties such as its SelectedValue, which is set to “+” if you didn’t change it in step 3. In VB.NET, you should see this property in the main list directly. 5.
Right-click ValueBox1 in the code at the top that checks the length of the text in the TextBox controls and choose Expression: ‘ValueBox1’ ➪ Add Watch. Again, your Add Watch option may be located directly on the right-click menu. The variable is added to the Watch window where you can expand it similar to how you expanded the data tip. Expand the item and you’ll see, for example, that the Text property is set to “5”.
. 6
Double-click the value “5” for the Text property, change it to “12” (including the quotes), and press Enter.
. 7
Open the Locals window (choose Debug ➪ Windows ➪ Locals if the window isn’t visible yet.) Press F10 to execute the line under the breakpoint. This gets the value from ValueBox1, converts
682 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
it to a double, and assigns it to value1. Look at the value1 variable in the Locals window (see Figure 18-17). It now contains 12.0, the value you assigned to the Text property of the text box in the previous step, now converted to a double.
Figure 18-17
Note that the value of the value1 variable has changed color as well. This means that the item has recently been changed. Also note that because all of this is happening at the server during a postback, the browser is unmodified, and the text box still shows the value 5. Only when the page has finished rendering to the browser will you see the new value show up. In Figure 18-17 you also see the other variables that are currently in scope, such as result, myCalculator, and this (Me in Visual Basic) that contains a reference to the page that is currently being executed. 8.
Press F10 once more so value2 is updated as well. The color of the value of the value2 variable has changed to red to indicate it has changed, while value1’s value is black again. This makes it easy to see what variables are modified by the last statement.
9 .
Press F10 until you reach the line that calls the Add method on the Calculator class. Instead of pressing F10 to execute that line, press F11. This steps into the Add method so you can see how it performs the calculation. Inside the Add method, you can hover over the method’s arguments to see their values, as demonstrated in Figure 18-18.
1 0.
Figure 18-18
Choose Debug ➪ Windows ➪ Call Stack to bring up the Call Stack window (or press Ctrl+Alt+C) and note that the Add method was called by the CalculateButton’s Click handler, shown in Figure 18-19.
Figure 18-19
11. Double-click the second line in the Call Stack window and you’ll be taken back to the Calculator page. Note that this doesn’t execute any code; all it does is show you the relevant code. Doubleclick the first line and you’ll be taken to the Add method code again.
Tools Support for Debugging ❘ 683
12. Press Shift+F11 to step out of the Add method and return to the calling code in the Calculator page. If you take another look at the Call Stack window you’ll see the line for the Add method has disappeared from the call stack.
1 3.
Open the Immediate window (choose Debug ➪ Windows ➪ Immediate) to test out some code. In the window that appeared, type the following and press Enter: VB.NET
? New Calculator().Multiply(4, 12) C#
? new Calculator().Multiply(4, 12);
The Immediate window displays the outcome of the calculation, as shown in Figure 18-20. Figure 18-20
1 4.
Finally, press F5. This executes the remainder of the code in the page. The focus will be put on the browser again, which now displays the outcome of the calculation in the Label control. If everything turned out as planned, you should see the number 19: the sum of 12 (the new value you entered for ValueBox1.Text in step 6), and 7 (that you entered in step 3).
How It Works As demonstrated in a previous Try It Out exercise, when you put a breakpoint in your code, execution is halted as soon as the line with the breakpoint is about to be executed. From there, you can jump around in your code, investigate variables, and execute statements. In this exercise you saw how to step in and over code using the F10 and F11 shortcut keys. You usually use F10 to execute a line if you’re not interested in the underlying code that is being called. You use F11 if you want to see the code that is being executed, as you saw how to do with the Add method. The data tips and Watch and Locals windows are invaluable tools in examining and changing variables and values. For example, even though you entered 5 in the first text box in the browser, you were able to change that value to 12 during debugging. Any changes you make while debugging are propagated to the rest of the code that still needs to be executed. The Immediate window lets you try out small snippets of code. This can be useful to try out some ideas, without the need to write it in the code window and debug it. In this exercise, you wrote some code that created a new Calculator instance, called the Multiply method, and outputted the value using the question mark. VB.NET
? New Calculator().Multiply(4, 12) C#
? new Calculator().Multiply(4, 12);
684 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Besides debugging code at the server, VWD comes with excellent support for debugging client-side JavaScript as well.
Debugging Client-Side Script So far, you have used the debugging tools to debug ASPX pages and Code Behind. However, that’s not all there is to it. Visual Studio 2010 also has great support for debugging client-side JavaScript. Debugging client-side JavaScript requires that you use Internet Explorer and won’t work correctly with other browsers like Firefox or Opera. The cool thing about debugging client-side JavaScript in VWD is that you already know how to do it. You can use the same familiar tools that you have seen in this chapter to debug both server-side and client-side code. The JavaScript that is eventually used by the page in the web browser can come from a lot of different sources. You can have JavaScript in external script files, embedded in the page, in a master page, and even server controls can emit their own JavaScript. This makes it difficult sometimes to break in the right code, because you don’t always know where it comes from. Fortunately, VWD has a great solution for this; it lets you set breakpoints in the final HTML being displayed in the browser. To show you in what file you’re adding breakpoints or what code you are debugging, VWD updates the Solution Explorer and displays a list of all files containing client-side script that you can step through as soon as you are in debug mode. Breakpoints you set in these files during debugging are preserved when possible, making debugging a smooth experience. The easiest way to learn the new client-side JavaScript debugging possibilities is by trying them out, so the next exercise dives right in and shows you how to debug the web service test page that you created in Chapter 10.
Try It Out
Debugging JavaScript in Internet Explorer
You need to use Microsoft Internet Explorer to carry out the following exercise because most of the features shown in this Try It Out only work with that browser. If Internet Explorer is currently not your default browser in VWD, right-click a page in the Solution Explorer and choose Browse With. Select Internet Explorer in the Browsers list and choose Set as Default. You can click Cancel to close the dialog box and VWD still remembers your default browser setting. When you’re done with this exercise, you can switch back to your favorite browser using the exact same steps. 1.
Open the file NameService.cs or NameService.vb from the App_Code folder. Locate the HelloWorld web method and set a breakpoint on the first and only line of code in the method that returns the personalized greeting. Close the file.
. 2
Open WebServices.aspx from the Demos folder and switch to Markup View. Locate the HelloWorld JavaScript method, click the line that declares the yourName variable, and press F9 to set a breakpoint, visible in Figure 18-21.
. 3
Press F5 to start debugging. The page will load in the browser and you get a text box and two buttons. Enter your name in the text box and click the Say Hello button. As soon as you click it, focus is put back on Visual Studio, and the code halts in the JavaScript code block.
Debugging Client-Side Script ❘ 685
Figure 18-21
COMMON MISTAKES If your client-side JavaScript breakpoint doesn’t get hit, close your browser to stop debugging, type the word debugger before the line you set the breakpoint on and press F5 again. VWD does not always correctly debug your client-side JavaScript breakpoints but it works fine when using the debugger keyword: debugger var yourName = $get(‘YourName’).value; NameService.HelloWorld(yourName, HelloWorldCallback);
4.
Press F10 to execute the highlighted line (you need to press it twice if you’re using the debugger keyword) and assign the name you entered to the yourName variable. Then hover your mouse over that variable and you’ll see a data tip appear.
. 5
Open the other debugging windows and notice how they all behave identically to what you saw before. You can add JavaScript variables to the Watch window to look at their values, enter JavaScript in the Immediate window for evaluation, and so on. Also note that the Solution Explorer has changed, showing the active client-side files containing script right above the web project (see Figure 18-22).
Figure 18-22
686 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
6.
To look inside these documents, you can double-click them under the Windows Internet Explorer node that has appeared in the Solution Explorer. The file WebServices.aspx should already be open in the Document Window. At first, the file may look like before. But if you look closely, you can see that this is no longer the original source file with ASP.NET controls mixed up with other markup, but the final HTML rendered in the browser. To warn you that you are looking at the final file, and not the original source, VWD has added the text [dynamic] and a lock icon to the tab for the file above the Document Window, shown in Figure 18-23.
Figure 18-23
What’s really nice about this is that even though you are looking at a runtime file, VWD is still able to relate this runtime file with the original source. This means you can set breakpoints in the runtime file, and they’ll be remembered in the original source so they are available for the next debugging session. . 7
To see how this works, set a breakpoint on the line with the alert statement in the HelloWorldCallback handler. Once the code returns from the web service that is being called in this exercise, you’ll return to this handler again, so you can investigate the value returned by the service.
. 8
Press F5 to continue executing code. The name you entered in the text box is retrieved and then sent into the HelloWorld method of the service. Because you added a breakpoint there in step 1, the code should stop again, enabling you to look at the variable yourName passed to the web method. Although this exercise itself is pretty simple, a lot of magic just happened under the hood. You stepped from some client-side code running in the browser into code running in a web service at the server, all from the same IDE.
. 9
Press F5 again and you’ll be taken back from the server-side web service into the client-side code where you can see the result of the web service in the HelloWorldCallback handler.
1 0.
Press F5 once more. The code will complete and shows a JavaScript alert window with a greeting containing your name, just as it did in Chapter 10.
1 1.
Close your browser, go back to VWD again, and open the file WebServices.aspx by double-clicking it in the Solution Explorer. This opens the original source file, and not the dynamic version you saw in step 6 of this exercise. Locate the HelloWorldCallback handler in the WebServices.aspx file. Note that the breakpoint you set in step 7 has been persisted.
Tracing Your ASP.NET Web Pages ❘ 687
How It Works There are a few interesting points to take away from this exercise. First of all, you should understand the notion of dynamic files, or runtime files. These files are the final result from your ASPX pages and give you insight in the final HTML, CSS, and JavaScript that ends up in the browser. This is a great help, because it gives you a total view of all relevant content. Remember, the final markup displayed in the browser comes from a variety of resources, including master pages, content pages, external CSS and JavaScript files, and from the various server controls that live in your page. The ability to look at the combined result from a single location makes it easy to see how everything fits together. Another important point to remember from this exercise is how the IDE offers you fully integrated debugging features, from the client-side code in the user interface, all the way up into the server. To make it easy to set breakpoints, VWD doesn’t restrict you to adding them in pages at design time only; instead it also enables you to set them in the dynamic runtime files. When you stop debugging, VWD tracks the new breakpoints for you, finds out to what source file they belong, and adds them there again for you, so they are available for the next debugging session. Although you may not realize it because everything is taking place on the same computer and in the same IDE, you are crossing many boundaries when debugging like this. First, VWD enables you to debug client-side script in the browser, so you can hook into that even before any data is sent to the server. When you press F5 in step 8, the code continues and sends the value to the server where it was used in the NameService’s HelloWorld method. Once that server-side web method is done, execution returns to the client again, enabling you to break on the alert statement that shows the message from the web service. For some reason, debugging client-side JavaScript in VWD 2010 doesn’t always work. If you’re encountering issues, remember the debugger keyword. Just add it before the line you want to break on and VWD will halt execution when it encounters this keyword. Don’t forget to remove the debugger keyword again if you’re done with debugging.
Useful as debugging may be during the development of your site, it lacks the capability to investigate the behavior of your site while it’s running in production. Fortunately, ASP.NET has a solution for that as well: tracing.
Tracing Your ASP.NET Web Pages Without tracing, finding out the values of variables, objects, the execution path your code follows, and so on at runtime is problematic at best. You would probably add a Label control to the page, and then write information to it like this: VB.NET
Dim value2 As Double = Convert.ToDouble(ValueBox2.Text) DebugLabel.Text &= “The value of value2 = “ & value2.ToString() & “ ” C#
double value2 = Convert.ToDouble(ValueBox2.Text); DebugLabel.Text += “The value of value2 = “ + value2.ToString() + “ ”;
688 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Although this certainly works, it’s quite cumbersome. First, you need to write a lot of code to make this work. Secondly, you end up with an ugly Label control in your page that you shouldn’t forget to remove when you’re done doing your debugging or tracing. And finally, when you’re ready, you should remove all the code that sets the DebugLabel label. You could take the easy way out by setting the Label control’s Visible property to False, but you would still take the performance hit of assigning the text to the Label control. Tracing in ASP.NET solves all of these problems. It lets your pages, controls, and code write information to a central location, called the trace, which can then be shown in the browser. Tracing is built into the ASP.NET Framework, which means you can use it without any manual coding. Additionally, you can add your own information to the trace. In the following section you see how to use the built-in tracing capabilities, giving you a wealth of information about your page. In a later exercise you see how to add your own information to the trace.
Using the Standard Tracing Capabilities Without much work, you can get a lot of good information about the way your pages execute. All you need to do is enable tracing for your pages. You can do this at the page level or at the application level. With tracing enabled at the page level, you can choose one or more specific pages you want to trace. Application-level tracing is useful if you want to look at multiple pages at the same time. This may help you, for example, to find slow pages in your web site.
Tracing with Individual Pages To enable tracing in a page, you need to set its Trace attribute in the Page directive to True:
When you run a trace-enabled page, you get a long list of details at the bottom of the page. Figure 18-24 shows the ASP.NET Trace for the calculator demo page you have been working with in this chapter. The trace provides a lot of details about your current page. At the top you find a summary of the request details, including the current date and time, the method used to retrieve this page (GET or POST), and the status code (status 200 in Figure 18-24, to indicate success). Below that you see the Trace Information section. The ASP.NET Page class writes to the trace when you enable it. This is similar to the demo page you wrote in Chapter 15 that wrote to a Label control from the numerous events triggered during the page’s life cycle. By default, the data is sorted by time, putting the events in the order in which they occurred. You can also sort them on the category (more on categories in the section that deals with adding your own information to the trace) by changing the TraceMode from SortByTime to SortByCategory:
A little further down the page (not visible in Figure 18-24) you see the control tree, which presents a hierarchical view of the controls in your page and their size.
Tracing Your ASP.NET Web Pages ❘ 689
Figure 18-24
Below the control tree you see the details for a number of important collections, including the Query String, Cookies, Form, Headers, and Server Variables. Additionally, you see information you may have stored in Session or Application state. Being able to see these collections can be a great aid in figuring out a problem. For example, if you have a page that is supposed to read from a cookie, but that crashes as soon as the page loads, you can look at the Cookies collection and see if the page receives the data you expect. These collections are invaluable tools in understanding the execution of your page and can really aid in finding and fixing bugs in your code. Page-level tracing means you need to enable tracing on every page you want to trace. It also means that you need to disable it on every page after you’re done. Because this can be cumbersome in a large site, ASP.NET also enables you to trace the entire application.
Tracing the Entire Web Site You can enable tracing for the entire web site by changing trace settings in the web.config file. You do this by creating a element under <system.web>. The following table lists the most important attributes that the element takes. Attribute
Description
enabled
Determines whether or not tracing is enabled for the application. By default, tracing is disabled, so you need to set this attribute to True explicitly.
traceMode
Determines the order in which items are sorted in the trace output. It works identically to the TraceMode attribute of the Page directive. continues
690 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
(continued) Attribute
Description
requestLimit
Determines the number of trace requests that ASP.NET keeps available.
pageOutput
Specifies whether the trace information is displayed on the page. When set to False (the default) you can only access the tracing information using Trace. axd, which is discussed later.
localOnly
Specifies whether the special Trace.axd handler is accessible from the local host only. From a security point of view, you’re best off to leave this set to True, which means the trace is not available to outside users.
mostRecent
Determines whether old trace records are discarded when the number of trace requests hits the requestLimit. When set to False, tracing is disabled auto‑ matically when the requestLimit is hit.
When you have enabled tracing, you have two ways to read the trace information. When you have set pageOutput to True, the trace information is appended to each page, similar to what you saw with page-level tracing. However, to make tracing less obtrusive, you can disable pageOutput and then request tracing information using a special file called Trace.axd. This is a virtual file, which means you won’t find it in your web site when you go looking for it. However, the ASP.NET runtime knows it should provide you with tracing information when you request this special page. Although the file is virtual, you can still protect it using ASP.NET’s URL security by adding a element to the main web.config file as you’ve done with other file and folders. You see how this works in the following exercise.
Try It Out
Enabling Tracing for the Entire Site
In this Try It Out you see how to enable site-wide tracing. First, you make a few changes to the web. config file. You then browse your site, filling the trace log with your page requests. Finally, you request the special Trace.axd page to see the available trace log information. 1.
Open web.config and locate the opening <system.web> tag. As a direct child of that element, add the following configuration information to enable tracing: <system.web>
This enables the trace, but doesn’t add its output to the page. Instead you need to request the special Trace.axd page to see the trace information. Additionally, you make your system a little more secure by only allowing requests for the trace information from the local machine. Save and close web.config. . 2
Right-click Default.aspx in the Solution Explorer and choose View in Browser.
Tracing Your ASP.NET Web Pages ❘ 691
3.
Click around the site, opening pages, changing the theme, filling in the contact form, and so on.
. 4
After you have requested at least five pages, change the address bar of your browser as follows so it requests the special Trace.axd page: http://localhost:49394/Trace.axd
Your port number may be different, but it’s important that you request the page Trace.axd on localhost. Note that this address assumes you configured the site with / as the Virtual Path as you learned how to do in Chapter 7. If you haven’t done that, select the web site in the Solution Explorer, open its Properties Grid, and set Virtual Path to /. Then restart this exercise at step 2 again. You should get a page similar to Figure 18-25. If the page is empty, press Ctrl+F5 to refresh it.
Figure 18-25
5.
The list of traces is sorted based on time, from oldest to newest. Click the View Details link you see in Figure 18-25 for an ASPX page. You get a page similar to the one shown in Figure 18-24.
How It Works The ability to see trace information for pages that have been requested is extremely valuable. The information can help you understand the flow of information from and to a web page. For example, the trace information for the Contact.aspx page also shows the information that users have entered in the text box controls on the page. To see what this information looks like, click the View Details link for an item you want to zoom in on.
Although the information that ASP.NET traces for you automatically is extremely useful, you’re not limited to just this information. You can also add your own information to the Trace log.
Adding Your Own Information to the Trace Adding your own data to the trace is useful if, for example, you want to see the value of a variable, or want to find out if a specific event fires, and when it fires exactly.
692 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
You can add information to the trace by using the Trace class. This class exposes two useful methods: Write and Warn. Both do pretty much the same thing: they add information to the trace that you can optionally put in a category you can make up yourself. The only difference between Write and Warn is that messages written by Warn appear in red. You could use the Warn method for unexpected situations because the message will draw more attention. In the following exercise you see how simple it is to add your own information to the trace using the Warn and Write methods.
Try It Out
Adding Trace Data to Your Pages
In this Try It Out you add some custom information to the ASP.NET trace. You use the Write method to write out trace information in a normal page execution, and use the Warn method for unexpected scenarios. 1. . 2
Open CalculatorDemo.aspx, switch to its Code Behind, and locate the Click handler for the Calculate button. Right before the Select Case (VB.NET) or switch statement (C#), add the following Trace. Write call: VB.NET
Trace.Write(String.Format(“Performing the calculation with the {0} operator”, OperatorList.SelectedValue)) Select Case OperatorList.SelectedValue C#
Trace.Write(string.Format(“Performing the calculation with the {0} operator”, OperatorList.SelectedValue)); switch (OperatorList.SelectedValue)
3.
Near the bottom of the event handler, modify the Else statement for the check that ensures that both TextBox controls contain a value: VB.NET
Else Result.Text = String.Empty Trace.Warn(“Custom Category”, “TextBox controls are empty; time to add Validation controls?”) End If C#
else { Result.Text = string.Empty; Trace.Warn(“Custom Category”, “TextBox controls are empty; time to add Validation controls?”); }
4.
Enable tracing for this page explicitly. You can do this by setting the Trace attribute of the Page directive in Markup View:
Tracing Your ASP.NET Web Pages ❘ 693
5.
Save all your changes and request the Calculator page in the browser by pressing Ctrl+F5. Enter two numbers and click the Calculate button. Note that your custom information is added to the trace, between the Begin Raise PostBackEvent and End Raise PostBackEvent trace entries. Note also that the Category is missing.
. 6
Clear the text from both TextBox controls in the browser and click the Calculate button again. The trace information should now be easier to spot because of its different color and own category name, as shown in Figure 18-26.
Figure 18-26
7.
Go back to VWD once more and disable tracing. You need to change this at the page level (remove the Trace attribute from the Page directive of the Calculator page) and at the site level in web.config by setting the enabled attribute to false. Save all your changes and request the Calculator page again.
Note that the page still functions correctly, but no longer outputs the trace information.
How It Works The Write and Warn methods of the Trace class enable you to write additional information to the trace. The ASP.NET runtime keeps track of the information and displays it together with the rest of the trace info, either directly at the bottom of the page in the browser with page-level tracing, or through the special Trace.axd page you saw earlier. The Write and Warn methods each have three overloads. The first one (shown only with Write in the previous example) accepts a single string that is displayed in the Message column. The second overload also accepts a category name as demonstrated with the Warn method. The final overload, not shown in the Try It Out exercise, also accepts an Exception object whose message will be added to the trace output. This is useful to trace the information of an exception in a Catch block.
694 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
Tracing and Performance Although it may seem that leaving Warn and Write statements in your code on your production system may hurt performance, this isn’t the case. Because you can disable tracing in the web.config file by setting the enabled property of the trace element to false, you effectively eliminate the performance overhead of tracing.
A Security Warning Tracing can be very useful, but leaving trace information in your production environment can lead to information disclosure. Therefore, you should always either disable tracing by setting its enabled attribute in web.config to false, or at least by setting the localOnly attribute to true. In Chapter 19 you learn a trick that enables you to make this change for all sites on your production server, making it easy to block access to the trace functionality.
Practical Debugging Tips The following list provides some practical tips to help you debug your application: ➤➤
Never leave debug=”true” in the web.config file in a production environment. Always set it to false to improve performance. In Chapter 19 you see an even better solution to ensure this setting is never set to true on a production server.
➤➤
Try to avoid swallowing exceptions in a Catch block. You may be tempted to wrap your code in a Try/Catch block and then leave the entire Catch block empty. Although this certainly avoids exceptions showing up in the user interface, it makes debugging extremely difficult. Because you are no longer aware a problem occurs, you also cannot write code to prevent the error from happening in the first place. The general rule here is: Catch errors that you can handle successfully, for example by displaying a message to the user. If you can’t handle the exception in your code, let it bubble up and log it in the Application_Error event handler so you know that the exception occurred.
➤➤
If you need to re-throw an exception in a Catch block, don’t use Throw ex (throw ex in C#), but use Throw (throw in C#) only. When you use Throw ex, you make it difficult to track the path the code has followed before the exception occurred. Here’s the code showing both options: VB.NET
Try ... Catch ex As Exception ‘ Do something with the error, such as logging it Throw ex ‘ Bad example; you lose track of the source of the exception ‘ Good example; forwards the exception and maintains the call stack Throw End Try C#
try { ... } catch (Exception ex) {
Summary ❘ 695
// Do something with the error, such as logging it throw ex; // Bad example; you lose track of the source of the exception throw; // Good example; forwards the exception and maintains the call stack } ➤➤
Try to avoid exception handling when possible. As you saw in this chapter, it’s much better (and faster) to simply avoid an exception in the first place. For example, the DivideByZeroException exception can easily be avoided by checking for a value of zero before carrying out the division.
➤➤
Be as explicit as possible with the Exception types you catch in Try / Catch blocks. Try to avoid catching generic Exception types and set up multiple, explicit Catch blocks for each specific type you anticipate.
Summary No matter how carefully you program, your site is likely to contain some bugs or throw exceptions at runtime. To minimize these exceptions and build a site that runs as smoothly as possible, you can do a number of things. First of all, you can use exception-handling techniques, where you write code that is able to catch exceptions that you foresee and handle them appropriately. To help you write code with as few bugs as possible, VWD offers you a great set of debugging tools. The ability to break into your code and analyze and change the execution environment from clientside code all the way into the server is a great aid in your bug-slashing adventures. Even if you have debugged your application thoroughly, there’s still a chance your site may have issues in production, whether they are related to performance, logic errors, or other unexpected reasons. In those cases, you can use the ASP.NET tracing facilities that let you track information about running pages. Analyzing this trace information can bring you a long way in fixing the underlying issues. Now that your web site is complete and hopefully bug-free, the next step is to put it online. You see how to deploy your ASP.NET web site in the next chapter.
Exercises 1.
What’s the difference between debugging and tracing?
2.
Imagine you have some code that could potentially throw an exception. For example, you try to send an e‑mail message to a mail server, or write a file to disk while you’re not sure you have the right permissions to do so. What kind of exception-handling strategy would you use to avoid the exception from being displayed in the browser? What code would you need?
3.
You’re taking over a web site that has been built by another developer who had never heard of exception handling. Your client is complaining about the quality of the site and the large number of “Yellow Screens of Death” that users see. Besides analyzing the code for the entire application, what would be a quick solution to get information about the errors and the locations where they occur? And how can you shield the site’s users from the dirty details of the exception messages? Answers to Exercises can be found in Appendix A.
696 ❘ Chapter 18 Exception Handling, Debugging, and Tracing
⊲⊲ What You Learned in This Chapter Breakpoint
A marker you can set in your code to indicate where you want the debugger to halt at runtime
Data tips
Tooltips that present simple or rich data about variables during debugging
Debugging
The process of finding and fixing bugs in your code
Exception
The .NET term for an error that may occur in your code
Exception handling
A methodology to identify and handle errors that occur at runtime
Global.asax
A central file that is used to handle various application-scoped events such as Application_Start, Application_Error and more
Stack trace
A visual representation of the current stack of code calls
Trace
Enables ASP.NET controls and your own custom code to write information to a central log location at runtime
19
Deploying Your Web Site What You Will Learn in This Chapter: ➤➤
How to ease the deployment process through simple changes to your code and configuration
➤➤
How to prepare your site for deployment by creating a copy using Visual Web Developer’s built-in copy tools
➤➤
How to install and configure a web server and your web site on your target machine
➤➤
How to avoid common errors you may get when deploying a site
➤➤
How to copy data stored in your SQL Server 2008 databases to the target server
Congratulations! The fact that you’re reading this chapter probably means you now have a full-featured, database-driven ASP.NET web site that is ready for release into the wild. It’s an exciting time for you and your project. Pretty soon your application will be used and judged by your target audience. To make your web site accessible to users world-wide, you need to publish it to a production server that is connected to the Internet. What kind of server this is and where it is located depends on your own requirements and budget. You can host the site on a home server in your attic with a private Internet connection (as I do with http://imar.spaanjaars.com) or you can host it with an external (and often commercial) party with a direct connection to the Internet backbone. Either way, some work needs to be done to get your site from its development location at C:\BegASPNET\Site to a location where it’s accessible over the Internet.
698 ❘ Chapter 19 Deploying Your Web Site
This chapter deals with a few topics related to successfully deploying your web site. You learn about the process from preparing your site in the development environment to actually running and testing it at your production server. The chapter then ends with a list of things you need to take care of when deploying your site. You can use this checklist to help you make sure you configure your production site in the most secure and optimal way.
Preparing Your Web Site for Deployment When you’re working on the first edition of your web site in a development environment, managing the site and its source code is pretty straightforward. You have only a single version of the site’s source, making it easy to maintain. However, as soon as you put your site in production, you now have two versions of it: one running in the production environment and the one you use for development. This makes it difficult to keep things synchronized. For example, you probably use a different database and connection string in your production environment. You’re also likely to use different e‑mail addresses for the e‑mail that is sent by the site. Finally, you may want to disable sending the error e‑mails from the Global.asax files in a development environment. If you make all of these changes in the code directly when you put your site live, there’s a fair chance that you’ll overwrite some settings during the next update, which can lead to unwanted results. This section shows you how to make managing different versions of the same web site a little easier. You see how to move some of the hardcoded settings, such as e‑mail addresses, to the web.config file. The code in your application then reads these values at runtime. The only difference between your development and production environments is then a single configuration file, making it easy to have different settings in both environments.
Avoiding Hardcoded Settings So far the pages and user controls you have built use some hardcoded settings for things like e‑mail addresses. For example, ContactForm.ascx, the user control that sends out an e‑mail, uses the following code to set the recipient and sender information: VB.NET
Hardcoding settings in this manner makes it difficult to give them different values in different environments. Every time you want to roll out your site to production, you need to make sure you’re not accidentally overwriting settings you changed for the production environment. Fortunately, ASP.NET comes with a great solution to avoid these kinds of problems: the web.config file, expression syntax, and the WebConfigurationManager class you use to read from web.config.
Preparing Your Web Site for Deployment ❘ 699
The web.config File You’ve used the web.config file a number of times in this book to store information about connection strings, membership, roles and profile information, and more. What you haven’t seen yet is the element that enables you to store data in a key/value pair using elements. The element enables you to store simple information, such as an e‑mail address, and retrieve that value by its key. For example, to store an e‑mail address, you can add the following to the web.config file:
The element is placed outside the <system.web> element in the web.config file, yet still within the parent element. Obviously, you need a way to access the data in at runtime. You can do this in a couple of ways, including expression syntax and the WebConfigurationManager class, both of which are discussed next.
Expression Syntax Expression syntax enables you to bind control properties to resources, such as those found in the element in web.config, connection strings, localization resource files and various routing settings used in URL rewrite scenarios. To display data from the element, you use the following syntax where AppSettingKeyName refers to a key you define in web.config:
For example, to display a copyright notice on your pages in a Literal control, you can add the following setting to web.config:
You can then display this text in a Literal control like this:
To make it even easier to set properties like Text as in the preceding example, VWD comes with the Expression Editor. To access this dialog box, select a control in Design or Markup View, open its Properties Grid, and click the ellipsis for the (Expressions) item, shown in Figure 19-1. You may find that the (Expressions) item does not always show up when in Markup View. If that’s the case, switch to Split View or Design View first. Figure 19-1
700 ❘ Chapter 19 Deploying Your Web Site
The Expressions dialog box opens, enabling you to bind control properties to expressions. VWD limits the list of properties of the control to those that can be bound using an expression. To bind the Text property of the Literal control to an application setting, first click Text on the left side of the dialog box, choose AppSettings from the Expression Type drop-down list on the right, and finally, choose the right AppSetting from the drop-down list in the Expression Properties section. Figure 19-2 shows the complete Expressions dialog box for a Literal control used to display the copyright text.
Figure 19-2
When you click OK, VWD modifies the Text property of the Literal so it contains a reference to the correct application setting. Getting values from the web.config using expression syntax is useful, but may not cover all your needs. Therefore, it’s good to know you can retrieve the values programmatically as well. To do this, you can use the WebConfigurationManager class.
The WebConfigurationManager Class The WebConfigurationManager class from the System.Web.Configuration namespace provides access to data that is stored in configuration files. It has special support for the appSettings and connectionStrings elements of the web.config file, enabling you to retrieve data from those sections with a single line of code. The following snippet shows you how to retrieve the FromAddress value you saw earlier from the appSettings element: VB.NET
Imports System.Web.Configuration ... Dim fromAddress As String = WebConfigurationManager.AppSettings.Get(“FromAddress”) C#
using System.Web.Configuration; ... string fromAddress = WebConfigurationManager.AppSettings.Get(“FromAddress”);
Preparing Your Web Site for Deployment ❘ 701
The Get method always returns data as a string, so you’ll need to convert it to a proper type if you’re expecting anything other than a string. For example, if you have stored a Boolean value in web.config like this:
you need to use the following code to retrieve and convert the value: VB.NET
Dim sendMail As Boolean = Convert.ToBoolean(WebConfigurationManager.AppSettings.Get(“SendMailOnError”)) C#
Although you can access the WebConfigurationManager class in the Code Behind of your Web Forms and user controls directly (provided you have imported the System.Web.Configuration namespace), I prefer to create static, read-only properties in a custom configuration class that accesses the web.config file to get the values. You see how to do this in the following exercise.
Try It Out
Moving Application Settings to web.config
In this Try It Out you create a class with a few properties that get their values from the web.config file. You then use the properties of this class in your code to replace the hardcoded values that were used earlier. 1.
Inside the App_Code folder, create a new class file and call it AppConfiguration.vb or AppConfiguration.cs. In C#, remove the constructor code, shown in the following code block: public AppConfiguration() { // // TODO: Add constructor logic here // }
Because the class is going to have static properties exclusively, you don’t need the constructor. . 2
At the top of the class file, add an Imports/using statement for the System.Web.Configuration namespace: VB.NET
Imports System.Web.Configuration C#
using System.Web.Configuration;
3.
Add a new Shared (static in C#), read-only property to this class that returns the FromAddress from the web.config file. Recall from Chapter 5 that a Shared/static member (like a method or a property) operates on the class itself, and not on an instance of that class.
702 ❘ Chapter 19 Deploying Your Web Site
VB.NET
Public Class AppConfiguration Public Shared ReadOnly Property FromAddress() As String Get Dim result As String = WebConfigurationManager.AppSettings.Get(“FromAddress”) If Not String.IsNullOrEmpty(result) Then Return result End If Throw New Exception(“AppSetting FromAddress not found in web.config file.”) End Get End Property End Class C#
public class AppConfiguration { public static string FromAddress { get { string result = WebConfigurationManager.AppSettings.Get(“FromAddress”); if (!string.IsNullOrEmpty(result)) { return result; } throw new Exception(“AppSetting FromAddress not found in web.config file.”); } } }
4.
Repeat the previous step, but this time create the following three properties by creating a copy of FromAddress: ➤➤
FromName
➤➤
ToAddress
➤➤
ToName
Don’t forget to rename all three occurrences of FromAddress into the new property name. 5.
Still inside the AppConfiguration class, create a Boolean property called SendMailOnError: VB.NET
Public Shared ReadOnly Property SendMailOnError() As Boolean Get Dim result As String = WebConfigurationManager.AppSettings.Get(“SendMailOnError”) If Not String.IsNullOrEmpty(result) Then Return Convert.ToBoolean(result) End If
Preparing Your Web Site for Deployment ❘ 703
Throw New Exception( “AppSetting SendMailOnError not found in web.config file.”) End Get End Property C#
public static bool SendMailOnError { get { string result = WebConfigurationManager.AppSettings.Get(“SendMailOnError”); if (!string.IsNullOrEmpty(result)) { return Convert.ToBoolean(result); } throw new Exception( “AppSetting SendMailOnError not found in web.config file.”); } }
6.
When you’re ready with the five properties, save and close the AppConfiguration file.
. 7
Open the Code Behind of ContactForm.ascx in the Controls folder and locate the code that sets the From and To addresses and replace the hardcoded values with their AppConfiguration counterparts: VB.NET
myMessage.From = New MailAddress(AppConfiguration.FromAddress, AppConfiguration.FromName) myMessage.To.Add(New MailAddress(AppConfiguration.ToAddress, AppConfiguration.ToName)) C#
myMessage.From = new MailAddress(AppConfiguration.FromAddress, AppConfiguration.FromName); myMessage.To.Add(new MailAddress(AppConfiguration.ToAddress, AppConfiguration.ToName));
Notice how IntelliSense helps you pick the correct property of your AppConfiguration class. 8.
Save your changes and close the file.
. 9
Open the Global.asax file and wrap the entire code in Application_Error in an If check that ensures that SendMailOnError is set to True. Additionally, change the hardcoded e‑mail addresses to use the ToAddress and ToName from the AppConfiguration class instead: VB.NET
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) If AppConfiguration.SendMailOnError Then If HttpContext.Current.Server.GetLastError() IsNot Nothing Then ... Dim myMessage As MailMessage = New MailMessage(AppConfiguration.FromAddress, AppConfiguration.ToAddress, mailSubject, message) ...
704 ❘ Chapter 19 Deploying Your Web Site
End If End If End Sub C#
void Application_Error(object sender, EventArgs e) { if (AppConfiguration.SendMailOnError) { if (HttpContext.Current.Server.GetLastError() != null) { ... MailMessage myMessage = new MailMessage(AppConfiguration.FromAddress, AppConfiguration.ToAddress, mailSubject, message); ... } } }
10. Open web.config and add the following element as a direct child of the main element. Change the e‑mail address for FromAddress and ToAddress to your own: ...
11. Save all your changes and press Ctrl+F5 to open the home page in your browser. Go to the Contact page and fill in the contact form. You should receive an e‑mail at the address you specified in step 10.
1 2.
Request a nonexistent page in your browser. For example, change the page name in the address in the browser’s address bar to DefaultTest.aspx. You should receive a “File Not Found” message and an e‑mail with the exception details, just as in the preceding chapter.
1 3.
Go back to Visual Web Developer, open web.config, and change the setting for SendMailOnError from true to false:
14. Save your changes, and again request a page that doesn’t exist. Because you changed the SendMailOnError setting, you shouldn’t get an e‑mail with the exception details.
Preparing Your Web Site for Deployment ❘ 705
How It Works The properties of the AppConfiguration class look in the web.config file for the requested application settings. When the setting is not defined or does not contain a value, each property throws an exception. This is useful to detect missing application settings at an early stage. Instead of silently returning an empty value, you now get an exception that reminds you to add the required application setting. At runtime, the code accesses these properties like this: VB.NET
myMessage.From = New MailAddress(AppConfiguration.FromAddress, AppConfiguration.FromName) C#
myMessage.From = new MailAddress(AppConfiguration.FromAddress, AppConfiguration.FromName);
Because the properties have been defined as Shared (static in C#), you can access them directly on the AppConfiguration class, without the need to create a new instance of AppConfiguration first. Although you could access the element in web.config directly in the code (for example, you could use WebConfigurationManager.AppSettings.Get(“FromAddress”) to get the e‑mail address in ContactForm.ascx directly), it’s better to wrap the elements in shared properties in their own class. This solution gives you IntelliSense on the AppConfiguration class, making it easy to see what configuration properties are available. It also enables you to write centralized code that throws exceptions when the required application settings cannot be found or that supplies sensible defaults. Notice how the properties only throw an exception when a valid value cannot be returned. If you access web.config directly in your own code, you would need to check for valid values every time you access a setting. The same principle is used for the SendMailOnError setting. When an exception occurs at runtime, the code in Application_Error now consults the SendMailOnError property. This property in turn checks the element of web.config to determine if an error message should be e‑mailed. Because the SendMailOnError property is a Boolean, the code uses Convert.ToBoolean to convert the string returned from the web.config file into a Boolean. By storing values in web.config instead of hardcoding them, your site becomes easier to maintain and deploy. When you go live, all you need to do is create a copy of web.config for your production environment and change a few settings.
With the hardcoded application settings moved to the central web.config file, the next step in the deployment process is creating a copy of your web site.
706 ❘ Chapter 19 Deploying Your Web Site
Copying Your Web Site During development of your site you use the built-in web server that ships with Visual Web Developer. Although this server is great for local development, you can’t use it in a production environment, because it only listens to requests coming from localhost. To put your site in production, you need to deploy it to a machine that runs IIS (Internet Information Services), Microsoft’s professional web server. In this section you see how to prepare your site so it can be run under IIS. Later in this chapter you see how to install and configure IIS. To deploy your web site to a production server, the deployment targets shown in the following table are available, right from within VWD. Deployment Option
Description
File system
Enables you to create a copy of the site on the local file system of your development machine or a networked machine. This option is useful if you want to move the files manually to your production server later.
Local IIS
Enables you to create a copy of your site that will run under your local IIS installation.
FTP Site
Enables you to send the files that make up your web application to a remote server directly using FTP.
Remote Site
Enables you to send the files that make up your web application to a remote IIS server. For this option to work, the remote server needs to have the Front Page Server Extensions installed. Check out the documentation that comes with IIS or consult the administrator of your remote server for more help with this option.
If you are using a commercial version of Visual Studio, you can access these four deployment options from the two main ways of deployment that VWD offers: Copy Web Site and Publish Web Site. If you’re using the free Express edition, you can only use Copy Web Site.
NOTE At the very beginning of this book you learned about the differences between Web Site Projects (WSP) and Web Application Projects (WAP). For this book, I have chosen to use the WSP model because it’s easier to work with for beginners and supports a few features not available to WAPs (such as Profile and the dynamic App_Code folder). Unfortunately, there is one major feature that WAP supports and WSP doesn’t: web packaging. This is a mechanism to create a setup package of your entire web site so it can easily be deployed to a production server, taking the differences in configuration between the development and production machines into account. For more information about Web Packaging in WAPs, check out the list of links on deployment at the VWD Team blog via http://tinyurl.com/WebDeploymentOverview.
Copying Your Web Site ❘ 707
Creating a Simple Copy of Your Web Site The Copy Web Site command simply creates a copy of your site using any of the four transportation options. This is a great way to quickly copy your site to another location, including your production server or even to a portable media device like a USB stick that you can take with you. You can access the Copy Web Site option from the Solution Explorer’s toolbar shown in Figure 19-3 or from the main Website menu.
Nest Related Files Refresh Copy Web Site Properties ASP.NET Configuration
Figure 19-3
Before you create a copy of your web site, it’s a good idea to check the state of your web site. You should do a full recompile where VWD compiles all the code and pages in your web site. This helps you detect any problems in your web site before you deploy it to a production environment. Deploying a site is also a good moment to do some housekeeping. To avoid slowing down the deployment process and to keep your site as lean and clean as possible, you should delete the files from your web site that you don’t need in production. In the next exercise you see how to use the Copy Web Site command to create a copy of the entire Planet Wrox project. In a later exercise you use this copy again when you configure IIS for your production web site.
Try It Out
Using the Copy Web Site Option
In this Try It Out you use the Copy Web Site option together with the Local File System option to create a copy of the site. The other three transportation options (FTP, Local IIS, and Remote IIS) work similarly. The biggest difference with these options is that they ask you for details about the destination, such as a username and password, or the IIS web site you want to use. With the copy you create in this exercise, you can manually configure an IIS web site, something you see how to do later in this chapter. 1.
Close all open files in Visual Web Developer and then choose Build ➪ Rebuild Web Site from the main menu. Doing so forces VWD to recompile the entire site even if it already had compiled some parts of it. VWD lists any problems your site may have in the Error List. To verify your site is error free, open the Error List (choose View ➪ Error List from the main menu) and make sure you don’t have any compile errors. Fix any errors you may have in your site. If you’re using C# you’ll get a warning about unreachable code in ContactForm.ascx. You can either ignore the warning or remove the reference to the jQuery file that you added there to enable IntelliSense.
. 2
When the Error List is empty, choose Website ➪ Copy Web Site or click the Copy Web Site icon on the Solution Explorer’s toolbar shown in Figure 19-3.
. 3
At the top of the dialog box, click the Connect button to bring up a dialog box that lets you choose the destination location for your site. At the left side of the dialog box, make sure that File System is selected. Then on the right side, locate the folder C:\BegASPNET, click it once to select it, and then click the Create New Folder button; the button with the yellow folder icon at the top-right corner of the dialog box. Type Release and press Enter to apply the new name. Figure 19-4 shows the final dialog box.
708 ❘ Chapter 19 Deploying Your Web Site
Figure 19-4
Finally, click Open to choose C:\BegASPNET\Release as the destination location for your copy operation. . 4
In the Copy Web Site dialog box, put focus on the list at the left by clicking a file or folder and then press Ctrl+A to select all files in the Source Web Site list.
. 5
Click the Synchronize Selected Files button (the one with the two arrows facing opposite directions) between the two lists, visible in Figure 19-5. This starts the synchronization process. Because the folder displayed on the right side is empty, all files from the left list are copied to the right. When copying is complete, your dialog box should look like Figure 19-5.
Figure 19-5
Copying Your Web Site ❘ 709
6.
Next, open a Windows Explorer window and browse to C:\BegASPNET\Release. Verify that all relevant files that make up your site are there.
How It Works The Copy Web Site option simply creates a copy of all files that make up your site. It can create a copy of the site at different locations, including the local file system, an FTP server, and an IIS server. In this exercise, you created a copy at a local hard drive. The first two buttons with blue arrows between the two file lists in Figure 19-5 enable you to copy files from the source to the remote location or vice versa. The third button, with the two arrows, enables you to synchronize, rather than just copy over the files. When you create a copy on your local system this may not seem like a big deal, but when you’re creating the copy over a slow FTP connection, you’ll be glad this tool only uploads new and changed files, and leaves unmodified files untouched. In this exercise you opted to copy the files to the local system. This is a great way to create a copy that is detached from the development environment that can be run on your local machine. You can, of course copy the same set of files to another machine using an FTP program, a USB stick, and so on. Also, if you host your site externally, your hosting provider may offer a web-based interface to upload these files to its server. The detached local copy enables you to make modifications to a few files first (like web.config) and then upload everything to your host.
Besides copying a web site, the commercial versions of Visual Studio also support publishing your web site.
Publishing Your Web Site The Publish Web Site command, only available in the commercial versions of Visual Studio and not in the Express editions, is similar to the Copy Web Site option in that it creates a copy of the web site you can use for deployment. However, it’s different in that it allows you to precompile the application, which means all the code in the Code Behind of your ASPX pages, controls, code files in App_Code, and so on are compiled into .NET assemblies; files with a .dll extension in the bin folder of your site. The main benefits of precompiling are source protection (others with access to the server can’t look into your source) and an increased performance the very first time a page is requested. Pages that are not precompiled are compiled on the fly when they are requested the first time, which takes a little bit of time. Remember that regular visitors to your site will never be able to see the source of your application. All they’ll be able to see is the final HTML that gets sent to the browser. The Publish Web Site command is available from the Build menu and brings up the dialog box shown in Figure 19-6.
710 ❘ Chapter 19 Deploying Your Web Site
Figure 19-6
Clicking the ellipsis at the end of the target location brings up the same dialog box shown in Figure 19-4, enabling you to choose a target location. When the option Allow This Precompiled Site to Be Updateable is selected, VWD compiles all your VB.NET and C# code into .NET assemblies and places them in a bin folder that it creates at the root of your site. It leaves the markup in ASPX and ASCX files alone. However, with this option turned off, all the markup code is compiled into .dll files as well. The actual files still need to be deployed to the server, but their content has been replaced with placeholder text: “This is a marker file generated by the precompilation tool, and should not be deleted!” When the page is requested by a browser, the ASP.NET runtime finds the appropriate content in the assemblies in the bin folder and serves its content as if it were a normal page. This latter option is especially great if you want to prevent other people with access to your server from altering your site after it has been deployed. Because all the source and markup is compiled into .dll files, there is no way to change it on the server anymore, other than uploading a new set of published files. Copying or publishing your web site to a new folder on your local system is only one step of the deployment process. The next part is configuring the web server so it knows where to look for your files.
Running Your Site under IIS Up until now, you’ve been using the built-in web server that ships with Visual Web Developer to debug and test your application. However, because requests to this server are limited to those coming from the localhost to minimize security implications, you’ll need to use IIS, which comes with most major Windows versions. In order to have your web site run under IIS, you need to perform the following steps:
1. 2. 3.
Install and configure IIS. Install and configure the .NET Framework 4. Configure security settings.
Running Your Site under IIS ❘ 711
Depending on the current state of your system, some of these actions are optional. In the following sections you see how to carry out these steps.
NOTE Installing and configuring a web server can be a complex task. There are many factors to take into account, including your operating system, its configuration, the account you use to log on to your machine, the final SQL Server you’re going to use and more. Don’t panic if you get stuck. Instead, visit the IIS Web site at www.iis.net for detailed walkthroughs or come over to this book’s own discussion forum at http://p2p.wrox.com where you’ll find fellow programmers (including me) that will help you succeed.
Installing and Configuring the Web Server Although IIS ships with most Windows versions, it’s not installed by default, so you need to install it first. You also need to make sure that your version of Windows supports IIS. Although the Starter and Home Basic versions of Windows Vista and Windows 7 ship with some parts of IIS, you can’t run ASP.NET pages on them, so you need at least the Home Premium edition. On the server-based versions of Windows, IIS is fully supported.
NOTE Even though IIS is supported on consumer versions of Windows such as Windows Vista and Windows 7 it doesn’t mean these operating systems are the best choices for hosting your web site. You typically use these versions of Windows for local development and testing, whereas the server versions of Windows (Windows Server 2003 and Windows Server 2008) are used for hosting production web sites.
To install and configure IIS on your Windows machine, you need to be logged on as an Administrator. If the account you use to log on to your machine does not have administrative privileges, you need to ask your administrator to install and configure IIS for you. In addition to installing IIS, you’ll also see how to create and configure the web site in IIS. Because of the way security works under Windows, your site probably won’t work immediately after you configure IIS unless you change some of the security settings under Windows. You see how to do this in the section “Understanding Security in IIS” and the Try It Out entitled “Configuring the File System.” You’ll be able to test out your IIS settings more easily if you already have SQL Server 2008 Express installed. This is the case if you follow along the following exercises on the same machine that is running VWD. Otherwise, you can download the free Express edition of SQL Server using the Web Platform Installer, available from www.microsoft.com/web. If you have a different version of SQL Server, or have SQL Server on a remote machine, pay special attention to the section “Moving Data to a Remote Server” in this chapter and to Appendix B.
712 ❘ Chapter 19 Deploying Your Web Site
Making Sure IIS Is Installed In this section, you see how to make sure that IIS is installed on recent Windows versions: Windows Vista, Windows 7 and Windows Server 2008 (the first edition and the recent R2 release). The procedure to check this and to install IIS is different for each version of Windows, so the following sections guide you through the different options. You should pick the one that matches your operating system. For a lot more information about IIS, including help with installing IIS on versions of Windows not discussed here, check out the official IIS web site at www.iis.net.
NOTE Microsoft has created a tool called the Web Platform Installer (WPI) which enables you to install IIS, SQL Server, VWD 2010 and much more using a single installer. It’s a great tool to quickly set up a new development or production server, with all the tools and services you need for ASP.NET development and deployment. You can find out more about this tool here: www.microsoft .com/web. If you use WPI you can skip most of the following instructions on installing IIS and ASP.NET. Just make sure you install at least IIS, ASP.NET and SQL Server 2008. To better understand what steps are involved in installing and configuring your operating system, you can follow the individual steps described in the following sections. Once you have some experience with this process, you can use the WPI for new installations.
Windows Vista and Windows 7 You install IIS through the Programs and Features section, which you can access through the Control Panel or by clicking Start, typing appwiz.cpl in the Search box, and pressing Enter. When you’re in the Programs and Features screen, click the link Turn Windows Features On Or Off to bring up the Windows Features dialog box, shown in Figure 19-7. Click the Internet Information Services item to select it. This also selects some of its required subfeatures. Then expand Internet Information Services ➪ World Wide Web Services ➪ Application Development Features and select at least ASP.NET. This causes some of the other Development features to be selected as well. Refer to the IIS documentation for help on the other, optional components.
Figure 19-7
Finally, click OK and Windows installs the requested features for you. You can proceed with configuring ASP.NET in the section “Installing and Configuring ASP.NET.”
Windows Server 2008 / Windows Server 2008 R2 You install IIS through the Programs and Features section, accessible through the Control Panel or by clicking Start and typing appwiz.cpl in the Search box. When you’re in the Programs and
Running Your Site under IIS ❘ 713
Features screen, click the link Turn Windows Features On Or Off to bring up the Server Manager, shown in Figure 19-8.
Figure 19-8
Right-click the Roles item in the tree on the left and choose Add Roles (you may need to wait until the Roles panel is finished loading). Follow the on-screen instructions and install the role Web Server (IIS). When you get to the screen where you can choose the various Role Services, make sure you at least select ASP.NET under the Application Development node, as shown in Figure 19-9. This in turn selects a number of other features that are required by ASP.NET.
Figure 19-9
714 ❘ Chapter 19 Deploying Your Web Site
Continue the Roles Wizard by clicking Next until Windows starts installing the required files for the selected roles. When everything is ready, you may need to reboot your computer. When you log back in, IIS is ready for use. When IIS is installed successfully, you need to make sure you have the Microsoft .NET Framework version 4 installed.
Installing and Configuring ASP.NET If you installed Visual Web Developer 2010 (any edition) on your target machine, you already have the .NET Framework 4 installed. Otherwise, you need to download it from the Microsoft site at http://msdn.microsoft.com/en-us/netframework. Follow the Download or Install link or use the search option and search for “download .NET framework 4.” Make sure you download the full version of the .NET 4 Framework and not an earlier version or the Client Profile package. After you have downloaded the .NET Framework, run the installer and follow the on-screen instructions. If you already had the .NET Framework 4 on your machine and installed IIS afterward, you need to tell IIS about the existence of the framework. Normally, this is done during installation of the .NET Framework. If you installed IIS later, you need to do this manually. To register ASP.NET in IIS follow these steps:
1.
Open a command prompt in Administrative mode. To do this, click Start, type cmd in the search box, and press Ctrl+Shift+Enter to start the command prompt with elevated permissions. When you confirm the action, the command prompt will open normally.
2.
Navigate to the .NET Framework version 4 folder by entering the following command and pressing enter: cd \Windows\Microsoft.NET\Framework\v4.0.30128
Note that the actual version number following v4.0 may be slightly different on your machine. Also, if you are using a 64 bit version of Windows, the Framework folder is called Framework64. Use Windows Explorer to find out the correct folder before you enter it at the command prompt.
3.
Type aspnet_regiis -i and press Enter again. After a while you should get a message that ASP.NET 4 was registered with IIS successfully.
Now that IIS and the .NET Framework have been installed and configured correctly, the next step is to configure your web site under IIS. You see how to do this in the next Try It Out exercise. After the Try It Out, you learn more about configuring security permissions for your system.
Try It Out
Configuring a Site with Windows Vista, Windows 7, or Server 2008
In this exercise you see how to configure the standard “Default Web Site” that ships with IIS. Although it’s possible to create more than one site under IIS on Windows Vista, Windows 7, and Windows Server 2008, this option is not discussed here. Contact your system administrator or read the help that comes with IIS to learn more about creating multiple web sites under IIS. Most of the steps in Windows 7 are identical to those in Windows Vista and Windows Server 2008. However, the screens you see in the
Running Your Site under IIS ❘ 715
following exercise are taken in Windows 7 and are slightly different on the other operating systems. If you’re doing this exercise on a machine other than the one you used to build the Planet Wrox site on, be sure to copy the BegASPNET folder to the root of the C drive of the target machine. Also make sure this machine has access to SQL Server 2008, installed either locally or on another remote machine. 1.
Open the Internet Information Services (IIS) Manager. You find this item in the Administrative Tools section of the Control Panel which you can access through its System and Security category (called System and Maintenance on Windows Vista and the first release of Server 2008). Alternatively, click Start, type inetmgr in the Search box, and press Enter.
2.
Expand the tree on the left until you see Application Pools and the Default Web Site, as shown in Figure 19-10.
Figure 19-10
3.
Click the Application Pools item and confirm you have an entry called ASP.NET v4.0 that uses v4.0 as the .NET Framework Version and that has its Managed Pipeline Mode set to Integrated. If you don’t have this item, click Add Application Pool in the Actions panel on the right and create a new application pool called ASP.NET v4.0 using the .NET Framework version 4.0 with Integrated as the Managed Pipeline mode.
. 4
Select the ASP.NET v4.0 application pool (whether it was already there or not) and click Advanced Settings. Locate the property called Identity and ensure it is set to Network Service (Windows Vista and the initial release of Windows Server 2008) or ApplicationPoolIdentity (Windows 7 and Windows Server 2008 R2.) If it’s not, choose the correct item from the list of options. You use this identity later when configuring security. In the same dialog, set the Load User Profile option to True if you’re using Windows 7 or Server 2008. Click OK to close the Advanced Settings dialog.
. 5
Click the Default Web Site item to select it and click Advanced Settings in the Actions pane on the right of Figure 19-10.
716 ❘ Chapter 19 Deploying Your Web Site
6.
In the Advanced Settings dialog box, click the Physical Path property, click the ellipsis to open up a folder browser, select the folder C:\BegASPNET\Release, and click OK to confirm the path.
7.
In the same dialog, click Application Pool, then click the ellipses, choose the Application Pool from step 3, labeled ASP.NET v4.0 and click OK. Click OK again to close the Advanced Settings dialog box.
. 8
Next you need to make sure that IIS is configured to use a sensible default document; the document that is served when you request a folder name or the root of the site. The Planet Wrox site uses Default.aspx, which is the most common default document name for ASP.NET web sites. To check this, double-click the Default Document option in the IIS Features list (below the items you see in Figure 19-10). Then make sure that Default.aspx is present and at the beginning of the list. If the item is not there, add it manually. For Windows Vista, simply type the document name in the File Name(s) list and click Apply. For Windows 7 or Server 2008, click the Add link in the Actions panel to add it and use the Move Up links to move it to the top of the list. Your dialog should look similar to Figure 19-11 (if Default.aspx was already in the list, its Entry Type is set to Inherited instead of Local).
Figure 19-11
9.
You can now close the Internet Information Services Manager, because the site is configured correctly as far as IIS is concerned. However, it still won’t run correctly because you need to configure security permissions on the file system, as you see later.
How It Works Each new IIS installation has a Default Web Site, the site that listens to http://localhost by default. In this exercise, you configured this default web site to run Planet Wrox, but you can also create a whole new site that can run side by side with other web sites. You pointed the root of the site to the Release folder that contains your web site. With that mapping set up, IIS is able to see what files to serve when you request a URL like http://localhost. It means that a URL like http://localhost/Login.aspx is mapped to the physical file at C:\BegASPNET\Release\Login.aspx. You also assigned the web site
Running Your Site under IIS ❘ 717
an application pool — an IIS mechanism to isolate and configure one or more IIS web sites in one fell swoop. Two web sites running in different application pools do not affect each other in case of a problem such as a crash. In this exercise you selected an application pool that uses the .NET 4 Framework and uses the Integrated Pipeline mode. In this mode, IIS and ASP.NET are tightly integrated which means you can use ASP.NET features (such as Forms Authentication you saw in Chapter 16) in standard IIS functionality such as serving static files. For more information about this mode, check out the official IIS web site via http://tinyurl.com/IntegratedPipelineMode. At the end of the exercise you configured a default document, the file that is served when you request a URL without an explicit file name, like http://localhost/ or http://localhost/Reviews/. By configuring Default.aspx as the default document, IIS will try to find and serve a file by that name.
The final thing you need to do to make sure your site runs on your local IIS installation is configure the security settings. This is discussed in the following two sections.
Understanding Security in IIS Because of the seamless integration of the built-in web server in Visual Web Developer 2010, you may not realize what happens under the hood, and what security settings are in effect when you browse pages in your site. In order to use resources in your site, such as ASPX files, Code Behind files, the database in the App_Data folder, and the images in your site, your web server needs permissions from Windows to access those resources. This means that you need to configure Windows and grant access to those resources to the account that the web server uses. But what exactly is that account? The specific account that needs permission depends on a number of factors, including the version of Windows, whether you run your site under IIS or with the built-in web server, and on a number of settings within IIS. In most cases, however, there are only two scenarios to consider: using the built-in web server or using IIS as your web server. In the former case, the account that the built-in web server uses is the account you use to log on to your Windows machine. This account is usually something like DomainName\UserName or MachineName\UserName. Logged in with this account on Windows, you start up VWD 2010, which in turn starts up the built-in web server. This means that the entire web server runs with your credentials. Because it’s likely you’re an Administrator or a power user on your local Windows machine and have permissions to access all files that make up your site, things probably worked fine so far without any changes to the security settings. In the latter case, where IIS is used, things are quite different. By default, an ASP.NET application under IIS runs with a special account created when you installed IIS. This account is called Network Service on Windows Vista, and the original release of Windows Server 2008 and it is called ApplicationPoolIdentity on Windows 7 and Windows Server 2008 R2. You won’t find the ApplicationPoolIdentity user account on your system directly, as it depends on the name of the configured application pool.
718 ❘ Chapter 19 Deploying Your Web Site
Because the Application Pool you saw earlier runs in Integrated Pipeline mode, you only need to configure a single user account. If you are running in Classic Mode (which isn’t necessary for the PlanetWrox web site) you also need to configure another account called IUSR. This account is used by IIS to serve non-ASP.NET content such as HTML files and images. Consult the IIS documentation for more information about Classic Mode and the IUSR account. After you have determined the account that you need to configure, the final step is to configure the file system.
NTFS Settings for Planet Wrox Regardless of the account you are using, you’ll need to make changes to the Windows file system, so the web server is allowed to access your resources. This is only necessary when your hard drive is formatted using NTFS and not with FAT or FAT32, the older Microsoft file systems. You see how to determine the type of your drive in an exercise later in this section. To successfully configure your NTFS file system for the Planet Wrox web site, you need to grant the following permissions to the web server accounts that you determined in the previous section: Folder Name
Permissions
Explanation
Release
List folder contents Read
The web server account needs to be able to read all the files and folders that make up the web site. Child folders, like Reviews, need to be set up to inherit these settings.
Modify List folder contents Read Write
The web server account needs to be able to read from and write to the Microsoft SQL Server 2008 databases in the App_Data folder. It also needs to be able to save the uploaded images in the GigPics folder.
(Located at C:\BegASPNET\)
App_Data GigPics
(both located at C:\BegASPNET\)
If you came here from Chapter 12 to learn how to configure NTFS for the App_Data folder you can ignore the Release folder that was created earlier in this chapter. Instead, grant Modify permissions for your own account to the App_Data folder of your site as per the instructions in the next exercise. In the following exercise you learn how to configure the security settings for these folders.
Try It Out
Configuring the File System
In this Try It Out you see how to configure the file system for the Planet Wrox web site. The exercise shows you screenshots from Windows 7, but the other flavors of Windows have similar screens. Search Windows help for “security NTFS” or contact your administrator if you’re having problems carrying out the following steps. 1.
Start by opening a Windows Explorer and then locate your C drive. Right-click it and choose Properties. Verify that under File System you have NTFS. If you see the older FAT or FAT32
Running Your Site under IIS ❘ 719
file systems you can skip this entire Try It Out, because FAT doesn’t support changing security settings. Close the Properties dialog. . 2
In the same Windows Explorer window, browse to the Release folder at C:\BegASPNET\Release, visible in Figure 19-12.
Figure 19-12
3.
Right-click the Release folder, choose Properties, and switch to the Security tab (see Figure 19-13).
. 4
By default, the Release folder inherits its settings from its parent folder (C:\BegASPNET), which in turn gets its settings from the root of your C drive. To break this inheritance chain, click the Advanced button to open the Advanced Security Settings dialog box. Then for Windows 7 and Windows Server 2008 R2 click the Change Permissions button and for Windows Vista and Windows Server 2008, click the Edit button on the Advanced Security Settings dialog to turn this screen into Edit mode. You should get a screen similar to Figure 19-14.
Figure 19-13
Figure 19-14
720 ❘ Chapter 19 Deploying Your Web Site
5.
Clear the Include Inheritable Permissions From This Object’s Parent option and click OK. You’ll get a dialog box that asks you if you want to Add (Windows 7 and Server 2008 R2) or Copy (Vista and Server 2008) or Remove the existing settings. Choose Add or Copy and then close all dialog boxes until you’re back in the Release Properties dialog box visible in Figure 19-13.
. 6
The next step is adding accounts. Click the Edit button visible in Figure 19-13, and then click the Add button. Type the name of the web server account which depends on the version of Windows you are using. If your account is the ApplicationPoolIdentity, enter IIS AppPool\ASP.NET v4.0 as the account name. Otherwise, enter Network Service. Click OK to add the account. With the account selected in the Group or User Names list, ensure that only List Folder Contents and Read are selected. Your dialog box should end up similar to Figure 19-15.
. 7
Close all open dialog boxes except for the Release Properties dialog box shown in Figure 19-13.
. 8
Click the Advanced button to open the Advanced Security Settings dialog box again. For Windows 7 and Server 2008 R2, click the Change Permissions button and for Windows Vista or Windows Server 2008, click the Edit button in the Advanced Security Settings dialog box to put it in Edit mode. Select the second check box you see in Figure 19-14. This forces Windows to apply the same security settings to all sub files and folders, replacing all existing settings. Click OK and then confirm the changes that will be made. Finally, close all remaining open dialog boxes.
Figure 19-15
. 9
Back in Windows Explorer, right-click App_Data from the Release folder, open its Properties dialog and then its Security tab, and edit the permissions for the web server account you added in step 6 by adding Modify permissions (Write, Read & Execute get selected automatically as well). You need to click the Edit button first to bring the Properties dialog box in editable mode. Repeat this step for the GigPics folder.
1 0.
If you are using IIS on the same machine that has SQL Server 2008 Express, you’re pretty much done now and your web site should be ready for action. If you’re using a different database server, look at the section “Moving Data to a Remote Server” later in this chapter and look at Appendix B that explains how to configure a different SQL Server. To check if the site works, open a browser and go to http://localhost. You should see the Planet Wrox web site appear. To verify that everything is in order, browse through the site by requesting pages from the main menu, filling in the contact form, creating a new Album, uploading pictures, and so on. If you get an error, refer to the section “Troubleshooting Web Server Errors.”
Running Your Site under IIS ❘ 721
NOTE If you still can’t make it work, try configuring the file system for the Everyone group. Although, from a security point of view, this is absolutely not the recommended group to use in a production environment, it may help you in finding out whether it’s a security issue. If it works for the Everyone account, it’s indeed security-related, so you need to make sure you configured the correct accounts. Don’t forget to remove the Everyone account later again.
How It Works On a standard Windows system, all files and folders are protected using the Windows NTFS file system. To ensure proper operation of your web site you need to grant the account used by the web server the necessary permissions to the files and folders of your web site. For most files and folders, Read permission is enough. However, for two folders you need to change the permissions. Both App_Data and GigPics are written to at runtime so you need to grant Modify and Write permissions to these folders.
Troubleshooting Web Server Errors When you try to access your site in a web browser, you may run into a number of problems. The first thing you need to do to get helpful error messages is to change the <customErrors> section in web.config from On to Off or RemoteOnly. This makes it easier to see what’s going on. Additionally, you may want to check out the Windows Event Viewer (type eventvwr from the Start Menu) for more details about errors and hints how to solve them. This section lists the most common problems and provides a fix for them. You should realize a large number of possible reasons exist for the errors you may get, so it’s impossible to cover them all here. If you run into a problem you can’t solve, turn to this book’s forum at the Wrox community site at http://p2p.wrox.com. You’ll find many helping hands (including mine) that understand your problem and can help you find a solution for it. ➤➤
It is an error to use a section registered as allowDefinition=‘MachineToApplication’ beyond application level: You get this error when your web site is not at the root of the web server, or you haven’t configured the folder as a separate application. Given the current configuration for the Planet Wrox site, you get this error when, for example, you map your site in IIS to C:\BegASPNET and then browse to http://localhost/Release. To fix this error, make sure that the root of your IIS web site points to the folder that contains your main web.config file; C:\BegASPNET\Release in this case. You get the same error when you open an incorrect folder in VWD. For example, when you open C:\BegASPNET and then browse to http:// localhost/Site. Instead, open C:\BegASPNET\Site as the web site in VWD. You may also run into this error if a subfolder in your site contains a web.config file that tries to override settings that are meant to be defined at the root of the site only; for example, if you have a <membership /> element in the web.config file of the Management folder.
➤➤
HTTP Error 401.3–Unauthorized: You get this error when the account used by the web server does not have permission to read the files on disk. To fix this problem, refer to the Try It Out entitled “Configuring the File System” earlier in this chapter and configure the correct permissions.
722 ❘ Chapter 19 Deploying Your Web Site
➤➤
Failed to update database “C:\BEGASPNET\RELEASE\APP_DATA\ASPNETDB.MDF” because the database is read-only: You get this error when either the database files have been marked as read-only, or if the account used by the web server is not allowed to write to the database files. In the former case, open the file’s Properties in Windows Explorer and verify that the Read Only check box is cleared. In the latter case, ensure that the account used by ASP.NET has at least Modify permissions on the App_Data folder.
➤➤
HTTP Error 403.14–Forbidden: Although this error seems to suggest a problem with NTFS permissions at first, it’s often caused by an incorrect or missing default document. If you get this error, ensure that the site or folder you are accessing contains a document called Default.aspx and that you configured that document name as a default document in IIS.
➤➤
HTTP Error 404.0–Not Found: You get this error when you try to request a file or folder that doesn’t exist, such as http://localhost/DoesNotExist or http://localhost/ DoesNotExist.gif.
➤➤
An error has occurred while establishing a connection to the server. When connecting to SQL Server 2008, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: Named Pipes Provider, error: 40–Could not open a connection to SQL Server). Alternatively you may get the error: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified): You can get these errors for a number of reasons. Although the error message mentions SQL Server 2008 explicitly, you can also get the same error for SQL Server 2005. Usually, this error is caused by problems reaching the configured database server. You can get it when you misspelled the server’s name in a connection string, the server is down, or the server can only be reached from the local machine and is not accessible over the network. To make sure that SQL Server is running correctly, open the Services section of the Administrative Tools (that you find in the Control Panel). Then look under SQL Server and verify that SQL Server is started. Appendix B explains SQL Server security in more detail and provides solutions to these problems.
➤➤
Failed to generate a user instance of SQL Server due to failure in retrieving the user’s local application data path. Please make sure the user has a local user profile on the computer. The connection will be closed: You can get this error when using Windows 7 or Windows Server 2008 R2 when you forgot to enable the “Load User Profile” option discussed in the Try It Out titled “Configuring a Site with Windows Vista, Windows 7, or Server 2008”.
➤➤
HTTP Error 500.21 - Internal Server Error Handler “PageHandlerFactory-Integrated” has a bad module “ManagedPipelineHandler” in its module list Detailed Error Information: You get this error on Windows 7 or Windows Server 2008 when ASP.NET is not registered with IIS. Refer to the section labeled “Installing and Configuring ASP.NET” to learn how to use aspnet_regiis to fix this issue.
➤➤
Runtime Error Description: An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed. Details: To enable the details of this specific error message to be viewable on the local server machine, please create a <customErrors> tag within a “web.config” configuration
Moving Data to a Remote Server ❘ 723
file located in the root directory of the current web application. This <customErrors> tag should then have its “mode” attribute set to “RemoteOnly”. To enable the details to be viewable on remote machines, please set “mode” to “Off”: You may get this error when a runtime error occurs, and the web.config does not contain a <customErrors> element. However, you may also get the same error when the web.config file itself contains an error, for example when you forgot to close an element. To fix this latter category of errors, open the file in VWD and it will provide you with more details about the error. ➤➤
Could not load type ‘System.ServiceModel.Activation.HttpModule’ from assembly ‘System. ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’: You get this error when IIS is configured to run an earlier version of .NET. To fix this problem, run aspnet_regiis -i from the ASP.NET 4 Framework folder in a command prompt as explained earlier.
When you are deploying to a machine that also has SQL Server 2008 Express edition installed, you are done with the deployment process now. However, if you’re dealing with a different SQL Server, the only thing that’s left to do is to make sure your new site has the required data. You see how to do this next.
Moving Data to a Remote Server Releasing a site to IIS on your local machine is pretty straightforward. You simply copy the data to a new location, configure IIS, change a few security settings, and that’s it. Because the site continues to use SQL Server 2008 Express edition, it will run fine. If you need to move your site to an external server or host, things are not so easy. Although copying the files that make up your site is usually extremely simple using an FTP program, copying data from your SQL Server 2008 database to your host is quite often a bit trickier. This is because most web hosts don’t support SQL Server 2008 Express edition, so you can’t just simply copy the .mdf files to the App_Data folder at your remote host. Instead, these hosts often offer the full versions of SQL Server, which you can access either with a web-based management tool or with tools such as SQL Server Management Studio for SQL Server 2005 and 2008. To make it easy to transfer data from your local SQL Server 2008 database into a SQL Server database of your web host, Microsoft created the Database Publishing Wizard.
Using the Database Publishing Wizard The Database Publishing Wizard enables you to create a .sql script that contains all the information required to recreate your database and its data at a remote server. Recreating this data is a two-step process:
1. 2.
Create a .sql script from your local SQL Server database. Send this script to your host and execute it there.
724 ❘ Chapter 19 Deploying Your Web Site
The first step is described in detail in the next exercise. I won’t show you how to run the script at your host because this is different from host to host. Instead, I will give you some general pointers so you know what to look for with your host.
Try It Out
Exporting the Planet Wrox Database
You can access the Database Publishing Wizard from the Database Explorer window (the Server Explorer in paid versions of Visual Studio). Once you have completed the wizard, you’ll have a .sql file that contains all the necessary SQL code to recreate the database at a different server.
1 .
With the Planet Wrox project loaded in VWD, choose View ➪ Database Explorer (or View ➪ Server Explorer).
. 2
Right-click the PlanetWrox.mdf database and choose Publish to Provider to invoke the Database Publishing Wizard. If you get a welcome screen, click Next. Ensure that your database is selected and that Script All Objects in the Database is selected, and then click Next. The dialog box shown in Figure 19-16 appears.
Figure 19-16
3.
In this screen, you can choose between two options. The first enables you to create a text file with the necessary SQL statements, and the second option enables you to talk to your shared hosting provider over the Internet directly. If your host supports this, they can give you the necessary information to set up a Provider here. For now, choose Script to File and click the Next button.
. 4 . 5
In the step that follows, accept all defaults shown in Figure 19-17 and click Next once more. Click Finish and the wizard generates the SQL script for you in your Documents folder. Open the file in Notepad and look at the SQL statements it contains. Although most of it probably looks like gibberish to you, it can be used as is to recreate the database on a compatible SQL Server 2008 database.
Moving Data to a Remote Server ❘ 725
Figure 19-17
How It Works The contents of a database can be separated in two categories: the structure of the database and the actual data. When the Database Publishing Wizard runs, it investigates the structure of your database first and creates SQL CREATE statements for all the items it finds in your database, such as the tables you created in earlier chapters. It then creates INSERT statements that recreate all records such as Reviews, Genres, and even users in the target database. By clearing the Script All Objects check box at the beginning of the wizard, you can selectively choose parts of your database, enabling you to script only a few tables, for example. At the end, the wizard assembles all the SQL statements and saves them to a single .sql file. This file is now ready to be run at your host to recreate the database.
Recreating the Database Although every host has its own rules and procedures when it comes to providing access to their SQL Server, they can be grouped in three categories. First, some hosts don’t give you remote access to their database and require you to submit a .sql file so they can execute it for you. In this case, you don’t have to do anything other than send the file and wait for the host to create your database.
NOTE For detailed information about hosting your site with an external hosting party, check out the tutorials in the hosting section of the official ASP.NET site at www.asp.net/learn/hosting.
726 ❘ Chapter 19 Deploying Your Web Site
The second category contains the hosts that allow you to execute SQL statements through a web interface. You typically log in to your online control panel and then execute the SQL statements created by the Database Publishing Wizard, either by uploading the file or by pasting its contents in a text area in a web page. Regardless of the method, you then end up with a database that you can access from your application. How this works exactly is different with each host, so consult the hosting service’s help or support system for more information. Some known issues exist with webbased database management tools from some providers, resulting in errors when you try to run the generated SQL file. Although the file itself is technically valid, the tool may still run into issues with it. If that’s the case, contact your host for help on resolving the issue. The final category contains hosts that allow you to connect to their SQL Server over the Internet. This enables you to use tools like SQL Server Management Studio to connect to the database at your host right from your desktop and execute the SQL scripts remotely. The SQL Server Management Studio also has a free Express edition that you can download from the Microsoft site (http://www.microsoft.com/express/database/). This tool works pretty much the same as its commercial counterpart that ships with the paid versions of SQL Server 2008 and is an invaluable tool for anyone working with SQL Server databases. After your database is recreated at your target server, you need to reconfigure your ASP.NET application to use the new database by changing the connection strings in your web site. For this to work, you need to modify three connection strings: the PlanetWroxConnectionString1 and PlanetWroxEntities you created in an earlier chapter, and the LocalSqlServer connection string that the ASP.NET Application Services use by default. The easiest way to accomplish this is to clear the old connection string using and then add a new one. How your connection string must look depends on the database you are using and its configuration. For many examples of proper connection strings, check out www.connectionstrings.com. The following snippet provides a simple example that reconfigures your application to use a database server called DatabaseServer. This example shows a connection string for a SQL Server that requires you log in with a username and password (in your configuration file, each connection string should be on a single line):
This points all three connection strings for the PlanetWrox database with the Reviews and Albums tables and the ASPNETDB database that contains Membership, Roles, and Profile information to a different SQL Server. The element is used to remove any previously declared connection string, giving you the chance to add the LocalSqlServer item again with a different connection
The Deployment Checklist ❘ 727
string. Consult Appendix B for more information about configuring your ASP.NET application and SQL Server to operate with each other. Appendix B also explains how you can incorporate the data of the ASPNETDB database into your own, which is useful if your host charges you extra for each separate database. At this stage, you’re pretty much done configuring your newly created web site. Congratulations! However, before you relax and start enjoying your new web site, read the following checklist that helps you secure your site and improve its performance.
The Deployment Checklist Instead of ending this chapter with general tips about deployment, this section gives you a practical list of things to check when you’re ready to put your web site in production: ➤➤
Make sure you don’t have debugging enabled in the web.config file. This causes unnecessary overhead and decreases performance of your web site as code executes slower and important files cannot be cached by the browser. To ensure debugging is disabled, open the web.config file you are using for your production environment, and verify that debug is set to false:
➤➤
Make sure you have turned on custom errors by setting the mode attribute of the customErrors element in web.config to either On or RemoteOnly. In the first case, everyone sees your custom error pages, and in the second case, only users local to the web server can see the error details. Never leave the mode set to Off, because doing so can lead to information disclosure. The following snippet shows a safe configuration of the customErrors element: <customErrors mode=”On” defaultRedirect=”~/Errors/OtherErrors.aspx”> Optional <error /> elements go here
➤➤
Disable tracing, or at least limit the trace information to users coming from the local machine. The following element from web.config blocks tracing for users coming from machines other than the web server itself. Additionally, it stops the trace information from appearing in the page:
➤➤
Consider setting the retail attribute of the deployment element in machine.config to true: <system.web> <deployment retail=”true”/>
This section is used to indicate that the server hosts production-ready versions of your sites and for all sites on the server changes all three previous items to a secure setting: debugging and tracing are disabled, and error messages are only accessible to local users.
728 ❘ Chapter 19 Deploying Your Web Site
To make this change, you need to be logged in as an Administrator on your system. Also, be sure to make a backup copy of the file first. Because it serves as the root configuration file for all your ASP.NET web sites, you don’t want to mess up this file. ➤➤
Scan your site for important files that may contain sensitive information (like Word or text documents) and either exclude them from the release version or consider moving them to the App_Data folder. Files in that folder cannot be accessed directly. However, your own code can still access the files as you saw in Chapter 9.
➤➤
Make sure you turn on error logging. With the error logging code you created in the previous chapter, you are notified whenever an error occurs, enabling you to proactively keep an eye on your server, fixing errors before they get a chance to happen again.
➤➤
If you are using themes in your site, make sure you remove either the theme or the styleSheetTheme attribute from the <pages> element in web.config. The Planet Wrox web site uses themes, but you added the styleSheetTheme attribute to enable design-time support in Visual Web Developer. On your production server all you need is this: <pages theme=”Monochrome”> ...
This way, the page won’t include the same style sheet twice.
What’s Next Now that you have finished your first ASP.NET web site, I am sure you are looking forward to creating your next site. The Planet Wrox site can serve as a basis for new sites you will build. You probably won’t use any of its pages in your site directly, but hopefully this book and the Planet Wrox web site inspired you enough to build a new web site on your own. Because this book is aimed at beginners, I haven’t been able to provide you with a lot of in-depth information on some important topics. Most subjects that have their own chapter in this book easily warrant an entire book on their own. For example, topics like CSS, AJAX, and LINQ are so extensive that Wrox has published many books about them. Now that you’ve mastered the basics of these technologies, you can dig deeper into them using the following books in the Wrox Professional series: ➤➤
Professional CSS: Cascading Style Sheets for Web Design, 2nd Edition (ISBN: 978-0-470-17708-2)
➤➤
Professional ASP.NET 2.0 Design: CSS, Themes, and Master Pages (ISBN: 978-0-470-12448-2)
➤➤
Professional AJAX 3.5 (ISBN: 978-0-470-39217-1)
➤➤
Professional ASP.NET 4 in C# and VB (ISBN: 978-0-470-50220-4)
➤➤
Professional LINQ (ISBN: 978-0-470-04181-9)
➤➤
Professional IIS 7 (ISBN: 978-0-470-09782-3)
Summary ❘ 729
Of course the Web is also a good place for more information. The following URLs may be helpful in your search for more information about ASP.NET and its related technologies: ➤➤
http://p2p.wrox.com: The public discussion forum from Wrox where you can go for all your programming-related questions. This book has its own category on that site, enabling you to ask targeted questions. I am a frequent visitor of these forums and I’ll do my best to answer each question you may have about this book.
➤➤
http://imar.spaanjaars.com: My own web site where I keep you up to date about various
web programming-related topics. ➤➤
http://www.asp.net: The Microsoft community site for ASP.NET technology. Go here for
news on ASP.NET, additional downloads, and tutorials. ➤➤
http://msdn.microsoft.com/asp.net: The official home for ASP.NET at the Microsoft
developers web site that gives you a wealth of information on ASP.NET.
Summary Obviously, deployment is an important action at the end of the development cycle of your new web site. However, it’s unlikely that you only deploy your site once. As soon as you release the first version of your site, you’ll think of other new and cool features you want to add, making the development of your site a never-ending story. To accommodate for this, you need to make your site easy to deploy. One way to do this is by moving hardcoded configuration settings to the web.config file, giving you a single location to change parameters for the site in your development and production environments. When you’re ready to roll out your site, it’s a good idea to create a copy of your site and clean that up before you send the files to your target server. Copying and then publishing a site is a breeze with the Copy Web Site and Publish Web Site commands. Because you will deploy your site against IIS, you need to understand some important settings of this web server. In this chapter you saw how to configure the default web site and make some configuration changes. Because of the way security works in Windows and IIS, you also need to configure your hard drive so that the accounts used by the web server can read the files in your site, and write to specific folders such as App_Data and GigPics. The chapter closed with a short discussion of the Database Publishing Wizard, a tool that enables you to create a scripted copy of your database that you can use to restore your database on a remote server. Appendix B digs deeper into configuring and connecting to remote database servers.
Exercises There are no exercises in this chapter, because the Planet Wrox site is now completely finished. However, your biggest challenge starts now: building web sites with the knowledge you gained from this book. If you ever build a site with the information from this book and want to share it with me, please contact me through my web site at http://imar.spaanjaars.com. Have fun!
730 ❘ Chapter 19 Deploying Your Web Site
⊲⊲ What You Learned in This Chapter .NET Assembly
A file with a .dll extension that contains executable and callable .NET code
Application Pool
A mechanism to isolate (one or more) web sites in IIS to give them their own set of resources
Database Publishing Wizard
A tool integrated with VWD to export data from a SQL Server database to flat files or to a remote database
Deployment
The process of releasing a web site from your development environment to the production environment
Expression syntax
A technique that enables you to bind control properties to different resources, such as application settings defined in web.config
IIS
Internet Information Services — Microsoft’s web server for the Windows platform
Integrated Pipeline mode
With Integrated Pipeline mode turned on for an application pool in IIS, ASP.NET and IIS are tightly integrated together, enabling you to use ASP.NET techniques for non-.NET resources such as static files
Precompilation
The process of compiling a web application into a set of .dll files which can be deployed to a production server. Without precompilation, the ASP.NET files are compiled on the fly the first time they are requested
WebConfigurationManager
Provides access to data that is stored in configuration files
class
CONFER PROGRAMMER TO PROGRAMMER ABOUT THIS TOPIC.
Visit p2p.wrox.com
A
Exercise Answers Chapter 1
Exercise 1 Solution The markup of a page in VWD contains the raw and unprocessed source for the page, including the HTML, ASP.NET Server Controls, and programming code. The web server then processes the page and sends out the final HTML to the browser. In the browser this HTML is then used to render the user interface.
Exercise 2 Solution XHTML and HTML are related because XHTML is a version of HTML rewritten with XML rules. While HTML is much more relaxed, XHTML forces the programmer to write code that conforms to XML rules, such as correct capitalization, closed tags, and attributes that are enclosed in quotes.
Exercise 3 Solution The easiest way to store HTML fragments that you use often is to select them in the Document Window and then drag them to a free space on the Toolbox. When the item is added, you can rename it to give it a more meaningful name. Now you can simply double-click an item or drag it from the Toolbox into your page whenever you need it.
732 ❘ Appendix A Exercise Answers
Exercise 4 Solution There are a number of ways to reset part of the customization changes you may have made, including: ➤➤
Resetting the Window layout by choosing Window ➪ Reset Window Layout.
➤➤
Resetting the Toolbox by right-clicking it and choosing Reset Toolbox.
➤➤
Resetting all settings of VWD using Tools ➪ Settings ➪ Import and Export Settings or Tools ➪ Import and Export Settings, depending on your version of VWD.
Exercise 5 Solution To change the property of a control on a page, you can click the control in Design or Markup View and then use the Properties Grid (which you can bring up by pressing F4) to change the value of the property. Alternatively, you can change the property directly in Markup View.
Chapter 2
Exercise 1 Solution A number of files fall in the Web Files category, including .aspx files (Web Forms that end up as pages in the web browser), .html files (that contain static HTML for your site), .css files that contain Cascading Style Sheets information and .config files that contain configuration information for the web site. Refer to the table with the different file types in Chapter 2 for a complete list of files.
Exercise 2 Solution When you want to make a piece of text both bold and italicized you need to select the text and then click the Bold button in the Formatting toolbar. Next you need to click the Italic button. The final HTML code in the page should look like this: <strong><em>Welcome to Planet Wrox
Exercise 3 Solution The first way is using the Solution Explorer. Right-click your project, choose Add Existing Item and then browse for the item(s) you want to add. Secondly, you can drag and drop files from Windows Explorer or from your desktop directly into a VWD project. As a third alternative, you could put the files directly in the project’s folder using Windows Explorer. For example, files you add to the folder C:\BegASPNET\Site become part of your web site automatically. If you don’t see the new files appear in VWD directly, click the Refresh icon on the Solution Explorer toolbar.
Chapter 3 ❘ 733
Exercise 4 Solution In VWD, you have three different views on your code: Design View, Markup View (also referred to as Source View or Code View), and Split View. The first gives you an impression of how your web page is going to look in the browser, while the second view shows you the raw markup. Split View enables you to see both views at the same time. VWD also has different views for other files. For example, programming code for an ASPX page is generally referred to as the Code Behind view or simply the Code Behind.
Chapter 3
Exercise 1 Solution The biggest benefit of an external style sheet is the fact that it can be applied to the entire site. Simply by changing a single rule in that file, you can influence all pages in your site that make use of that rule. With embedded or inline styles, you need to manually change all the files in your site. External style sheets also make it much easier to reuse a certain style with many different elements in your site. Simply create a class or an ID selector and reuse them wherever you see fit.
Exercise 2 Solution The rule can look like this: h1 { font-family: Arial; color: Blue; font-size: 18px; border-top: 1px solid blue; border-left: 1px solid blue; }
Note another shorthand version of the border property. This one looks similar to the border property you saw earlier in this chapter that allowed you to set the size, style, and color of the border at once. In this rule, border-top and border-left are used to change the left and top borders only; the other two directions, the bottom and right border, are not affected by this rule.
Exercise 3 Solution The second declaration is more reusable in your site because it denotes a Class selector as opposed to an ID selector. The latter can only be used once in a single page by an element that has a matching id attribute: MainContent in this example. The Class selector BoxWithBorders, on the other hand, can be used by multiple elements in a single page, as you are allowed to apply an identical class attribute to multiple elements in a page.
734 ❘ Appendix A Exercise Answers
Exercise 4 Solution VWD lets you attach a style sheet in the following ways: ➤➤
Type in the required element in the of the page in Markup View directly.
➤➤
Drag a CSS file from the Solution Explorer into the section of a page in Markup View.
➤➤
Drag a CSS file from the Solution Explorer and drop it onto the page in Design View.
➤➤
Use the main menu Format ➪ Attach Style Sheet and then browse to your CSS file.
➤➤
Use the Manage Styles or Apply Styles windows and click the Attach Style Sheet link.
Chapter 4
Exercise 1 Solution The mechanism that enables controls to maintain their state is called View State.
Exercise 2 Solution The ASP.NET run time stores the values for the controls in a hidden field called __VIEWSTATE. This hidden field is sent with each postback to the server, where it’s unpacked and then used to repopulate the controls in the page with their previous values.
Exercise 3 Solution The DropDownList only allows a user to make a single selection whereas the ListBox allows for multiple selections. In addition, the DropDownList only shows one item in the list when it’s not expanded, while the ListBox is capable of displaying multiple items simultaneously.
Exercise 4 Solution In order to have a CheckBox control submit back to the server when you select or clear it in the browser, you need to set the AutoPostBack property to True:
Exercise 5 Solution Many of the ASP.NET Server Controls let you change colors using properties like BackColor and ForeColor. Additionally, you can use BorderColor, BorderStyle, and BorderWidth to change the border around a control in the browser. Finally, to affect the size of the control, you need to set its Height and Width properties.
Chapter 5 ❘ 735
Exercise 6 Solution Instead of setting individual styles, you’re much better off setting the CssClass property that points to a rule set. This way, your pages become easier to maintain, as style-related information is stored in a single place: in the style sheet. At the same time, your page loads faster as not all the style information is sent for each control on each request. Instead, the browser reads in the style sheet once and keeps a locally cached copy.
Chapter 5
Exercise 1 Solution Both the Byte and the SByte data types are designed to hold small, numeric values. Both of them take up exactly the same amount of computer memory, so you’re probably best off using the Byte data type. Because it doesn’t allow you to store negative numbers, it’s clear from the start that it can only contain a number between 0 and 255. However, it’s much better not to store someone’s age, but the date of birth instead. That way, you can always extract the age from the date of birth by comparing it with today’s date. Because the date of birth is a fixed point in time, it will always reflect someone’s age correctly without the need to annually update it.
Exercise 2 Solution This piece of code toggles the visibility of the DeleteButton control. It uses both the assignment operator and the negation operator. First, the negation operator is applied to the current value of Visible. When that value is currently True, the Not (! in C#) operator turns it into False and vice versa. This result is then assigned back to the Visible property. So, where the button was previously hidden, it is now visible. Where it was visible before, it’s now made invisible.
Exercise 3 Solution To create a specialized version of Person, you need to create a second class that inherits the Person class and extend its behavior by adding the StudentId property. VB.NET
Public Class Student Inherits Person Public Property StudentId As String End Class C#
public class Student : Person { public string StudentId { get; set; } }
736 ❘ Appendix A Exercise Answers
Chapter 6
Exercise 1 Solution The ContentPlaceHolder element should be placed in the master page. It defines a region that content pages can fill in. The Content control should be placed in a content page that is based on the master page. It is used to supply the content for the ContentPlaceHolder element that it is connected to.
Exercise 2 Solution To link a Content control to its ContentPlaceHolder in the master page, you need to set the ContentPlaceHolderID:
Exercise 3 Solution There are a few ways to do this. First, you can create a named skin with a different CSS class in the same skin file.
You then hook up the control you want to change to this named skin using the SkinID attribute:
Alternatively, you can disable theming on the Button control and give it a different CSS class directly in the ASPX page:
In both solutions, you need a CSS class that sets the background color: .RedButton { background-color: Red; }
Exercise 4 Solution A StyleSheetTheme is applied early in the page’s life cycle. This gives controls in the ASPX page the opportunity to override settings they initially got from the StyleSheetTheme. This means that the StyleSheetTheme just suggests the look and feel of controls, giving the individual controls the ability to override that look. A Theme on the other hand overrides any settings applied by the controls. This allows you as a page developer to enforce the look and feel of controls in your site with the settings from the theme. If you still need to change individual controls, you can disable theming by setting EnableTheming to False.
Chapter 7 ❘ 737
Exercise 5 Solution There are three ways to set the Theme property in an ASP.NET 4 web site. The first option is to set the property directly in the @ Page directive so it applies to that page only:
To apply a theme to all pages in your site, you set the theme attribute of the <pages> element in the web.config file: <pages theme=”Monochrome”>
The final option you have is to set the theme programmatically. You have to do this in the PreInit event of the Page class, which you can handle in individual pages in your site or at a central location using the BasePage class as you did in Chapter 6.
Exercise 6 Solution A base page allows you to centralize behavior for all the pages in your site. Instead of recoding the same functionality over and over again in every page, you move this code to the base page so all ASPX pages can use it. When you implement the theme switcher, all you have to do is write some code in the central BasePage class. All pages in your site that inherit from this BasePage class then correctly set the selected theme, without the need for any additional code.
Chapter 7
Exercise 1 Solution To change the background color of items in the TreeView control you can use the NodeStyle element as follows: ...
Instead of setting the BackColor property (which results in an inline style), you’re better off setting the CssClass property:
You then need to create a separate class in your CSS file: .TreeViewNodeStyle { background-color: White; }
This way, it’s easier to manage the styling from a central location.
738 ❘ Appendix A Exercise Answers
Exercise 2 Solution To redirect a user to another page programmatically, you can use Response.Redirect, Response.RedirectPermanent, and Server.Transfer. The first two options send a redirect instruction to the browser and are thus considered client-side redirects. Server.Transfer, on the other hand, takes place at the server, enabling you to serve a different page without affecting the user’s address bar.
Exercise 3 Solution To display a TreeView that doesn’t have the ability to expand or collapse nodes, you need to set the ShowExpandCollapse property of the TreeView to False.
Chapter 8
Exercise 1 Solution A standard property uses a normal backing variable to store its value, whereas a View State property uses the ViewState collection for this. A normal property is reset on each postback, whereas a View State property is able to maintain its value. This advantage of the View State property comes at a cost, however. Storing the value in View State adds to the size of the page, both during the request and the response. A normal property doesn’t have this disadvantage. You should carefully consider what you store in View State to minimize the page size.
Exercise 2 Solution To make the property maintain its state across postbacks, you need to turn it into a View State property. The required code is almost identical to that of the NavigateUrl, but it uses the Direction data type instead of a string. You need to remove the automatic property and replace it with the following code: VB.NET
Public Property DisplayDirection() As Direction Get Dim _displayDirection As Object = ViewState(“DisplayDirection”) If _displayDirection IsNot Nothing Then Return CType(_displayDirection, Direction) Else Return Direction.Vertical ‘ Not found in View State; return a default value End If End Get Set(ByVal Value As Direction) ViewState(“DisplayDirection”) = Value End Set End Property C#
public Direction DisplayDirection {
Chapter 9 ❘ 739
get { object _displayDirection = ViewState[“DisplayDirection”]; if (_displayDirection != null) { return (Direction)_displayDirection; } else { return Direction.Vertical; // Not found in View State; return a default value } } set { ViewState[“DisplayDirection”] = value; } }
Exercise 3 Solution Using a custom data type like the Direction enumeration has two benefits over using numeric or string data types. Because of the way IntelliSense helps you select the right item, you don’t have to memorize magic numbers or strings like 0 or 1. Additionally, the compiler helps you check the spelling, so if you type Direction.Vrtical instead of Direction.Vertical you get an error at development time.
Chapter 9
Exercise 1 Solution First, you need to write a property in the Code Behind of the user control, similar to the DisplayDirection property you created in the previous chapter for the Banner control. This property could look like this: VB.NET
Public Property PageDescription As String C#
public string PageDescription { get; set; }
You then need to modify the control declaration. For example, in Contact.aspx, you can modify the control like this:
740 ❘ Appendix A Exercise Answers
Note that the PageDescription property contains a short description of the containing page. Obviously, you can put whatever text you see fit to describe the page in the property. Finally, you need to add the PageDescription to the subject or body of the e‑mail message. The following code snippet shows you how to extend the subject with the value of this new property: VB.NET
myMessage.Subject = “Response from web site. Page: “ & PageDescription myMessage.From = New MailAddress(“[email protected]”, “Sender Name”) C#
myMessage.Subject = “Response from web site. Page: “ + PageDescription; myMessage.From = new MailAddress(“[email protected]”, “Sender Name”);
From now on, this custom page description is added to the subject of the mail message.
Exercise 2 Solution If you don’t inspect the IsValid property, your system is vulnerable to invalid data. Users can disable JavaScript in their browser and submit invalid data directly into your page. By checking the IsValid property you can tell whether or not it’s safe to continue with the submitted data.
Exercise 3 Solution The From property of the MailMessage class is of type MailAddress, meaning that you can directly assign a single instance of this class to it. Because you can potentially have more than one recipient, the To property is a collection of MailAddress objects, and so you need to use its Add method to add instances of MailAddress to the To property.
Exercise 4 Solution To call a client-side validation function, you need to set the ClientValidationFunction property of the CustomValidator like this: *
The client function that you need to add to the markup of the page must have the following signature: function FunctionName(source, args) {}
The source argument contains a reference to the actual CustomValidator control in the clientside HTML code. The args argument provides context information about the data and allows you to indicate whether or not the data is valid. The names of the arguments don’t have to be source and args; however, when using these names, the client-side function looks as close to its server-side counterpart as possible. Another common naming scheme, used for almost all other event handlers in ASP.NET, is to use sender and e, respectively.
Chapter 10 ❘ 741
Exercise 5 Solution To tell the validation mechanism whether the data you checked is valid, you set the IsValid property of the args argument in your custom validation method. This applies to both client- and server-side code. The following snippet shows how this is done in the client-side validation method for the ContactForm.ascx control: if (phoneHome.value != ‘’ || phoneBusiness.value !=’’) { args.IsValid = true; } else { args.IsValid = false; }
Chapter 10
Exercise 1 Solution The ScriptManager control is a required component in almost all Ajax-related operations. It takes care of registering client-side JavaScript files, handles interaction with web services defined in your web site, and it’s responsible for the partial page updates. You usually place the ScriptManager directly in a content page if you think you need Ajax capabilities on only a handful of pages. However, you can also place the ScriptManager in a master page so it becomes available throughout the entire site. When you have the ScriptManager in the master page you can use the ScriptManagerProxy to register individual web services or script files on content pages. Because you can have only one ScriptManager in a page, you can’t add another one in a content page that uses your master page with the ScriptManager. The ScriptManagerProxy serves as a bridge between the content page and the ScriptManager, giving you great flexibility as to where you register your services.
Exercise 2 Solution You can let your users know a partial page update is in progress by adding an UpdateProgress control to the page. You connect this control to an UpdatePanel using its AssociatedUpdatePanelID. Inside the you define whatever markup you see fit to inform your user an update is in progress. A typical contains an animated icon, some text, or both.
Exercise 3 Solution To create a script-callable web service, you first need to add a web service file to your site using the Add New Item dialog box. The web service you create inherits from System.Web.Services .WebService, the base class that defines the default behavior for all web services.
742 ❘ Appendix A Exercise Answers
When you create the web service, you need to add the ScriptService attribute to it: VB.NET
<System.Web.Script.Services.ScriptService()> _ <WebService(Namespace:=”http://tempuri.org/”)> _ <WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _ _ Public Class NameService Inherits System.Web.Services.WebService C#
[System.Web.Script.Services.ScriptService] public class NameService : System.Web.Services.WebService
Finally, you need to decorate each method within this class that you want exposed as a web method with the WebMethod attribute: VB.NET
<WebMethod()> Public Function HelloWorld(ByVal yourName As String) As String C#
[WebMethod] public string HelloWorld(string yourName)
Exercise 4 Solution To expose and use a page method you need to carry out the following steps:
1. 2.
Set EnablePageMethods on the ScriptManager to True. Define a static (Shared in VB.NET) method in your page and apply the WebMethod attribute like this: VB.NET
<WebMethod()> Public Shared Function HelloWorld(ByVal yourName As String) As String ... End Function C#
[WebMethod] public static string HelloWorld(string yourName) { ... }
3.
You call the method in JavaScript through the PageMethods object and define a callback method to handle the result of the method: PageMethods.HelloWorld(‘Some Value’, HelloWorldCallback);
Chapter 11 ❘ 743
Chapter 11
Exercise 1 Solution To accomplish this, you first need to set the ClientIDMode of VerticalPanel to Static. This makes it easier to work with the client IDs of the controls inside the panel:
Then you need to add a span element just below VerticalPanel with the text Hide Banner. The style attribute changes the mouse cursor into a hand so users understand the text is clickable when they hover over it. You need to add a runat attribute and a server-side ID so you can hide the link in the Code Behind. Because making the link server side will change the client ID, you also need to set the ClientIDMode to Static. You should end up with this span element: <span id="HideBanner" style="cursor: pointer;" runat="server" ClientIDMode="Static"> Hide Banner
In a script block below the last panel you need to add the following code: <script type=”text/javascript”> $(function() { $(‘#HideBanner’).bind(‘click’, function() { $('#VerticalPanel').slideToggle(); if ($(this).css(‘display’) == ‘block’) { $(‘#HideBanner’)[0].innerText = ‘Hide Banner’; } else { $(‘#HideBanner’)[0].innerText = ‘Show Banner’; } }); });
This code dynamically binds some code to the click event of the span element found by using $(‘#HideBanner’). Then inside the handler for the click, the code finds the whole panel (#VerticalPanel) and calls slideToggle, which hides items in the matched set when they are visible and shows them when they aren’t. The if check then uses the css method on the HideBanner span (now referred to with the this keyword) and asks for its display property. When it is block it means the banner is visible, and thus the text must be Hide Banner. Otherwise, the text is set to Show Banner. Finally, you need to hide the link when the banner is in horizontal mode: VB
Exercise 2 Solution The slideUp method hides elements by slowly decreasing their height. slideDown shows hidden elements instead by doing the reverse: slowly increasing the height of an element until it’s fully visible. Both methods accept, among other arguments, a speed parameter that either accepts a fixed value (slow, normal, or fast) or a number specifying the speed of the animation in milliseconds.
Exercise 3 Solution jQuery’s document ready function fires when the page is finished loading in the browser, only during the initial request. The pageLoad method fires both when the page first loads and after an asynchronous postback, for example when using an UpdatePanel. This difference enables you to choose the desired behavior. Need to fire some code on initial load and after a postback? Choose pageLoad. Otherwise, choose jQuery’s document ready function.
Chapter 12
Exercise 1 Solution The DELETE statement fails because there is a relationship between the Id of the Genre table and the GenreId of the Review table. As long as this relationship is in effect, you cannot delete genres that still have reviews attached to them. To be able to delete the requested genre, you need to delete the associated reviews first, or assign them to a different genre using an UPDATE statement. Exercise 4 shows you how you can accomplish this.
Exercise 2 Solution The relationship between the Genre and Review tables is a one-way relationship. The relationship enforces that the GenreId assigned to a review must exist as an Id in the Genre table. At the same time, it blocks you from deleting genres that have reviews attached to them. However, the relationship doesn’t stop you from deleting records from the Review table.
Exercise 3 Solution To delete reviews with an Id of 100 or less, you need the following SQL statement: DELETE FROM Review WHERE Id 0 Select New With {genre.Name, genre.Reviews} C#
var allGenres = from genre in myEntities.Genres orderby genre.Name where genre.Reviews.Count() > 0 select new { genre.Name, genre.Reviews };
Chapter 15
Exercise 1 Solution The Load event of the Page always fires before user-triggered events such as a Button control’s Click.
Exercise 2 Solution To preselect the correct item in the drop-down list after a user has inserted or edited a review, you need to make two modifications. First, you need to change the Redirect statement in the page AddEditReviewHandCoded.aspx so it includes the ID of the genre: VB.NET
Using String.Format makes this code a bit easier to read as opposed to plain string concatenation using the ampersand (&) in VB.NET and the plus (+) in C#. If you now insert or edit a new review, you’ll see that the ID of the genre is passed back to the Reviews .aspx page. On that page, you can use that ID to preselect the correct item in the DropDownList control, which you can accomplish with the following code in the Page_Load method: VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load If Not Page.IsPostBack Then Dim genreId As String = Request.QueryString.Get(“GenreId”)
750 ❘ Appendix A Exercise Answers
If Not String.IsNullOrEmpty(genreId) Then DropDownList1.DataBind() Dim myItem As ListItem = DropDownList1.Items.FindByValue(genreId) If myItem IsNot Nothing Then myItem.Selected = True End If End If End If End Sub C#
Only when the page loads from a new request (and not from a postback) does this code fire. The code then tries to find a GenreId in the query string. If it can find it, it tries to find an item with that requested value in the DropDownList. Because the DropDownList control hasn’t been data bound yet it doesn’t contain any items. Therefore, you need to call DataBind() first. This gets the genres from the database using EF and puts them in the DropDownList. Once that’s done and the item is found in the Items collection, it’s made the active item by setting its Selected property to true. The SqlDataSource control watches this DropDownList so when the data source gets its reviews, it does so for the correct genre.
Exercise 3 Solution The various data-bound controls can raise exceptions that you can handle in their event handlers. Once you have dealt with the exception appropriately, you need to set the ExceptionHandled property of the e argument to True. The following code snippet shows how a Label control is updated with an error message. The ExceptionHandled is then set to stop the Exception from getting passed on into the user interface where it would otherwise result in a “Yellow Screen Of Death.” VB.NET
Protected Sub SqlDataSource1_Deleted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceStatusEventArgs) _ Handles SqlDataSource1.Deleted
Chapter 16 ❘ 751
If e.Exception IsNot Nothing Then ErrorMessage.Text = “We’re sorry, but something went terribly wrong while “ & “deleting your genre.” e.ExceptionHandled = True End If End Sub C#
protected void SqlDataSource1_Deleted(object sender, SqlDataSourceStatusEventArgs e) { if (e.Exception != null) { ErrorMessage.Text = @”We’re sorry, but something went terribly wrong while deleting your genre.”; e.ExceptionHandled = true; } }
Chapter 16
Exercise 1 Solution Authentication is all about proving your identity to a system like a web site. After you have been authenticated, authorization then determines what you can and cannot do in the system.
Exercise 2 Solution To expand the access to the Management folder for John and all users in the Editors role, you need to expand the current roles attribute to include Editors, and add an additional allow element with its users attribute set to John: <system.web> <deny users=”*” />
The roles attribute allows you to specify multiple roles separated by a comma. In order to grant the John account access you need to add an additional allow element and then fill in John’s name in the users attribute. From a maintainability perspective, it would be a lot better to add John to the Managers or Editors role if possible. However, you may end up giving John more rights than you want (he could then access anything that a Manager or an Editor could access). Generally, it’s best to manage users through roles as much as possible, but it’s good to know that you can grant individual accounts the necessary rights as well (or explicitly take those rights away using a deny element).
752 ❘ Appendix A Exercise Answers
Exercise 3 Solution If you want to redirect all users to the same page, all you need to set is the DestinationPageUrl:
When a user is logged in successfully, she’s taken to MyProfile.aspx automatically.
Exercise 4 Solution The LoginStatus simply displays a simple text that indicates whether or not the user is logged in. By default the text that is displayed is Login when the user is currently not logged in, and Logout when the user is already logged in. Clicking the link either sends the user to the default Login page, or logs the user out. The LoginView is somewhat similar in that it displays different content depending on whether the user is currently logged in. However, because the control is completely template driven, you can fully control the content that is displayed. To enable you to differentiate between different user roles, you can use the RoleGroups element to set up templates that are only shown to users in specific roles.
Chapter 17
Exercise 1 Solution You would implement the favorite theme as a String property and call it FavoriteTheme. To ensure that you always have a valid theme, you could also set a default value. Finally, you should make the property accessible to anonymous users. Your final profile property could end up like this:
To support anonymous profiles, you need to explicitly enable them by adding an element as a direct child of <system.web> in the web.config file: <system.web>
Exercise 2 Solution Given the syntax you saw in the question, you could now access the new property and use it to change the current theme in the BasePage: VB.NET
Private Sub Page_PreInit(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.PreInit Dim myProfile As ProfileCommon = CType(HttpContext.Current.Profile, ProfileCommon)
Chapter 17 ❘ 753
If Not String.IsNullOrEmpty(myProfile.FavoriteTheme) Then Page.Theme = myProfile.FavoriteTheme End If End Sub C#
Exercise 3 Solution To finalize the theme selector using Profile, you also need to change the code in the master page Frontend.master. Instead of storing the user-selected theme in a cookie, you should now store it in Profile. Change the code in Page_Load as follows: VB.NET
If Not Page.IsPostBack Then Dim selectedTheme As String = Page.Theme If Not String.IsNullOrEmpty(Profile.FavoriteTheme) Then selectedTheme = Profile.FavoriteTheme End If If ThemeList.Items.FindByValue(selectedTheme) IsNot Nothing Then ThemeList.Items.FindByValue(selectedTheme).Selected = True End If End If Select Case Page.Theme.ToLower() C#
if (!Page.IsPostBack) { string selectedTheme = Page.Theme; if (!string.IsNullOrEmpty(Profile.FavoriteTheme)) { selectedTheme = Profile.FavoriteTheme; } if (ThemeList.Items.FindByValue(selectedTheme) != null) { ThemeList.Items.FindByValue(selectedTheme).Selected = true; } } switch (Page.Theme.ToLower())
754 ❘ Appendix A Exercise Answers
You can then simplify the code in lstPreferredTheme_SelectedIndexChanged in the master page to: VB.NET
Protected Sub ThemeList_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ThemeList.SelectedIndexChanged Profile.FavoriteTheme = ThemeList.SelectedValue Response.Redirect(Request.Url.ToString()) End Sub C#
To enable Profile for anonymous users, you need to enable anonymous identification with the following element in web.config, directly under <system.web>:
Chapter 18
Exercise 1 Solution Debugging is the process of watching your code execute in the development environment, investigating variables and looking into objects in order to understand the execution path of your code, looking for bugs with the aim to fix them. Debugging usually takes place at development time in your Visual Web Developer IDE. Tracing, on the other hand, provides you with information on the runtime execution of your code. As discussed in this chapter, you can use tracing to get information about events that fire and the order in which they fire. Additionally, you can add your own information to the trace. Because disabling tracing through configuration greatly minimizes the performance overhead associated with it, you can leave your trace calls in the code, making it easy to disable and enable tracing whenever you need it.
Exercise 2 Solution The best way to stop a possible exception from ending up in the user interface is to wrap your code in a Try/Catch block. That way you can display an error message to the user in case something goes wrong. Your code could end up looking like this: VB.NET
Try ‘ Execute code here to send an e‑mail or write to a file. Catch ex As Exception ErrorMessage.Text = “Something went wrong while executing the requested operation” End Try
Chapter 18 ❘ 755
C#
try { // Execute code here to send an e‑mail or write to a file. } catch (Exception ex) { ErrorMessage.Text = “Something went wrong while executing the requested operation”; }
Exercise 3 Solution To understand which exceptions occur in the site and find out where they occur (that is, what pages or code files are causing the exceptions) you can log all exceptions using some code in the Application_Error event handler. The exception details you can intercept in this method should help you understand the cause of the exception, which in turn should help finding a fix for it. To prevent your users from seeing the “Yellow Screen of Death” error messages, you need to use custom error pages. You should create a simple Web Form that tells the user something went wrong. To tell the ASP.NET runtime to show the contents of that file instead of the error message, you need the following element in your web.config: <customErrors mode=”On” defaultRedirect=”~/Errors/AllOtherErrors.aspx” redirectMode=”ResponseRewrite”> <error statusCode=”500” redirect=”~/Errors/Error500.aspx” />
This element sets up a special page for error code 500 that occurs when your code crashes unexpectedly. When other exceptions occur, such as a “Page not found” error, users are sent to the more generic AllOtherErrors.aspx page.
B
Configuring SQL Server 2008 So far, the exercises in the book assume that you are using Microsoft SQL Server 2008 Express edition as the database for the Planet Wrox project. SQL Server 2008 Express edition is great for development because it’s free, relatively lightweight, and easy to use. However, in a production environment it doesn’t always cut it because it is limited in terms of processor and memory usage and database size. In cases where the Express edition is not enough, you need to look at its bigger brothers: the commercial versions of SQL Server 2008, such as the Standard or Enterprise Editions. In this appendix you will learn about security in SQL Server 2008, how to enable your SQL Server 2008 database and your ASP.NET 4 application to work together, and how to obtain and use SQL Server Management Studio Express, a free tool from Microsoft that lets you manage your database. Although this appendix doesn’t discuss SQL Server 2000 or 2005, you’ll find that most of the concepts apply to these older versions of SQL Server as well. In fact, you can even use SQL Server 2008 Management Studio Express to manage your SQL Server 2000 and 2005 databases. The Appendix shows screen shots of an Express edition of SQL Server, but the exact same principles apply to the commercial versions of SQL Server. In addition to configuring your SQL Server and ASP.NET 4 application to work together, this appendix also shows you how to configure a SQL Server database to support the ASP.NET 4 application services. This is necessary if you want your application to use your own, custom database instead of the aspnetdb.mdf database that is generated for you automatically.
Configuring SQL Server 2008 Before you can configure your database, you need to be aware of the various security concepts that are inevitably associated with databases and web applications. In Chapter 19 you learned how the different computer accounts used by the web server play a big role when configuring security settings for the file system, and that’s no different when connecting to SQL Server. In the following section, you get a quick primer on the different ways to connect to SQL Server.
758 ❘ Appendix B Configuring SQL Server 2008
In the section that follows you see how to attach your .mdf database files to SQL Server followed by a discussion of configuring your application and database so they can talk to each other.
Terminology and Concepts When you connect to a SQL Server database, SQL Server needs to know who you are, so it can enforce the correct access policies on the objects like tables in the database. SQL Server 2008 supports two different authentication mechanisms: SQL Server Authentication and Windows Authentication (often called Integrated Security). Both come with a few advantages of their own and require you to write different connection strings to connect to SQL Server. In the following section you see the two most common connection strings, but you’re advised to visit www.connectionstrings.com for an extensive list of possible connection strings.
SQL Server Authentication With SQL Server Authentication, SQL Server takes care of user management. You manage the users for your SQL Server database with Microsoft SQL Server Management Studio, either the Express edition (which you’ll see how to obtain and use a little later in this appendix) or the full versions that ship with the commercial versions of SQL Server 2008. To connect your web application to a SQL Server 2008 installation that requires you to use SQL Server authentication, you need to pass a user name and password in the connection string of your application. A typical connection string looks like this: Data Source=ServerName;Initial Catalog=DatabaseName; User Id=UserName;Password=Password;
In this case the Data Source addresses an unnamed instance of SQL Server: the SQL server is addressed by its machine name alone. Recall from Chapter 13 that it’s also possible to install SQL Server as a named instance. With a named instance, the name of the server is followed by a back slash (\) and the name of a particular SQL Server instance. For example: Data Source=ServerName\InstanceName;Initial Catalog=DatabaseName; User Id=UserName;Password=Password;
SQL Server authentication is often used when you need to connect to a remote SQL Server over the Internet as Windows authentication, discussed next, is not supported in that scenario.
Windows Authentication With Windows Authentication, the Windows OS takes care of user management. All interaction with the database is done in the context of the calling user so the database knows who’s accessing the system. You still need to map a Windows account to a SQL Server account so SQL Server can determine whether the account has sufficient permissions. I’ll show you how to do this later. A typical connection string using Windows Authentication looks like this: Data Source=ServerName;Initial Catalog=DatabaseName;Trusted_Connection=True
Instead of specifying a user name and password you add Trusted_Connection=True to the connection string to indicate you want to connect to the server with the user context of the calling user. You may also come across the setting Integrated Security=True which has the same effect.
Configuring SQL Server 2008 ❘ 759
Since both authentication methods eventually do the same thing (they enable you to connect to SQL Server), you may wonder which one of the two you should use.
Choosing between Windows and Server Authentication In general, it’s recommended to use Windows authentication. The fact that you don’t need to use a password in the connection string means your application will be a bit safer. You don’t need to send the password over the wire, and it’s also not stored in a configuration file for your application. However, SQL Server Authentication is a bit easier to set up. Since you specify the user name and password explicitly, you don’t need to know the final user account that your application runs under. Later in this appendix you see how to use both authentication mechanisms to connect to your database. However, you need to look at something else first: the tools used to manage SQL Server.
Using SQL Server Management Studio You use SQL Server Management Studio to manage your databases. It enables you to attach and detach databases to your SQL Server; create new database objects like tables in existing databases; select, create, edit and delete data; and much more. If you are using a commercial version of SQL Server 2008, you already have access to SQL Server Management Studio, as it comes bundled with the database engine. If you are using the free Express edition of SQL Server, you need to download SQL Server Management Studio Express first. This Express edition of the database management tool is similar to its commercial counterpart and it enables you to carry out most if not all of your database management tasks.
Obtaining and Installing SQL Server Management Studio Express You can download SQL Server Management Studio Express from the following page at the Microsoft site: www.microsoft.com/express/database/. Alternatively, you can go to the main downloads page at www.microsoft.com/downloads and search for “SQL Server 2008 Management Studio Express.” Make sure you download and install the 2008 version of Management Studio, and not the older 2005 version. As a final alternative, you can use the Web Platform Installer as explained in Chapter 19. After you have downloaded the Management Studio setup file, run the installer and follow the on-screen instructions. When asked for the Installation Type, choose Perform a New Installation of SQL Server 2008. This option enables you to choose Management Tools as a component to install later in the Setup Wizard. After installation is finished, you can start up SQL Server Management Studio from the Start Menu. You’ll be presented with a screen similar to Figure B-1 that enables you to select a SQL Server instance you want to log in to. Figure B-1
760 ❘ Appendix B Configuring SQL Server 2008
If not already preselected in the Server Name drop-down list, enter the name of the SQL Server instance you want to connect to. If you’re connecting to a local Express installation, enter .\SqlExpress. Otherwise, enter the name of the database server and the Instance name if used. Depending on the database server you are connecting to, you can log in with Windows Authentication using your current Windows account or you can choose SQL Server Authentication from the Authentication drop-down list and enter a user name and password. After you log in, you get a screen similar to Figure B-2 that enables you to manage your SQL Server instance.
Figure B-2
If you are having trouble connecting to a remote SQL Server (for example, an instance of SQL Server that is not on the same physical server as the one you’re running Management Studio on), you may need to enable SQL Server for remote connections first. This is discussed next.
Enabling Remote Connections in SQL Server When working with SQL Server 2008, you may receive the following error: An error has occurred while establishing a connection to the server. When connecting to SQL Server 2008, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. Alternatively, you may get a more generic error stating that the server was not found or not accessible. Although you may also get this error when the database server is down or not responding, you also get this error when SQL Server is not configured for remote connections. In a default installation, SQL Server only allows local applications to connect and blocks remote connections automatically. To resolve this, and grant remote systems access to the server as well, follow these steps:
1.
Open the SQL Server Configuration Manager from the Microsoft SQL Server 2008 Start menu item. Depending on the version of SQL Server you’re using, this item may be located under the Configuration Tools submenu.
2.
In the window that appears, expand SQL Server Network Configuration, locate your instance of SQL Server and click it to display the list with available protocols. Figure B-3 shows the list for a SQL Server instance called SQLEXPRESS.
Configuring SQL Server 2008 ❘ 761
Figure B-3
3.
In the list with protocols on the right, right-click Named Pipes and choose Enable if its status is currently set to Disabled.
4. 5.
Repeat the previous step, but now enable TCP/IP.
Restart SQL Server. You can do this by right-clicking the server in the Object Explorer of SQL Server Management Studio (shown in Figure B-2) and choosing Restart. If you get an error about security permissions, you may need to reboot your computer instead.
SQL Server now allows incoming connections from remote machines. However, before you can actually use your databases, you need to attach them to SQL Server first. This is described in the following section.
NOTE If you have trouble connecting to SQL Server, make sure that SQL Server is installed and running. To verify this, open the Control Panel and then open the Administrative Tools section found in the System and Security category (called System and Maintenance on Windows Vista). Next, open the Services item and then verify that the SQL Server instance you are connecting to is running. If you installed SQL Server Express on your local machine, the service is called SQL Server (Express).
Attaching Databases to SQL Server SQL Server Express enables you to work with database files in two ways: you can either attach them at run-time using a special attribute in the connection string, or you can attach them using tools such as Management Studio before you start using the database. You’ve been using the first option in all database-related chapters so far. The Planet Wrox connection string you used looks like this:
762 ❘ Appendix B Configuring SQL Server 2008
This connection string points to a database called PlanetWrox.mdf located in the web site’s App_Data folder (determined by |DataDirectory|). The connection string instructs SQL Server to attach this database file to SQL Server on the fly when it’s used. When the database is no longer needed, it is detached again. It also instructs the server to use a User Instance which is a SQL Server feature to allow accounts with low privileges to still run SQL Server and attach databases to it on the fly. This is a great solution for local development because it enables you to easily use SQL Server databases, create and use new ones, and move them around from project to project. However, with a production database this option isn’t good enough and you need to attach the database to SQL Server first. The following steps explain how to attach the PlanetWrox.mdf and the aspnetdb.mdf databases to an instance of SQL Server in case you have the need. You can follow the exact same steps if you want to use SQL Server Management Studio to perform maintenance tasks on a SQL Server database that you cannot do in VWD (managing users and roles, for example).
1. 2.
3.
Enable Modify permissions on the folder where the database resides (C:\Data\SqlServer) for the account used by SQL Server (which is the Network Service account by default) and for your own account. Chapter 19 explains how to set these permissions.
4.
Open SQL Server 2008 Management Studio (any version will do) and log into your SQL Server instance you want to attach the database to. Depending on your security settings, you may have to run this process as an administrator. To do this, right-click the Management Studio menu item in the Windows Start Menu and choose Run as Administrator. Regardless of how you started SQL Server Management Studio, you should see a screen similar to Figure B-2.
Right-click the Databases node shown in Figure B-2 and choose Attach.
5. 6.
7.
Click the value in the Attach As column to make it editable and type PlanetWrox as the new name that will be given to the database. Once you’re done, your dialog box should look like Figure B-4.
8.
Click OK to attach the database to SQL Server. If you get an error, make sure your own account (or the Users group you are part of) and the Network Service account both have Modify permissions on the C:\Data\SqlServer folder and the .mdf files this folder contains.
9.
Your database is now accessible under the Databases node of Management Studio’s Object Explorer. If you expand the Databases element and then look into your database you should see familiar items like tables that you also saw in the Database Explorer in Visual Web Developer earlier in the book. Figure B-5 shows the attached database and its tables.
10.
Create a folder that will hold your new database, such as C:\Data\SqlServer. Move the files PlanetWrox.mdf and aspnetdb.mdf and their associated .ldf files from the web site’s App_Data folder at C:\BegASPNET\Release to this new folder.
In the dialog box that follows, click the Add button and then select the PlanetWrox.mdf file you moved to C:\Data\SqlServer in step 2.
Repeat steps 5 through 8, but now attach the aspnetdb.mdf database. In step 7 type aspnetdb as the new name for the database.
Configuring SQL Server 2008 ❘ 763
Figure B-4
Figure B-5
At this stage, only administrative accounts (Windows administrators or the built-in SQL Server administrative account called SA) have access to the database. In order to have the Planet Wrox web site work with these two databases, you need to configure both SQL Server security and your web site. You see how to do this next.
764 ❘ Appendix B Configuring SQL Server 2008
Connecting Your Application to SQL Server 2008 In the following section, I’ll show you how to connect to SQL Server from two different but common scenarios: using SQL Server Authentication and using Windows Authentication when IIS and SQL Server are on the same server. For both scenarios you’ll see how to configure SQL Server, the Planet Wrox web site, and if necessary, your Windows accounts. You’re likely to use the first scenario when dealing with an external hosting company that hosts your site. When web hosts offer SQL Server, they often use SQL Server Authentication and as such require you to pass a user name and password to the database server. The second scenario is useful if you host the site yourself and have both SQL Server 2008 and IIS on the same machine. More advanced scenarios, such as using Windows Authentication with IIS and SQL Server on two different machines, are beyond the scope of this appendix. For more information about configuring and securing SQL Server, get yourself a copy of Beginning Microsoft SQL Server 2008 Administration (Wrox, ISBN: 978-0-470-44091-9) or Professional Microsoft SQL Server 2008 Administration (Wrox, ISBN: 978-0-470-24796-9).
Scenario 1 — Using SQL Server Authentication From a configuration point of view, this is probably the easiest scenario to configure: all you need to do is make sure that your SQL Server installation supports SQL Server authentication, create a user in SQL Server, and then use that account in the connection string of the Planet Wrox web site. To do this, follow these steps:
1.
In SQL Server Management Studio, right-click the server name in the Object Explorer shown in Figure B-2, choose Properties, and switch to the Security category. The dialog box shown in Figure B-6 appears.
2.
If not already selected, choose the SQL Server and Windows Authentication Mode item at the top of the screen. Before you click OK, click the Help item at the top of the screen and read a bit more about SQL and Windows Authentication, and determine if you really need SQL Server Authentication. Windows Authentication is more secure than SQL Server authentication, so you’re advised to use that option whenever possible.
3.
Restart SQL Server. You can do this by right-clicking the server in the Object Explorer and choosing Restart. If you get an error about security permissions, you may need to reboot your computer instead.
4.
Back in SQL Server’s Management Studio’s Object Explorer, expand the server’s Security node visible in Figure B-2. Make sure you choose the one directly under your server name, and not the one belonging to a specific database. Right-click on Logins and choose New Login.
5.
Type a Login name, then select the SQL Server Authentication option, and type a password. In this and the following examples, I’ll use PlanetWroxUser as the user name, and Pa$$w0rD (with a zero instead of the letter o) as the password.
6.
Clear the Enforce Password Expiration option. This also disables User Must Change Password at Next Login. Your dialog box should end up looking like the one in Figure B-7.
Configuring SQL Server 2008 ❘ 765
If you want to learn more about the individual settings on this screen, click the Help button at the top of the screen.
7.
Click OK to create the new account.
Figure B-6
Figure B-7
766 ❘ Appendix B Configuring SQL Server 2008
With the account created, the next step is to give this new account the proper permissions to your database:
1.
On the Object Explorer, expand Databases, then the PlanetWrox database, followed by the Security node. Finally, right-click the Users node, and choose New User.
In the User Name text box, type PlanetWroxUser.
2. 3.
4.
At the bottom of the screen, you see a box labeled Database Role Membership. In this box, you can choose a number of roles that you can grant to your new user. The rule here is: give users as little permissions as possible. A good choice is db_datareader and db_datawriter that allows the account to both read from and write to tables in the database, so check these two options, visible in Figure B-8.
In the Login Name text box, type PlanetWroxUser again. Alternatively, click the ellipsis button, then click Browse, and select the user from the list that has appeared.
Figure B-8
NOTE Check out SQL Server’s Books Online for more information about the various roles.
Configuring SQL Server 2008 ❘ 767
5.
If you want to set fine-grained security options for your database objects, click the Securables option you see in the left-hand part of Figure B-8. This dialog box enables you to determine permissions for the user account on objects in your database like tables, views, and stored procedures. For the Planet Wrox web site, you don’t need to make any changes in this dialog box.
6.
Finally, click OK to assign the PlanetWroxUser account to the db_datareader and db_datawriter roles.
7.
Repeat all preceding steps, but now configure the aspnetdb database that you attached earlier. When you get to step 4, select all the roles that start with aspnet_. These are the roles that are used by the ASP.NET application services and grant the new user account just enough permissions to support the various application services like Membership and Roles. There’s no need to select any of the other roles.
8.
You can close SQL Server Management Studio for the moment as you’re done with it.
Now that SQL Server and your user accounts are configured correctly, the final phase is to configure the web site to use this new user account.
1.
2.
Open the web.config file of the deployed Planet Wrox application from the C:\BegASPNET\ Release folder. Modify the element as follows:
In your configuration file, each connection string should be on a single line. Don’t forget to replace the value ServerName in the Data Source attributes with a valid server name. Depending on your server and configuration, this could be as simple as (local) or .\SqlExpress to point to a SQL Server on the local machine, DatabaseServer to point to a server called DatabaseServer on the network, or something like DatabaseServer\ Sql2008 that points to a named instance called Sql2008 on a machine called DatabaseServer. You need to add the LocalSqlServer connection string if you didn’t add it in Chapter 19.
768 ❘ Appendix B Configuring SQL Server 2008
3.
Save the changes and then open the site by starting your browser and going to http:// localhost. Everything should still work but the site now no longer uses the databases in the App_Data folder, but uses the SQL Server you defined in your connection strings instead. If you get an error when browsing to the site on your local host, you may need to turn off custom errors in web.config (for security reasons, set it to RemoteOnly instead of to Off) to see the actual error message. Possible reasons for an error include an incorrect user name, password or server name in the connection string, and an incorrectly configured database role membership for the PlanetWroxUser account.
Scenario 2 — Using Windows Authentication with IIS and the Database on the Same Machine This is a common scenario, especially when you’re running your site on a local machine that you control yourself. Both the web server (either IIS or the built-in development web server) and SQL Server run on the same physical machine. This scenario makes it easy to use Windows Authentication because both the web server and SQL Server can use the same Windows account. To configure your server for this scenario, follow these steps:
1.
Determine the account used by your web server. Refer to Chapter 19 for precise details on how to do this, but you’re likely to need the Network Service or the ApplicationPoolIdentity account (called IIS AppPool\ASP.NET v4.0 by default). I am using the account IIS AppPool\ ASP.NET v4.0 in the remainder of this section.
2.
Next, you need to map this Windows account to a SQL Server account. To do this, open SQL Server Management Studio and log in to your SQL Server instance. Expand the Security node for the server (and not of an individual database), as shown in Figure B-2. Then rightclick Logins and choose New Login.
3.
In the Login name box, enter NT Authority\Network Service or IIS AppPool\ASP.NET v4.0 depending on the account you’re configuring and click OK to add the new Login.
With the account created, the next step is to give this new account the proper permissions to your database.
1.
Open the Security node of the PlanetWrox database and then expand the Users node, rightclick it, and choose New User.
2.
In the User name text box, type Network Service or ASP.NET v4.0 depending on the account name you configured earlier).
3.
For the Login name text box, click the ellipsis button and then click Browse so you can select a user name. Choose the account you configured earlier and click OK twice.
4.
At the bottom of the screen, you see a box labeled Database role membership. In this box, you can choose a number of roles that you can grant to your new user. The rule here is: give users as little permissions as possible. A good choice is db_datareader and db_datawriter that allows the account to both read from and write to tables in the database, so check these two options, shown in Figure B-9.
Configuring SQL Server 2008 ❘ 769
NOTE Check out SQL Server’s Books Online for more information about the various roles.
Figure B-9
5.
If you want to set fine-grained security options for your database objects, click the Securables option visible in Figure B-9. This dialog box enables you to determine permissions for the user account on objects in your database like tables, views, and stored procedures. For the Planet Wrox web site, you don’t need to make any changes in this dialog box.
6. 7.
Finally, click OK to assign the account to the db_datareader and db_datawriter roles.
Repeat steps 1–6, but now configure the aspnetdb database that you attached earlier. When you get to step 4, select all the roles that start with aspnet_. These are the roles that are used by the ASP.NET application services and grant the new user account just enough permissions to support the various application services like Membership and Roles.
770 ❘ Appendix B Configuring SQL Server 2008
Now that SQL Server and your user accounts are configured correctly, the final step is to configure the web site to use this new user account.
1.
Open the web.config file of the PlanetWrox application from the C:\BegASPNET\Release folder.
2.
Modify the element so it ends up like this:
In your configuration file, each connection string should be on a single line. Don’t forget to replace the value ServerName in the Data Source attributes with a valid server name. Depending on your server and configuration, this could be as simple as (local) or .\ SqlExpress to point to a SQL Server on the local machine, DatabaseServer to point to a server on the network called DatabaseServer or something like DatabaseServer\Sql2008 that points to a named instance called Sql2008 on a machine called DatabaseServer. You need to add the LocalSqlServer connection string if you didn’t add it in Chapter 19.
3.
Save the changes and then open the site by starting your browser and going to http:// localhost (or the address you configured in Chapter 19). Everything should still work as expected, but the site now no longer uses the databases in the App_Data folder, but it uses the SQL Server defined in your connection strings instead through Windows Authentication, as identified by the Trusted_Connection=True attribute in the connection string. If you get an error when browsing to the site, you may need to turn off custom errors in the
web.config file (or set it to RemoteOnly) to see the actual error message. Possible reasons
for the error are an incorrect server name in the connection string and an incorrectly configured database role membership for the configured account.
Once you find out the correct account and have configured SQL Server correctly, using Windows Authentication isn’t that hard. In fact, your connection string now becomes a little easier and more secure, as you don’t need to store a user name and password in it anymore.
Configuring Application Services ❘ 771
Configuring Application Services Earlier in this book you learned that the ASP.NET application services make use of a SQL Server 2008 database that is accessed by using a connection string called LocalSqlServer. The application services then ensure that a new database called aspnetdb.mdf is created in your web site’s App_Data folder the very first time you use one of the application services like Membership or Profile. What if you don’t want to use the default aspnetdb.mdf database but rather one of your own? Or what if you’d rather not rely on a local SQL Server Express database to be created for you, and instead want to get your membership, role, and profile data from your own, dedicated server? In such cases, you need to reconfigure your application (through the web.config file) and tell it where your database is located. There are two ways to accomplish this: overriding the LocalSqlServer connection string, and overriding the settings for the various application services. However, before you look at these two options, you need to know how you can prepare a custom database for the ASP.NET application services.
Configuring Your Database for the Application Services When the aspnetdb database is created for you in the App_Data folder, it already contains a number of objects like tables and stored procedures (snippets of SQL code that can be executed at the server). If you want to use your own database instead of the default one, you need to prepare your database for the application services first. The Microsoft .NET Framework ships with a handy tool designed specifically for this task. You find this tool, called aspnet_regsql.exe, in the following location: %windir%\Microsoft.NET\Framework\v4.0.30128
If you have a newer version of the .NET Framework, the number following v4.0 may be slightly different and if you’re using a 64 Bit version of Windows the parent folder is called Framework64. You should type %windir% exactly as shown here in the address bar of Windows Explorer and it will automatically expand to your Windows folder. To execute the tool, simply double-click aspnet_regsql.exe in the folder. This starts the ASP.NET SQL Server Setup Wizard. Click Next twice to reach the screen that enables you to select a SQL Server and a database (see Figure B-10). Enter the name of your database server (for example .\SqlExpress for your local SQL Server Express edition) and then select a database (such as PlanetWrox) from the drop-down list in the bottom half of the screen. If you don’t see your database listed, you may need to attach it first. Attaching databases is described earlier in this appendix. Once you have selected your database, click Next twice and the tool creates the necessary objects in your database. You can click Finish to close the Wizard as you’re done with it.
772 ❘ Appendix B Configuring SQL Server 2008
Figure B-10
The GUI version of the tool that you just saw adds support for all application services in one fell swoop. If you want to control what gets created exactly, open up a command window, navigate to %windir%\Microsoft.NET\Framework\v4.0.30128, type aspnet_regsql /?, and then press Enter to get a (long) list of options. Again, your exact framework folder may be called slightly different. Scroll up in the option list to see all the available possibilities. At the very least you need to define the following options to add support for an application service: ➤➤
-S: Enables you to set the SQL Server name.
➤➤
-E: Indicates you want to use Windows Authentication to connect to SQL Server.
➤➤
-A: This option must be followed by one or more letters to indicate support for the different application services, like m for Membership, r for the Role manager, and p for Profile.
➤➤
-d: Defines the name of the database that must be configured. If you leave out this name, the tool uses the default aspnetdb database.
To execute a command that creates support for Membership, the Role manager, and Profile against a local SQL Server instance called SqlExpress in the database PlanetWrox, you need to execute the following command: aspnet_regsql -S .\SqlExpress -E -A mrp -d PlanetWrox
After a while you get a message indicating that the requested services were configured successfully. Whether you used the wizard or the command line interface, from now on the database you configured can be used for ASP.NET application services.
Configuring Application Services ❘ 773
Overriding the LocalSqlServer Connection String This is the easiest and quickest path to using a different database. All you need to do is overwrite the LocalSqlServer connection string and have it point to your new database. You already saw what this connection string looked like earlier in this appendix and in Chapter 19, but here it is again so you can see how it works: ...
Note that the element contains a element. This element is used to clear out all previously defined connection strings, including the LocalSqlServer connection string defined in machine.config. The configuration file then defines a new connection string with the same name, but with a different connection string. In this example the connection string points to an attached database called aspnetdb on a local SQL Express named instance. When the application services need to connect to SQL Server, they find the connection string LocalSqlServer, which then enables them to connect to the SQL Server that you have defined. You should substitute aspnetdb with the name of your own database when you added support for the application services to that database, as explained in the previous section. Overriding the LocalSqlServer connection string is a quick solution to have your ASP.NET web application use a different server. All you need to do is assign a valid Data Source and Initial Catalog. However, this solution brings two disadvantages. First, it feels a bit awkward to have a connection string called LocalSqlServer pointing to a remote server. Secondly, this option doesn’t allow you to completely override the settings for the configured provider. You can avoid both problems by reconfiguring the application services in web.config as you’ll see how to do next.
Overriding the Settings of the Application Services Overriding the settings of the different providers in the web.config file gives you the most flexibility. Not only does it enable you to point the various application services to a new database connection, it also enables you to change the other settings that each provider supports. To override the settings for the Planet Wrox application, you need to give each configured provider (Membership, Role Manager, and Profile) its own <provider> setting. The file machine.config, located at %windir%\Microsoft.NET\Framework\v4.0.30128\Config gives you an example of how the different providers need to look. Remember, your exact path may look slightly different. If you want to override all three providers for the Planet Wrox application so they use the standard PlanetWroxConnectionString1 instead of the default LocalSqlServer, you need to add the following configuration information to the web.config file. When you add these settings, make sure you overwrite all current settings that are already present in the file. In order for this to work you
774 ❘ Appendix B Configuring SQL Server 2008
need to configure the PlanetWrox database for the application services using aspnet_regsql.exe as explained earlier. <membership> <providers> <profile> <providers> <properties> <providers>
Configuring Application Services ❘ 775
Each application service has a <providers> section that enables you to define one or more providers for the service. The element is used once again to remove any previously defined provider from the configuration hierarchy. Each provider is then added again using an element. Note that each configuration section uses connectionStringName=”PlanetWroxConnectionString1” to point to the custom connection string defined in the same web.config file. Also note that you can change any of the other settings that the provider supports. All the provider configuration settings have an attribute called applicationName which is set to /. This is the name that each application uses to look in the database for the requested users, roles, and so on. You can make up another name if you like, but it’s important that all configured providers use the same applicationName or they’ll end up looking for the wrong information. Without a matching applicationName, the services won’t be able to relate records from one service to another. This in turn leads to a mismatch among users, roles, and their profile in the database. With these reconfigured application services, you no longer need the LocalSqlServer connection string you may have added to web.config earlier in this appendix or in Chapter 19. After you have saved the modified web.config, your site should continue to run as normal. However, the application services now make use of the PlanetWrox database, instead of a separate aspnetdb database.
App_Themes folder, 218, 219, 220, 221, 222, 224, 342, 535, 536 arithmetic operators, 152–154 arrays, 146–148 collections v., 148–149 as keyword, 146, 570, 574 asp: prefix, 14, 106 ASP (classic ASP, Active Server Pages), 1, 106, 129, 132, 139, 346 ASP.NET 2.0 Instant Results (Spaanjaars, ISBN: 9780-471-74951-6), 526, 604 ASP.NET 4 AJAX, 331–365 books Beginning ASP.NET MVC 1.0, 35 Professional ASP.NET 2.0 Design: CSS, Themes, and Master Pages, 86, 728 Professional ASP.NET 3.5 AJAX, 333, 338, 362, 728 Professional ASP.NET 3.5 Security, Membership, and Role Management with C# and VB, 613 Professional ASP.NET 4 in C# and VB, 125, 126, 132, 351, 363, 436, 505, 555, 565, 566, 568, 576, 604, 672, 728 earlier versions ASP.NET 1.0, 1–2, 581 ASP.NET 2.0, 2, 86, 127, 128, 198, 249, 526, 581, 604, 728 ASP.NET 3.5, 2, 333, 338, 434, 613 event-driven environment, 192 history, 1–2 introduction, 9–15 markup, 14–15. See also server controls Profile feature, 619–653 server controls. See server controls user controls, 271–296 ASP.NET application services. See application services ASP.NET page life cycle, 208–210, 238 control life cycle and, 540–545 data control events and, 545–549 ASP.NET pages. See ASPX pages ASP.NET runtime, 10 ASP.NET SQL Server Setup Wizard, 771 ASP.NET state engine, 128–135, 136 ASP.NET styles, 530–539, 577 ASP.NET Web Service (template), 36 ASP.NET Web Site (template), 36 ASP.NET web sites. See Planet Wrox; web sites
ASP.NET WebParts – Breakpoints Window
ASP.NET WebParts, 128 aspnetdb.mdf database, 585, 607, 626, 722, 726, 757, 762, 767, 769, 770, 771, 772, 773, 775 aspnet_regsql.exe, 771, 772, 773 .aspx files. See Web Forms ASPX pages (ASP.NET pages), 8. See also consistency; Web Forms page life cycle, 208–210, 238 user controls v., 271, 272 assemblies (.NET), 176, 177, 179, 216, 709, 710, 730 assignment operators, 152 AssociatedControlID, 556, 561, 567, 629, 631 AssociatedUpdatePanelID, 340 Asynchronous JavaScript And XML. See Ajax AsyncPostBackErrorMessage, 339 @ Register directive, 276, 279, 280, 296 Attachment class, 317 Attention class, 75, 102, 396 [attribute], 381 attributes, 31, 347, 365. See also specific attributes HTML, 12 ID, 106 media, 84 Runat, 106, 110, 111, 126 Src, 276 style, 12, 60, 85, 99, 101, 104, 112, 235, 508, 523, 534, 743 TagName, 276 TagPrefix, 276 ValidateRequest, 316 [attribute=value], 381 authentication defined, 580, 618 Forms authentication, 617, 717 SQL Server Authentication, 758, 759, 764–768 third-party, 617 Windows Authentication, 758–759, 768–770 authorization, 580–581, 618 authorization rules, 580 Auto Format feature, 532–533 AutoID (ClientIDMode), 282 automatic paging, 434 automatic properties, 185, 285, 286, 623, 738 AutoPostBack property, 115, 119, 120, 227, 229, 447, 451, 521, 524, 734 Autos Window, 679, 680–684 Average (operator), 488–489 avoiding hardcoded settings, 698 inline style sheets, 112
.NET Framework. See also ASP.NET 4 data types, 141. See also data types history, 1–2, 30 IL, 140 LINQ and. See LINQ runtime, 10, 140 Network Service, 446, 715, 717, 720, 762, 768 never trust user input, 299 New keyword, 176, 182, 187, 188, 358, 491, 527, 746 New Web Site dialog box, 5, 19, 34, 35 non-standard data types, 623–624 NOT, 158 not equals (‹›), 155, 409 ntext / text, 422 NTFS settings, for Planet Wrox, 718–721 nvarchar, 422
range variables, 528 ranges for, 143–144 VerticalAlign, 532 Vieira, Robert, 415 View State, 133, 137 considerations, 294 performance and, 134–135, 136 properties, 288–293, 296 server controls and, 133 turning off, 134–135, 137 Wizard control and, 124 views (Web Forms), 47–49. See also specific views _VIEWSTATE, 132, 133, 134, 137, 292, 336, 734 ViewState collection, 296 ViewStateMode, 134–135 virtual path, 327, 498, 691 Virtual Path property, 224, 242–244 visibility (CSS property), 76 Visible (server control property), 111 VisibleWhenLoggedIn, 587 :visited, 89, 90, 92 Visual Aids, 48, 58, 277 Visual Studio 2005, 2, 34, 35 Visual Studio 2010, 2, 3, 4, 15, 18, 31, 41, 217, 436, 684. See also Visual Web Developer 2010 Visual Web Developer 2010 (VWD), 2–5, 31 CSS in, 87–101 downloading, 3 IDE, 15–27, 31 customizing, 22–27, 30 Database Explorer, 18 developer profiles, 16 Document Window, 18–19 main menu, 16 Properties Grid, 18 restoring/resetting, 26–27 Solution Explorer, 17 Start Page, 19 toolbar area, 16–17 Toolbox, 17 installing (Express version), 3–5 Options dialog box, 27, 30, 233, 234 Table Designer, 423–426 tips for, 29–30 Visual Studio 2010 and, 2, 3, 4, 15, 18, 41, 217, 436, 684 web service creation, 347–351 web site creation, 5–9 Vogel, Peter, 128 vsdoc file, 370, 372, 373, 393 VWD. See Visual Web Developer 2010
801
W3C – Wrox
W W3C (World Wide Web Consortium), 12, 14, 68 WAPs. See Web Application Projects Watch Window, 678–684 Watermark plugin, 397–400 watermarking, 386–390, 402 Watt, Andrew, 306 WCF Service (template), 37 Web Application Projects (WAPs), 35 App_Code folder and, 173 File ÞNew Project and, 35, 38 Profile feature and, 621 web packaging and, 706 WSPs v., 706 web applications application services and, 581–582 configuring, 599–603 roles and, 608–612 web files, 41–43 Web Forms (.aspx files), 47–63 Code Behind and, 10, 49–54, 62, 64, 68, 179, 200 inline code and, 49–54, 62, 64, 68, 179, 200 tips for, 62–63 views on, 47–49 web packaging, 706 web pages. See ASPX pages; consistency; page life cycle web server errors, 721–723 web services, 346–358, 365 configuring, 351 creating, 347–351 defined, 346–347 external, 351 “Hello World,” 348–351 ScriptManager and, 351–352 ScriptManagerProxy and, 352–358 using, 351–358 Web Site Administration Tool (WSAT), 604–608, 617 Web Site Projects (WSPs), 34–35 App-Code folder and, 173 File ÞNew Web Site and, 19, 34, 35, 38, 42, 242 Profile feature and, 621 WAPs v., 706 web site templates, 35–37, 64 web sites (ASP.NET). See also Planet Wrox creation, 34–40 deployment. See deployment files/file types, 40–47. See also Web Forms adding existing files, 44–47 code files, 43 data files, 43–44
802
organizing, 46 special file types, 47 web files, 41–43 first, 5–9 opening, 39–40 Planet Wrox, 38–40 tracing for, 689–691 web-based calculator. See calculator web.config file connection strings stored in, 445–446, 473 deployment and, 699, 701–705 registering user controls in, 279–280 WebConfigurationManager class, 700–705 WebParts, 128 Web.sitemap, 246–248 Where clause (LINQ), 488 WHERE clause (SQL), 408–410, 431 Where property (EntityDataSource), 497 While loop, 168–170 width CSS property, 76 server control property, 111, 532 windows. See debugging windows; specific windows Windows Authentication, 758–759, 768–770 IIS and, 768–770 SQL Server Authentication v., 759 wiring syntax, 193 Wizard control, 118, 119, 120–121 exercise, 121–124 state engine and, 128 View State and, 124 World Wide Web Consortium (W3C), 12, 14, 68 Wrap, 532 WriteAllText (File class), 323 WriteOnly, 186 write-only properties, 186 Wrox ASP.NET 2.0 Instant Results, 526, 604 Beginning CSS: Cascading Style Sheets for Web Design, Second Edition, 76, 86 Beginning Microsoft SQL Server 2008 Administration, 764 Beginning Microsoft SQL Server 2008 Programming, 415 Beginning Microsoft Visual Basic 2010, 140 Beginning Microsoft Visual C# 2010, 140 Beginning Regular Expressions, 306 Beginning Web Programming with HTML, XHTML, and CSS, 2nd Edition, 14 Professional ADO.NET 2, 555
WSAT – zero-based
Professional ASP.NET 2.0 Design: CSS, Themes, and Master Pages, 86, 728 Professional ASP.NET 3.5 AJAX, 333, 338, 362, 728 Professional ASP.NET 3.5 Security, Membership, and Role Management with C# and VB, 613 Professional ASP.NET 4 in C# and VB, 125, 126, 132, 351, 363, 436, 505, 555, 565, 566, 568, 576, 604, 672, 728 Professional CSS: Cascading Style Sheets for Web Design, 2nd Edition, 728 Professional IIS 7, 728 Professional JavaScript for Web Developers, 2nd Edition, 303 Professional LINQ, 728 Professional Microsoft SQL Server 2008 Administration, 764 Professional .NET 2.0 Generics, 151 Professional Search Engine Optimization with ASP. NET: A Developer’s Guide to SEO, 575 Professional WCF Programming: .NET Development with the Windows Communication Foundation, 37 Professional Web Parts and Custom Controls with ASP.NET 2.0, 128 Programming Entity Framework Second Edition, 565 WSAT (Web Site Administration Tool), 604–608, 617
X XHTML, 13–14, 15, 31. See also HTML Beginning Web Programming with HTML, XHTML, and CSS, 2nd Edition, 14 defined, 13–14, 31 HTML v., 13–14, 731 XML control and, 126 XML (eXtensible Markup Language). See also Ajax comments, 180–181 LINQ to XML and, 479 XML control, 126 XmlDataSource, 435
Y Yellow Screen of Death, 554, 658, 662, 670, 695, 750, 755 York, Richard, 76
Z Zakas, Nicholas C., 303 zero-based arrays, 147, 167 index, 114, 147 string indexing, 185
803
Programmer to Programmer
TM
Take your library wherever you go. Now you can access complete Wrox books online, wherever you happen to be! Every diagram, description, screen capture, and code sample is available with your subscription to the Wrox Reference Library. For answers when and where you need them, go to wrox.books24x7.com and subscribe today!
Find books on
• ASP.NET • C#/C++ • Database • Java • Mac • Microsoft Office • .NET
• Open Source • PHP/MySQL • SQL Server • Visual Basic • Web • XML
www.wrox.com
Related Wrox Books Beginning Visual C# 2010 ISBN: 978-0-470-50226-6 Using this book, you will first cover the fundamentals such as variables, flow control, and object-oriented programming and gradually build your skills for Web and Windows programming, Windows forms, and data access. Step-by-step directions walk you through processes and invite you to “Try it Out,” at every stage. By the end, you’ll be able to write useful programming code following the steps you’ve learned in this thorough, practical book. If you’ve always wanted to master Visual C# programming, this book is the perfect one-stop resource.
Professional ASP.NET 4: in C# and VB ISBN: 978-0-470-50220-4 Written by three highly recognized and regarded ASP.NET experts, this book provides all-encompassing coverage on ASP.NET 4 and offers a unique approach of featuring examples in both C# and VB, as is the incomparable coverage of core ASP.NET. After a fast-paced refresher on essentials such as server controls, the book delves into expert coverage of all the latest capabilities of ASP.NET 4. You’ll learn site navigation, personalization, membership, role management, security, and more.
Professional C# 4 and .NET 4 ISBN: 978-0-470-50225-9 After a quick refresher on C# basics, the author dream team moves on to provide you with details of language and framework with C#, working in Visual Studio 2010 with C#, and more. With this book, you’ll quickly get up to date on all the newest capabilities of C# 4.
Professional Visual Basic 2010 and .NET 4 ISBN: 978-0-470-50224-2 If you’ve already covered the basics and want to dive deep into VB and .NET topics that professional programmers use most, this is your guide. You’ll explore all the new features of Visual Basic 2010 as well as all the essential functions that you need, including .NET features such as LINQ to SQL, LINQ to XML, WCF, and more. Plus, you’ll examine exception handling and debugging, Visual Studio features, and ASP.NET web programming.
Professional Visual Studio 2010 ISBN: 978-0-470-54865-3 Written by an author team of veteran programmers and developers, this book gets you quickly up to speed on what you can expect from Visual Studio 2010. Packed with helpful examples, this comprehensive guide examines the features of Visual Studio 2010, and walks you through every facet of the Integrated Development Environment (IDE), from common tasks and functions to its powerful tools.
Visual Basic 2010 Programmer’s Reference ISBN: 978-0-470-49983-2 Visual Basic 2010s Programmer’s Reference is a language tutorial and a reference guide to the 2010 release of Visual Basic. The tutorial provides basic material suitable for beginners but also includes in-depth content for more advanced developers.
Visual Studio 2010 and .NET 4 Six-in-One ISBN: 978-0-470-49948-1 This comprehensive resource offers a single resource for all you need to know to get productive with .NET 4. It reviews all the important features of .NET 4, including .NET charting and ASP.NET charting, ASP.NET dynamic data and jQuery, and F#. The coverage is divided into six distinctive parts for easy navigation and offers a practical approach and complete examples.
WPF Programmer’s Reference: Windows Presentation Foundation with C# 2010 and .NET 4 ISBN: 978-0-470-47722-9 Written by a leading expert on Microsoft graphics programming, this richly illustrated book provides an introduction to WPF development and explains fundamental WPF concepts. It is packed with helpful examples and progresses through a range of topics that gradually increase in their complexity.
Build rich web sites with the latest version of ASP.NET ASP.NET is the part of the .NET Framework that enables you to build rich, dynamic web sites. The latest version—ASP.NET 4—boasts numerous improvements to the previous iteration, including Web Forms enhancements and jQuery support. This step-by-step book walks you through building rich web sites with ASP.NET 4 and is packed with in-depth examples in both C# and VB. With hands-on exercises that take you right into the action, you’ll learn how to build web sites firsthand while you get a good look at what goes on behind the scenes when you view an ASP.NET 4 page in your browser.
Programmer Forums
Beginning ASP.NET 4: •
Demonstrates how to build ASP.NET 4 web pages
•
Explains adding features with pre-built server controls
•
Reviews working with the development tools to create ASP.NET web sites
•
Shares techniques for creating consistent-looking web sites
•
Teaches you how to implement jQuery and AJAX techniques in your web sites
•
Shows you how to work with databases and the Microsoft® ADO.NET Entity Framework
•
Addresses securing and personalizing your site
•
Investigates exception handling, debugging, and tracing pages
Imar Spaanjaars is a Microsoft ASP.NET MVP and runs his own company called De Vier Koeden in the Netherlands, specializing in Internet and intranet applications built with Microsoft technologies like ASP.NET 4. He is the author or coauthor of several books, including ASP.NET 2.0 Instant Results and Beginning ASP.NET 3.5 in C# and VB, and is one of the top contributors to the Wrox Community Forum at p2p.wrox.com. Wrox Beginning guides are crafted to make learning programming languages and technologies easier than you think, providing a structured, tutorial format that will guide you through all the techniques involved.
Web Development/ASP.NET
wrox.com
$44.99 USA $53.99 CAN
Join our Programmer to Programmer forums to ask and answer programming questions about this book, join discussions on the hottest topics in the industry, and connect with fellow programmers from around the world.
Code Downloads Take advantage of free code samples from this book, as well as code samples from hundreds of other books, all ready to use.
Read More Find articles, ebooks, sample chapters, and tables of contents for hundreds of books, and more reference resources on programming topics that matter to you.