Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp 2005 Recipes (2006) [eng]

.pdf
Скачиваний:
48
Добавлен:
16.08.2013
Размер:
4.04 Mб
Скачать

208 C H A P T E R 6 X M L P R O C E S S I N G

The xsd.exe utility is included with the .NET Framework. If you have installed Microsoft Visual Studio .NET, you will find it in a directory like C:\Program Files\Microsoft Visual Studio .NET\ FrameworkSDK\Bin. The xsd.exe utility can generate schema documents from compiled assemblies. You simply need to supply the filename and indicate the class that represents the XML document with the / t:[TypeName] parameter.

Usage

For example, consider the ProductCatalog and Product classes shown in recipe 6-9. You could create the XML schema for a product catalog with the following command line:

xsd Recipe6-09.exe /t:ProductCatalog

You need to specify only the ProductCatalog class on the command line because this class represents the actual XML document. The generated schema in this example will represent a complete product catalog, with contained product items. It will be given the default filename schema0.xsd.

You can now use the validation technique shown in recipe 6-8 to test whether the XML document can be successfully validated with the schema.

6-11. Generate a Class from a Schema

Problem

You need to create one or more C# classes based on an XML schema. You can then create an XML document in the appropriate format using these objects and the XmlSerializer.

Solution

Use the xsd.exe command-line utility included with the .NET Framework. Specify the name of your schema file as a command-line argument, and add the /c parameter to indicate you want to generate class code.

How It Works

Recipe 6-10 introduced the xsd.exe command-line utility, which you can use to generate schemas based on class definitions. The reverse operation—generating C# source code based on an XML schema document—is also possible. This is primarily useful if you want to write a certain format of XML document but you do not want to manually create the document by writing individual nodes with the XmlDocument class or the XmlWriter class. Instead, by using xsd.exe, you can generate a set of full .NET objects. You can then serialize these objects to the required XML representation using the XmlSerializer, as described in recipe 6-9.

To generate source code from a schema, you simply need to supply the filename of the schema document and add the /c parameter to indicate you want to generate the required classes.

Usage

For example, consider the schema shown in recipe 6-8. You can generate C# code for this schema with the following command line:

xsd ProductCatalog.xsd /c

C H A P T E R 6 X M L P R O C E S S I N G

209

This will generate one file (ProductCatalog.cs) with two classes: Product and ProductCalalog. These classes are similar to the ones created in recipe 6-9, except that the class member names match the XML document exactly. Optionally, you can add the /f parameter. If you do, the generated classes will be composed of public fields. If you do not, the generated classes will use public properties instead (which simply wrap private fields).

6-12. Perform an XSL Transform

Problem

You need to transform an XML document into another document using an XSLT stylesheet.

Solution

Use the System.Xml.Xsl.XslCompiledTransform class. Load the XSLT stylesheet using the XslCompiledTransform.Load method, and generate the output document by using the Transform method and supplying a source document.

How It Works

XSLT (or XSL transforms) is an XML-based language designed to transform one XML document into another document. You can use XSLT to create a new XML document with the same data but arranged in a different structure or to select a subset of the data in a document. You can also use it to create

a different type of structured document. XSLT is commonly used in this manner to format an XML document into an HTML page.

The Code

XSLT is a rich language, and creating XSL transforms is beyond the scope of this book. However, you can learn how to create simple XSLT documents by looking at a basic example. This recipe transforms the orders.xml document shown in recipe 6-6 into an HTML document with a table and then displays the results. To perform this transformation, you’ll need the following XSLT stylesheet:

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >

<xsl:template match="Order"> <html><body><p>

Order <b><xsl:value-of select="Client/@id"/></b> for <xsl:value-of select="Client/Name"/></p> <table border="1"> <td>ID</td><td>Name</td><td>Price</td> <xsl:apply-templates select="Items/Item"/> </table></body></html>

</xsl:template>

<xsl:template match="Items/Item"> <tr>

