HELP
Richard Mansfield
Printing This Lesson
Select what you’d like to include when you print, and then click the Print Lesson button:
Text, images and activities (IE users only)
Text and images
Text only
Saving This Lesson
For instructions on saving this lesson (shown below), please select the browser you're using.
Introduction
Welcome back! CSS style sheets are great for many formatting tasks, but if you need more powerful transformations of raw XML data, XSL (Extensible Style Sheet Language) is the solution.
XSL does everything CSS does—centering, coloring, and otherwise formatting text—and more. It includes techniques such as sorting, creating tables and lists, and inserting literal text and HTML code right into the style sheet itself. But as you probably suspect, there's a price for these extra features:
XSL is harder to learn and use than CSS.
We'll walk through it slowly in this lesson, though. You'll learn to use XSL to transform unprocessed, unformatted XML content from the recipes.xml document into a table or numbered list in IE.
And I'll also show you how to add an important capability to the cookbook program: searching. Users expect to be able to type in a word or phrase and have the computer provide a list of all the recipes containing the search term. Many computer programs include a search feature, and you certainly don't want to leave it out of the cookbook!
Okay, ready to get started? I'll see you in Chapter 2!
Experimenting With Simple XSL Formatting
XSL has several uses that we'll explore in the next three lessons. In this lesson, we'll focus on using XSL's formatting to format XML elements for display in IE.
In some ways, XSL is similar to CSS, which we worked with in the last lesson. Both languages let you specify how to display your XML data to make it look consistent, attractive, and efficient.
But CSS and XSL do differ in important ways:
Essentially, CSS is simpler but less powerful than XSL.
Formatting for paper can often be more demanding than for screen. One primary difference is that you scroll screens but turn pages. So a browser rarely displays such things as headers, footers, and page numbers, but books and magazines usually include them.
Also, typesetting for paper can be more refined and complicated than for screen (hanging indents, for example). XSL includes tools to deal with these paper issues and also for special kinds of screen output.
So when do you use XSL instead of CSS? The W3C—the organization that sets worldwide standards for the Internet—says it this way:
"Use CSS when you can; use XSL when you must."
But even though you'll use CSS for most XML formatting, you should still know the basics of XSL so you'll feel comfortable working with this type of style sheet if you need to.
Let's start with a simple example. We'll display the recipes.xml data as a table in IE.
Formatting With XSL
We'll need to write an XSL style sheet file that (like a CSS file) describes how we want to format our original XML document. Then we'll put a reference to the XSL style sheet in our XML document.
When we open the XML document in a browser, the browser locates the linked XSL file and then displays the XML according to our XSL style sheet's rules.
Okay, to see XSL in action, let's create a style sheet with the following rules:
Follow these steps to link the XSL style sheet to recipes.xml:
<?xml version="1.0" encoding="utf-8"?> <?xml-stylesheet type="text/xsl" href="recipes.xsl"?> <!--These recipes are used with the Cookbook program--> <cookbook> <recipe> <title>The Secret to Perfect Coleslaw</title> <instructions> To prevent the slaw from getting runny or turning grey, mix 1
You should now see your recipes formatted as a table, like this:
It's interesting that a good part of this XSL code is pure HTML. To better understand how you can mix HTML into XSL, let's separate the XSL file's code into two categories. We'll start with the HTML in that file, and then we'll look at the XSL.
Examining the HTML Elements
Here's the HTML part:
<html> <body> <center> <h2>Cookbook Displayed as a Table</h2> </center> <table border="1"> <tr bgcolor="lightblue"> <th>Title</th> <th>Instructions</th> </tr> <tr> <td bgcolor="lightyellow" width="30%"> </td> <td> </td> </tr> </table> </body> </html>
Let's take a quick look at this HTML:
<xsl:for-each select="cookbook/recipe"> <tr> <td bgcolor="lightyellow" width="30%"> <xsl:value-of select="title"/> </td> <td> <xsl:value-of select="instructions"/> </td> </tr> </xsl:for-each>
XSL's for-each element is like the For Each block in VB—it keeps looping (doing something to a set of data) until it finishes with the final item in the set. In this case, the loop goes through the recipes.xml file and displays each recipe's title and instructions.
And because this loop includes the HTML <tr> (table row) element, the browser will automatically create a new row to display each recipe.
Also within the xsl:for-each loop are a pair of <td> (table data) elements. The first one displays the title data and includes two attributes: a light yellow background color and a width of 30% of its container (therefore the instructions will be 70%). The second <td> data element displays the instructions.
All right, now that you have a sense of how HTML works within an XSL style sheet, let's look at the XSL code part of this same style sheet:
Examining the XSL Elements
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <xsl:for-each select="cookbook/recipe"> <xsl:value-of select="title"/> <xsl:value-of select="instructions"/> </xsl:for-each>
<recipe> <title></title> <instructions> </instructions> </recipe>
Tip
Here's another way to think about how this example mixes XSL and HTML in the same style sheet. When the browser processes this code, it renders the HTML as is—just as any HTML code would be. But any XSL instructions (code that begins with xsl:) is processed according to the XSL language rules.
That was some pretty detailed explanation, I know. So let's do a quick review before we move on. Can you match the descriptions to the correct XSL element?
Listing, Sorting, and Formatting With XSL
Now let's experiment with some additional HTML formatting and also try an XSL transform—sorting the recipes.
We'll start by displaying the recipes as a numbered list. To do this, just replace the various <table> elements in the HTML code in the previous chapter with <li> (list) and <ol> (ordered list) elements, like this:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <body> <center> <h2>Cookbook Displayed as a List</h2> </center> <ol> <xsl:for-each select="cookbook/recipe"> <li> <xsl:value-of select="title"/> <xsl:value-of select="instructions"/> </li> </xsl:for-each> </ol> </body> </html> </xsl:template> </xsl:stylesheet>
You should see the recipes listed, like this:
This code is quite straightforward. You enclose your list with the HTML ordered list <ol>. By default, it's a numbered list, but your other options include letters and Roman numerals. Then, around each list item (title and instructions displayed as a unit), you use the <li> tag.
Alphabetizing Your List
As you know, you can almost always improve on your first attempt at formatting. For one thing, recall that the XML document doesn't keep its data alphabetized. Each recipe element is stored just in order of when you added it to the bottom of the document. In VB, alphabetizing was merely a matter of setting the listbox's Sorted property to True.
In XSL, you alphabetize by inserting this code:
<xsl:sort select="title"/>
Make this improvement by adding the highlighted line of code below in the <ol> section of your recipes.xsl file:
<ol> <xsl:for-each select="cookbook/recipe"> <xsl:sort select="title"/> <li><xsl:text>TITLE: </xsl:text> <xsl:value-of select="title"/> <xsl:text> INSTRUCTIONS: </xsl:text> <xsl:value-of select="instructions"/></li> </xsl:for-each> </ol>
Now save this file from Notepad, switch to IE, and press F5 to refresh the page. You should now see the recipes alphabetized, like this:
You might be thinking that using a browser to view XML is similar to using the VB cookbook project. The browser can now alphabetize, for example. And browsers can also search—just press CTRL + F and type salt or some other search term. The browser will highlight all instances of that word.
So yes, a browser can format, sort, and search XML data. But the VB program can do all that and quite a bit more—delete, modify, and even import new recipes from the Windows clipboard.
As you can see in the image above, the title butts up right against the instructions. That doesn't look too great, and it's inefficient too. Users sometimes want to scroll down through the recipes, skipping from title to title, until they find the one they're after. You shouldn't bury the title inside the body text.
So make your next improvement to the formatting by employing the <xsl:text> element to label the titles and instructions, like this:
Use <xsl:text> when you want to insert literal text, like these labels, into a section of xsl code.
That's an improvement, but we can do even better. Try inserting some whitespace using the HTML <p> (paragraph) tag:
<li> <p><xsl:value-of select="title"/> </p> <xsl:text>Instructions: </xsl:text> <xsl:value-of select="instructions"/> </li>
Using whitespace is almost always a wise move. In this example, the whitespace visually isolates the recipes from each other and separates the titles from the instructions. Your XML data is now easy to read and easy to scan. And if you want to emphasize the titles even further, you could add HTML tags like <big> to enlarge the text size or <u> to underline it:
<p><u><xsl:value-of select="title"/></u></p>
Let's Chat!
How do you feel about whitespace? Do you think it clarifies text or just takes up room? Do you agree that most people find whitespace makes reading easier for them?
Join me and your classmates in the Discussion Area to share your opinion.
Because we'll continue to work with the recipes.xml document in future lessons, let's remove the style sheet link to restore the file to its original state:
<?xml version="1.0" encoding="utf-8"?> <?xml-stylesheet type="text/xsl" href="recipes.xsl"?> <!--These recipes are used with the Cookbook program--> <cookbook>
At this point, you should have a good idea how to mix HTML with XSL to create the presentation you're after when displaying XML in browsers.
Downloading All Projects for This Course
Let's now download all of the projects for the rest of this course. I'll explain why in a minute. Click this button to start the download of a self-extracting Zip file named L5 thru L12 Finished.zip:
Now follow these steps to save the folders you used in this course to your C:\drive:
You should now see eight new XML Finished folders in your C:\ drive, one for each lesson from L5 to L12:
These folders contain the finished projects as they are at the end of each lesson.
Why did we download all these projects? From now on, you'll have a choice in how you want to work adding features to the cookbook and other projects. You can build the projects by hand, as described in each lesson—this will improve your skills using the editor's Design, Code, and Properties windows. Or if you don't need practice working with the editor, you can just follow along using the projects that I already created (you'll find these in the folders for each lesson you just downloaded—XML L5 Finished, XML L6 Finished, and so on.)
For example, in the next chapter, we'll add a search feature to the cookbook project. This requires adding controls in the VB editor's Design window, entering code in the Code window, and modifying properties in the Properties window. If you feel that you've already mastered these editor techniques, you can simply skip these editor activities and use the finished projects instead. Just study the concepts to learn how the new search feature is implemented. If you want to take this approach in this lesson now, double-click the cookbook.sln file in the XML L5 Finished folder that you just downloaded. This opens the cookbook project as it will be at the end of this lesson.
But if you do want to practice working hands-on with the editor, double-click the cookbook.sln file in the XML Projects folder that you worked with in previous lessons.
Okay, now let's add a search feature!
Adding a Search Feature to the Cookbook
What good is a database if you can't search it? One of the most powerful aspects of computing is how quickly you can get information. You don't have to find a book, look in its index, and then track down a page number. Just type your search term into a program, and instantly see the results.
To add a search feature to the cookbook, you first need to add a textbox where the user can type in what he or she is searching for:
Now let's add a button to the form. (You'll use this button in the next lesson, so don't worry about its code for now.)
Your cookbook should now look like this:
Now we'll add the code that searches your recipes.xml document:
Okay, now let's take a few minutes to break down that code.
Let's take a look and see how our code searches through the recipes file. KeyPress is a new event that you haven't yet worked with. It's triggered every time the user types in a key on the keyboard. Let's say that the user wants to find every recipe containing the word "Italian." The KeyPress event will trigger seven times, once for each letter in that word.
But this sub is testing to see if the user pressed the Enter key, and that happens right here. It means if the key character pressed is Enter, then do everything here in between the If and the End If. In other words, carry out these instructions. However, if the key pressed was I or T or some other letter, then you don't do any of these things. You simply exit the sub.
But let's say the user did press the Enter key and triggers all this code here. This code begins with this command here, telling the program to clear out or empty the list titles, that textbox containing all of the recipe titles, because we're only going to fill it with recipes that match the search term "Italian."
Next, the code creates an XML node reader, which is a special kind of object that will contain a copy of the entire recipes.xml document. By the way, we get this entire list of recipes from the doc object that we created up in the form load sub earlier.
Now we have another block of code: While, End While. That's a loop, meaning that it continually repeats until some condition is satisfied. The condition, in this case, is as long as the reader can read, or in other words, as long as it hasn't reached the end of the recipes file.
We use the ReadElementContentAsString method twice here. The first time it extracts the title from a given element pair, and then it extracts the instructions from the same element pair. The ampersand command right here joins the title to the recipe or instructions and puts them in a variable we call WholeRecipe, right here.
Notice that the XML node reader keeps track of where you are currently located when in all the recipes. In other words, every time the reader executes its ReadElementContentAsString method, then it moves down one element in the file. So the first time it executes ReadElementContentAsString, it fetches the contents of the title element. And then the second time that it's executed right here, it fetches the instructions. That's pretty handy. It allows you to not worry about keeping track of where you are. It's done for you.
The code next uses the Visual Basic InStr command, and it looks inside a particular string variable and sees if the user typed into the search box, sees if it's located within the whole recipe. Put another way, it says, if in WholeRecipe variable, you find the text from the text search . . . If in this particular recipe we're looking at, you find the word "Italian," then do these things within the If, End If block.
END TRANSCRIPT
Let's pick up where we left off in the previous lesson and take a look at the rest of the code that searches for us. We're looking at one recipe now, and we want to find out if the user's search term is located within the current recipe that we're looking at.
We'll use the InStr command to see if this term "Italian," in our example, is located within this recipe. InStr means, is this string found within this other string? The answer is either yes or no, and if it's no, the code will skip past this "If, End If" block and fetch the next recipe in the file.
The InStr command is case-sensitive. It doesn't consider the word "Sugar" with a capital S matched to the word "sugar" with a small S. That's why you need to use this LCase command. LCase forces all the text into lowercase letters, so we're comparing the words, not their capitalization.
If we do find a match, then we add that recipe's title to our listbox. We set a variable name Found to true, which we'll explain later, and we empty the contents of our two text variables. It's always a good idea at the end of a loop to reset your variables. By using double quotes you can reset a text variable to empty or by adding a zero into a numeric variable, you set it to empty. You do this because if you don't empty the contents in a loop, you can get some strange and unpredictable results. Just remember it's a good practice to set everything empty at the end of a loop.
So this loop continues fetching each recipe from the file and looks to see if the word "Italian" is in any of them, in which case, it builds a list of all the hits, and the listbox will then only contain those recipes with the word "Italian" in them. Once we've finished searching all the recipes, we check to see if the variable Found contains true, which means that at least one recipe contained the word "Italian."
Notice this "If, Else, End If" structure. You can pretty much tell what it means by the English words. If we do find a hit, then do these things, and if we don't, Else, then do these other things. So let's assume we did find a hit, and what we do then is simulate the user clicking the first recipe in the listbox. This has the effect of highlighting the first recipe, and also displaying that recipe's instructions in the instructions textbox. Then we color the listbox blue. This alerts the user that the listbox is now different. It contains only the search hits, not all the recipes in the cookbook as it normally does when the program starts.
But if we didn't find any hits, if the word "Italian" appeared nowhere, then we use a button that we'll add in a future lesson, and you'll see that that button restores the listbox to its normal state of showing all the titles. And we also display a message to the user letting them know that there were no hits. And that's our search.
Sometimes You Need to Empty Variables
You might have wondered if it's necessary to empty the RecipeTitle and WholeRecipe variables by assigning empty strings to them (" "). After all, doesn't the program assign new text to them every time the ReadElementContentAsString method executes?
Not quite. Blanking variables is important, particularly in loops when you add strings together. If you skip this step, strange behavior might ensue. It never hurts to start fresh with variables like this. If you want to see what happens when you don't empty these variables here in this sub—temporarily "comment out" (skip these lines of code during testing), by adding the single-quote comment punctuation ( ' ) at the start of each line, like this:
'RecipeTitle = "" 'WholeRecipe = ""
VB will now skip over these two lines of code, because it ignores all comments. So you can test the program without these lines executing. Now press F5, and type in three or four different search terms, pressing the ENTER key after each term. You'll see random multiple titles listed. Be sure to remove the single-quote comment symbols when you're done experimenting. Those two lines of code are useful and the program should execute them.
Back to Our Search
If the search was successful, you simulate the user clicking the first title in the listbox (this highlights it and also displays that recipe's instructions):
lstTitles.SelectedIndex = 0 Call lstTitles_Click(Me, e) lstTitles.BackColor = Color.CornflowerBlue
Finally, the listbox is colored CornflowerBlue. This alerts the user that the listbox is now different. It currently contains only the search hits, not all the recipes in the cookbook as it does when the program starts.
But if the search turned up no matching recipes, then a message box alerts the user. This is where the Else command comes into play.
Else btnShowAllTitles_Click(Me, e) MsgBox("Nothing matched your search term " & txtSearch.Text) End If
Also, the program "clicks" the Show All Titles button. Its purpose is to restore the list of all recipe titles and change the listbox's color back to white.
When the program first runs, the user often wants to do a search right off the bat. He or she wants to start the cookbook and begin typing a search item right away. But by default, the form has the focus, so it's the object that responds to any typing. You want the search textbox to have the focus instead, so the user doesn't need to take the extra step of clicking to select the textbox.
So your task to make the textbox the currently selected control—as if the user had clicked it, placing the blinking insertion cursor inside the textbox.
Go to the Form_Load event, and type txtSearch. on a line just below this Form_Load line:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
As soon as you type that period after txtSearch, you see a list of all the commands that can be used with a textbox. See if you can figure out which command gives the focus to the textbox.
Summary
Congratulations! We covered quite a bit of territory today. You learned how to write an XSL style sheet file, link it to the raw XML data in the recipes.xml file, and then display our recipes in a table. You also learned how to display them in a numbered list and alphabetize them. And you explored how to embed HTML code within an XSL style sheet and how to use the <xsl:text> element to insert literal text into the style sheet.
We added some sophisticated VB code that searches all the recipes for a particular word or phrase in our cookbook. This is an important feature because the user might want to see a list of the recipes that use ham, for example, because that's what's in the fridge. Or a user might be in the mood for Chinese food. Chances are, users will probably use the search feature quite often!
In the next lesson, you'll meet two more Xs: XPath and XQuery—languages you can use to navigate XML. But first, be sure to check out the Supplementary Material links and FAQs for this lesson, and complete the assignment and quiz as well.
See you next time!
Here you'll find the HTML elements, their attributes, and some example code.
Q: Why would you want to mix HTML code into XSL code in an XSL style sheet?
A: Good question! When formatting raw XML data, you sometimes want to use HTML elements to ensure that the data looks the way you want it to. In this lesson, you used HTML code to turn your unformatted recipes into a table and a list. HTML elements efficiently transformed your data into both formats.
Q: You suggest that we might want to skip the steps of working with the VB Design and Code windows to add the search feature. Who should skip these steps?
A: If you already feel comfortable adding code to the editor, using the Toolbox to add a control, and positioning and aligning controls, you might prefer to skip these steps. You've done these tasks by hand in previous lessons, but your comfort level should determine whether you skip them in this and future lessons. But if you have the time, you might find it helpful to get more practice using the editor!
Your assignment for this lesson is to edit the HTML code below, adding HTML tags <ol> and <li> to display the planet names as a numbered list. The list won't be alphabetized, just numbered.
1. Copy this HTML code into Notepad:
<html> <body> Mars Venus Saturn Neptune </body> </html>
2. Now insert <ol> and <li> tags where needed to create a numbered list.
3. Press CTRL + S to save the file, and name it simple.htm.
4. Use Windows Explorer to locate simple.htm, and double-click the filename. Your browser opens, displaying this numbered list:
Back to top