<td><xsl:value-of select="@id"/></td> <td><xsl:value-of select="Name"/></td>

210 C H A P T E R 6 X M L P R O C E S S I N G

<td><xsl:value-of select="Price"/></td> </tr>

</xsl:template>

</xsl:stylesheet>

Essentially, every XSL stylesheet consists of a set of templates. Each template matches some set of elements in the source document and then describes the contribution that the matched element will make to the resulting document. To match the template, the XSLT document uses XPath expressions, as described in recipe 6-6.

The orders.xslt stylesheet contains two template elements (as children of the root stylesheet element). The first template matches the root Order element. When the XSLT processor finds an Order element, it outputs the tags necessary to start an HTML table with appropriate column headings and inserts some data about the client using the value-of command, which outputs the text result of an XPath expression. In this case, the XPath expressions (Client/@id and Client/Name) match the id attribute and the Name element.

Next the apply-templates command branches off and performs processing of any contained Item elements. This is required because there might be multiple Item elements. Each Item element is matched using the XPath expression Items/Item. The root Order node is not specified because Order is the current node. Finally, the initial template writes the tags necessary to end the HTML document.

If you execute this transform on the sample orders.xml file shown in recipe 6-6, you will end up with the following HTML document:

<html>

<body>

<p>

Order <b>ROS-930252034</b>

for Remarkable Office Supplies</p> <table border="1">

<td>ID</td>

<td>Name</td>

<td>Price</td>

<tr>

<td>1001</td>

<td>Electronic Protractor</td> <td>42.99</td>

</tr>

<tr>

<td>1002</td> <td>Invisible Ink</td> <td>200.25</td>

</tr>

</table>

</body>

</html>

To apply an XSLT stylesheet in .NET, you use the XslCompiledTransform class. (Do not confuse this class with the similar XslTransform class—it still works but is deprecated in .NET 2.0.)

The following code shows a Windows-based application that programmatically applies the transformation and then displays the transformed file in a window using the WebBrowser control:

using System;

using System.Windows.Forms; using System.Xml.Xsl;

C H A P T E R 6 X M L P R O C E S S I N G

211

namespace Apress.VisualCSharpRecipes.Chapter06

{

public partial class Recipe06_12 : System.Windows.Forms.Form

{

private void TransformXml_Load(object sender, System.EventArgs e)

{

XslCompiledTransform transform = new XslCompiledTransform();

//Load the XSL stylesheet. transform.Load("orders.xslt");

//Transform orders.xml into orders.html using orders.xslt. transform.Transform("orders.xml", "orders.html");

webBrowser.Navigate(Application.StartupPath + @"\orders.html");

}

}

}

Figure 6-2 shows the application.

Figure 6-2. The stylesheet output for orders.xml

In this example, the code uses the overloaded version of the Transform method that saves the result document directly to disk, although you could receive it as a stream and process it inside your application instead. The following code shows an alternate approach that keeps the document content in memory at all times (with no external results file). The XslCompiledTransform writes the results to an XmlWriter that wraps a StringBuilder. The content is then copied from the StringBuilder into the WebBrowser through the handy WebBrowser.DocumentText property. The results are identical.

StringBuilder htmlContent = new StringBuilder(); XmlWriter results = XmlWriter.Create(htmlContent); transform.Transform("orders.xml", results); webBrowser1.DocumentText = htmlContent.ToString();

C H A P T E R 7

■ ■ ■

Windows Forms

The Microsoft .NET Framework includes a rich set of classes for creating traditional Windows-based applications in the System.Windows.Forms namespace. These range from basic controls such as the TextBox, Button, and MainMenu classes to specialized controls such as TreeView, LinkLabel, and NotifyIcon. In addition, you will find all the tools you need to manage Multiple Document Interface (MDI) applications, integrate context-sensitive help, and even create multilingual user interfaces— all without needing to resort to the complexities of the Win32 API.

Most C# developers quickly find themselves at home with the Windows Forms programming model. This chapter offers a number of tips and timesaving techniques that can make your Windows programming endeavors even more productive.

Note Most of the recipes in this chapter use control classes, which are defined in the System.Windows.Forms namespace. When introducing these classes, the full namespace name is not indicated, and System.Windows.Forms is assumed.

The recipes in this chapter describe how to do the following:

Add controls to a form programmatically at runtime so that you can build forms dynamically instead of only building static forms in the Visual Studio forms designer (recipe 7-1)

Link arbitrary data objects to controls to provide an easy way to associate data with a control without the need to maintain additional data structures (recipe 7-2)

Process all the controls on a form in a generic way (recipe 7-3)

Track all the forms and MDI forms in an application (recipes 7-4 and 7-5)

Save user-based and computer-based configuration information for Windows Forms applications using the mechanisms built into the .NET Framework and Windows (recipe 7-6)

Force a list box to always display the most recently added item, so that users do not need to scroll up and down to find it (recipe 7-7)

Assist input validation by restricting what data a user can enter into a textbox, and implement a component-based mechanism for validating user input and reporting errors (recipes 7-8 and 7-17)

Implement a custom autocomplete combo box so that you can make suggests for completing words as users type data (recipe 7-9)

Allow users to sort a list view based on the values in any column (recipe 7-10)

Avoid the need to explicitly lay out controls on a form by using the Windows Forms layout controls (recipe 7-11)

213

214C H A P T E R 7 W I N D O W S F O R M S

Use part of a main menu in a context menu (recipe 7-12)

Provide multilingual support in your Windows Forms application (recipe 7-13)

Create forms that cannot be moved and create borderless forms that can be moved (recipes 7-14 and 7-15)

Create an animated system tray icon for your application (recipe 7-16)

Support drag-and-drop functionality in your Windows Forms application (recipe 7-18)

Provide context-sensitive help to the users of your Windows Forms application (recipe 7-19)

Display Web-based information within your Windows application and allow users to browse the Web from within your application (recipe 7-20)

Note Visual Studio, with its advanced design and editing capabilities, provides the easiest and most productive way to develop Windows Forms applications. Therefore, the recipes in this chapter—unlike those in most other chapters—rely heavily on the use of Visual Studio. Instead of focusing on the library classes that provide the required functionality, or looking at the code generated by Visual Studio, these recipes focus on how to achieve the recipe’s goal using the Visual Studio user interface and the code that you must write manually to complete the required functionality. The separation of generated and manual code is particularly elegant in Visual Studio 2005 due to the extensive use it makes of partial types.

7-1. Add a Control Programmatically

Problem

You need to add a control to a form at runtime, not design time.

Solution

Create an instance of the appropriate control class. Then add the control object to a form or a container control by calling Controls.Add on the container. (The container’s Controls property returns a ControlCollection instance.)

How It Works

In a .NET form-based application, there is really no difference between creating a control at design time and creating it at runtime. When you create controls at design time (using a tool like Microsoft Visual Studio), the necessary code is added to your form class, typically in a special method named InitializeComponent. In .NET Framework 2.0, Visual Studio will also place this code in a separate source file using the partial type functionality. You can use the same code in your application to create controls on the fly. Just follow these steps:

1.Create an instance of the appropriate control class.

2.Configure the control properties accordingly (particularly the size and position coordinates).

3.Add the control to the form or another container. Every control implements a read-only Controls property that references a ControlCollection containing references to all of its child controls. To add a child control, invoke the ControlCollection.Add method.

4.If you need to handle the events for the new control, you can wire them up to existing methods.

C H A P T E R 7 W I N D O W S F O R M S

215

If you need to add multiple controls to a form or container, you should call SuspendLayout on the parent control before adding the dynamic controls, and then call ResumeLayout once you have finished. This temporarily disables the layout logic used to position controls and will allow you to avoid significant performance overheads and weird flickering if many controls are being added.

The Code

The following example demonstrates the dynamic creation of a list of checkboxes. One checkbox is added for each item in a string array. All the checkboxes are added to a panel that has its AutoScroll property set to true, which gives basic scrolling support to the checkbox list.

using System;

using System.Windows.Forms;

namespace Apress.VisualCSharpRecipes.Chapter07

{

public partial class Recipe07_01 : Form

{

public Recipe07_01()

{

//Initialization code is designer generated and contained

//in a separate file using the C# 2.0 support for partial

//classes.

InitializeComponent();

}

protected override void OnLoad(EventArgs e)

{

//Call the OnLoad method of the base class to ensure the Load

//event is raised correctly.

base.OnLoad(e);

//Create an array of strings to use as the labels for

//the dynamic checkboxes.

string[] foods = {"Grain", "Bread", "Beans", "Eggs", "Chicken", "Milk", "Fruit", "Vegetables", "Pasta", "Rice", "Fish", "Beef"};

//Suspend the form's layout logic while multiple controls

//are added.

this.SuspendLayout();

//Specify the Y coordinate of the topmost checkbox in the list. int topPosition = 10;

//Create one new checkbox for each name in the list of

//food types.

foreach (string food in foods)

{

// Create a new checkbox.

CheckBox checkBox = new CheckBox();

// Configure the new checkbox. checkBox.Top = topPosition; checkBox.Left = 10; checkBox.Text = food;

216C H A P T E R 7 W I N D O W S F O R M S

//Set the Y coordinate of the next checkbox. topPosition += 30;

//Add the checkbox to the panel contained by the form. panel1.Controls.Add(checkBox);

}

//Resume the form's layout logic now that all controls

//have been added.

this.ResumeLayout();

}

[STAThread]

public static void Main(string[] args)

{

Application.Run(new Recipe07_01());

}

}

}

Usage

Figure 7-1 shows how the example will look when run.

Figure 7-1. A dynamically generated checkbox list

7-2. Link Data to a Control

Problem

You need to link an object to a specific control (perhaps to store some arbitrary information that relates to a given display item).

C H A P T E R 7 W I N D O W S F O R M S

217

Solution

Store a reference to the object in the Tag property of the control.

How It Works

Every class that derives from Control inherits a Tag property. The Tag property is not used by the control or the .NET Framework. Instead, it’s reserved as a convenient storage place for applicationspecific information. In addition, some other classes not derived from Control also provide a Tag property. Useful examples include the ListViewItem, TreeNode, and MenuItem classes.

Because the Tag property is defined as an Object type, you can use it to store any value type or reference type, from a simple number or string to a custom object you have defined. When retrieving data from the Tag property, you must cast the Object to the correct type before use.

The Code

The following example adds a list of filenames (as ListViewItem objects) to a ListView control. The corresponding System.IO.FileInfo object for each file is stored in the Tag property of its respective ListViewItem. When a user double-clicks one of the filenames, the code retrieves the FileInfo object from the Tag property and displays the filename and size using the MessageBox static method Show.

using System; using System.IO;

using System.Windows.Forms;

namespace Apress.VisualCSharpRecipes.Chapter07

{

public partial class Recipe07_02 : Form

{

public Recipe07_02()

{

//Initialization code is designer generated and contained

//in a separate file using the C# 2.0 support for partial

//classes.

InitializeComponent();

}

protected override void OnLoad(EventArgs e)

{

//Call the OnLoad method of the base class to ensure the Load

//event is raised correctly.

base.OnLoad(e);

//Get all the files in the root directory. DirectoryInfo directory = new DirectoryInfo(@"C:\"); FileInfo[] files = directory.GetFiles();

//Display the name of each file in the ListView. foreach (FileInfo file in files)

{

ListViewItem item = listView1.Items.Add(file.Name); item.ImageIndex = 0;

//Associate each FileInfo object with its ListViewItem.

item.Tag = file;

}

}

Соседние файлы в предмете Программирование на C++