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

Pro CSharp And The .NET 2.0 Platform (2005) [eng]

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

754 CHAPTER 21 PROGRAMMING WITH WINDOWS FORMS CONTROLS

{

ItalicUserMessageDialog dlg = new ItalicUserMessageDialog(); dlg.Message = userMessage;

dlg.Italic = textIsItalic;

//If user clicked OK button, render his message. if (DialogResult.OK == dlg.ShowDialog())

{

userMessage = dlg.Message; textIsItalic = dlg.Italic;

Invalidate();

}

//Have dialog clean up internal widgets now, rather

//than when the GC destroys the object. dlg.Dispose();

}

private void MainWindow_Paint(object sender, PaintEventArgs e)

{

Graphics g = e.Graphics; Font f = null; if(textIsItalic)

f = new Font("Times New Roman", 24, FontStyle.Italic);

else

f = new Font("Times New Roman", 24); g.DrawString(userMessage, f, Brushes.DarkBlue,

50, 50);

}

}

Source Code The SimpleModalDialog application is included under the Chapter 21 directory.

Dynamically Positioning Windows Forms Controls

To wrap up this chapter, let’s examine a few techniques you can use to control the layout of widgets on a Form. By and large, when you build a Form type, the assumption is that the controls are rendered using absolute position, meaning that if you placed a Button on your Forms designer 10 pixels down and 10 pixels over from the upper left portion of the Form, you expect the Button to stay put during its lifetime.

On a related note, when you are creating a Form that contains UI controls, you need to decide whether the Form should be resizable. Typically speaking, main windows are resizable, whereas dialog boxes are not. Recall that the resizability of a Form is controlled by the FormBorderStyle property, which can be set to any value of the FormBorderStyle enum.

public enum System.Windows.Forms.FormBorderStyle

{

None, FixedSingle, Fixed3D, FixedDialog, Sizable, FixedToolWindow, SizableToolWindow

}

Assume that you have allowed your Form to be resizable. This brings up some interesting questions regarding the contained controls. For example, if the user makes the Form smaller than the rectangle needed to display each control, should the controls adjust their size (and possibly location) to morph

CHAPTER 21 PROGRAMMING WITH WINDOWS FORMS CONTROLS

755

The Anchor Property

In Windows Forms, the Anchor property is used to define a relative fixed position in which the control should always be rendered. Every Control-derived type has an Anchor property, which can be set to any of the values from the AnchorStyles enumeration described in Table 21-13.

Table 21-13. AnchorStyles Values

Value

Meaning in Life

Bottom

The control’s bottom edge is anchored to the bottom edge of its container.

Left

The control’s left edge is anchored to the left edge of its container.

None

The control is not anchored to any edges of its container.

Right

The control’s right edge is anchored to the right edge of its container.

Top

The control’s top edge is anchored to the top edge of its container.

 

 

To anchor a widget at the upper-left corner, you are free to OR styles together (e.g., AnchorStyles.TopAnchorStyles.Left). Again, the idea behind the Anchor property is to configure which edges of the control are anchored to the edges of its container. For example, if you configure a Button with the following Anchor value:

// Anchor this widget relative to the right position. myButton.Anchor = AnchorStyles.Right;

you are ensured that as the Form is resized, this Button maintains its position relative to the right side of the Form.

The Dock Property

Another aspect of Windows Forms programming is establishing the docking behavior of your controls. If you so choose, you can set a widget’s Dock property to configure which side (or sides) of a Form the widget should be attached to. The value you assign to a control’s Dock property is honored, regardless of the Form’s current dimensions. Table 21-14 describes possible options.

Table 21-14. DockStyle Values

Value

Meaning in Life

Bottom

The control’s bottom edge is docked to the bottom of its containing control.

Fill

All the control’s edges are docked to all the edges of its containing control and sized

 

appropriately.

Left

The control’s left edge is docked to the left edge of its containing control.

None

The control is not docked.

Right

The control’s right edge is docked to the right edge of its containing control.

Top

The control’s top edge is docked to the top of its containing control.

 

 

So, for example, if you want to ensure that a given widget is always docked on the left side of a Form, you would write the following:

//This item is always located on the left of the Form, regardless

//of the Form's current size.

myButton.Dock = DockStyle.Left;

756 CHAPTER 21 PROGRAMMING WITH WINDOWS FORMS CONTROLS

To help you understand the implications of setting the Anchor and Dock properties, the downloadable code for this book contains a project named AnchoringControls. Once you build and run this application, you can make use of the Form’s menu system to set various AnchorStyles and DockStyle values and observe the change in behavior of the Button type (see Figure 21-37).

Figure 21-37. The AnchoringControls application

Be sure to resize the Form when changing the Anchor property to observe how the Button responds.

Source Code The AnchoringControls application is included under the Chapter 21 directory.

Table and Flow Layout

.NET 2.0 offers an additional way to control the layout of a Form’s widgets using one of two layout managers. The TableLayoutPanel and FlowLayoutPanel types can be docked into a Form’s client area to arrange the internal controls. For example, assume you place a new FlowLayoutPanel widget onto the Forms designer and configure it to dock fully within the parent Form (see Figure 21-38).

Figure 21-38. Docking a FlowLayoutPanel into a Form

Now, add ten new Button types within the FlowLayoutPanel using the Forms designer. If you now run your application, you will notice that the ten Buttons automatically rearrange themselves in a manner very close to standard HTML.

CHAPTER 21 PROGRAMMING WITH WINDOWS FORMS CONTROLS

757

On the other hand, if you create a Form that contains a TableLayoutPanel, you are able to build a UI that is partitioned into various “cells” (see Figure 21-39).

Figure 21-39. The TableLayoutPanel type

If you select the Edit Rows and Columns inline menu option using the Forms designer (as shown in Figure 21-39), you are able to control the overall format of the TableLayoutPanel on a cell- by-cell basis (see Figure 21-40).

Figure 21-40. Configuring the cells of the TableLayoutPanel type

Truth be told, the only way to see the effects of the TableLayoutPanel type is to do so in a handson manner. I’ll let interested readers handle that task.

758 CHAPTER 21 PROGRAMMING WITH WINDOWS FORMS CONTROLS

Summary

This chapter rounded off your understanding of the Windows Forms namespace by examining the programming of numerous GUI widgets, from the simple (e.g., Label) to the more exotic (e.g., TreeView). After examining numerous control types, you moved on to cover the construction of custom controls, including the topic of design-time integration.

In the latter half of this chapter, you learned how to build custom dialog boxes and how to derive a new Form from an existing Form type using form inheritance. This chapter concluded by briefly exploring the various anchoring and docking behaviors you can use to enforce a specific layout of your GUI types, as well as the new .NET 2.0 layout managers.

C H A P T E R 2 2

■ ■ ■

Database Access with ADO.NET

Unless you are a video game developer by trade, you are probably interested in the topic of database access. As you would expect, the .NET platform defines a number of namespaces that allow you to interact with local and remote data stores. Collectively speaking, these namespaces are known as ADO.NET.

In this chapter, once I frame the overall role of ADO.NET (in the next section), I’ll move on to discuss the topic of ADO.NET data providers. The .NET platform supports numerous data providers, each of which is optimized to communicate with a specific database management system (Microsoft SQL Server, Oracle, MySQL, etc.). After you understand how to manipulate a specific data provider, you will then examine the new data provider factory pattern offered by .NET 2.0. Using types within the System.Data.Common namespace (and a related app.config file), you are able to build a single code base that can dynamically pick and choose the underlying data provider without the need to recompile or redeploy the application’s code base.

The remaining part of this chapter examines how to programmatically interact with relational databases using your data provider of choice. As you will see, ADO.NET provides two distinct ways to interface with a data source, often termed the connected layer and disconnected layer. You will come to know the role of connection objects, command objects, data readers, data adapters, and numerous types within the System.Data namespace (specifically, DataSet, DataTable, DataRow,

DataColumn, DataView, and DataRelation).

A High-Level Definition of ADO.NET

If you have a background in Microsoft’s previous COM-based data access model (Active Data Objects, or ADO), understand that ADO.NET has very little to do with ADO beyond the letters “A,” “D,” and “O.” While it is true that there is some relationship between the two systems (e.g., each has the concept of connection and command objects), some familiar ADO types (e.g., the Recordset) no longer exist. Furthermore, there are a number of new ADO.NET types that have no direct equivalent under classic ADO (e.g., the data adapter).

Unlike classic ADO, which was primarily designed for tightly coupled client/server systems, ADO.NET was built with the disconnected world in mind, using DataSets. This type represents a local copy of any number of related tables. Using the DataSet, the client tier is able to manipulate and update its contents while disconnected from the data source, and it can submit the modified data back for processing using a related data adapter.

Another major difference between classic ADO and ADO.NET is that ADO.NET has deep support for XML data representation. In fact, the data obtained from a data store is serialized (by default) as XML. Given that XML is often transported between layers using standard HTTP, ADO.NET is not limited by firewall constraints.

759

760 CHAPTER 22 DATABASE ACCESS WITH ADO.NET

Note As of .NET 2.0, DataSets (and DataTables) can now be serialized in a binary format via the RemotingFormat property. This can be helpful when building distributed systems using the .NET remoting layer (see Chapter 18), as binary data is much more compact than XML data.

Perhaps the most fundamental difference between classic ADO and ADO.NET is that ADO.NET is a managed library of code, therefore it plays by the same rules as any managed library. The types that make up ADO.NET use the CLR memory management protocol, adhere to the same type system (classes, interfaces, enums, structures, and delegates), and can be accessed by any .NET language.

The Two Faces of ADO.NET

The ADO.NET libraries can be used in two conceptually unique manners: connected or disconnected. When you are making use of the connected layer, your code base will explicitly connect to and disconnect from the underlying data store. When you are using ADO.NET in this manner, you typically interact with the data store using connection objects, command objects, and data reader objects. As you will see later in this chapter, data readers provide a way to pull records from a data store using a forward-only, read-only approach (much like a fire-hose cursor).

The disconnected layer, on the other hand, allows you to obtain a set of DataTable objects (contained within a DataSet) that functions as a client-side copy of the external data. When you obtain a DataSet using a related data adapter object, the connection is automatically opened and closed on your behalf. As you would guess, this approach helps quickly free up connections for other callers. Once the client receives a DataSet, it is able to traverse and manipulate the contents without incurring the cost of network traffic. As well, if the client wishes to submit the changes back to the data store, the data adapter (in conjunction with a set of SQL statements) is used once again to update the data source, at which point the connection is closed immediately.

Understanding ADO.NET Data Providers

ADO.NET does not provide a single set of types that communicate with multiple database management systems (DBMSs). Rather, ADO.NET supports multiple data providers, each of which is optimized to interact with a specific DBMS. The first benefit of this approach is that a specific data provider can be programmed to access any unique features of the DBMS. Another benefit is that a specific data provider is able to directly connect to the underlying engine of the DBMS without an intermediate mapping layer standing between the tiers.

Simply put, a data provider is a set of types defined in a given namespace that understand how to communicate with a specific data source. Regardless of which data provider you make use of, each defines a set of class types that provide core functionality. Table 22-1 documents some (but not all) of the core common objects, their base class (all defined in the System.Data.Common namespace), and their implemented data-centric interfaces (each defined in the System.Data namespace).

 

 

CHAPTER 22 DATABASE ACCESS WITH ADO.NET

761

Table 22-1. Core Objects of an ADO.NET Data Provider

 

 

 

 

 

 

 

Object

Base Class

Implemented Interfaces

Meaning in Life

 

Connection

DbConnection

IDbConnection

Provides the ability to

 

 

 

 

connect to and

 

 

 

 

disconnect from the

 

 

 

 

data store. Connection

 

 

 

 

objects also provide

 

 

 

 

access to a related

 

 

 

 

transaction object.

 

Command

DbCommand

IDbCommand

Represents a SQL query

 

 

 

 

or name of a stored

 

 

 

 

procedure. Command

 

 

 

 

objects also provide

 

 

 

 

access to the provider’s

 

 

 

 

data reader object.

 

DataReader

DbDataReader

IDataReader, IDataRecord

Provides forward-only,

 

 

 

 

read-only access to data.

 

DataAdapter

DbDataAdapter

IDataAdapter, IDbDataAdapter

Transfers DataSets

 

 

 

 

between the caller and

 

 

 

 

the data store. Data

 

 

 

 

adapters contain a set of

 

 

 

 

four internal command

 

 

 

 

objects used to select,

 

 

 

 

insert, update, and

 

 

 

 

delete information from

 

 

 

 

the data store.

 

Parameter

DbParameter

IDataParameter,

Represents a named

 

 

 

IDbDataParameter

parameter within

 

 

 

 

a parameterized query.

 

Transaction

DbTransaction

IDbTransaction

Performs a database

 

 

 

 

transaction.

 

 

 

 

 

 

Although the names of these types will differ among data providers (e.g., SqlConnection versus

OracleConnection versus OdbcConnection versus MySqlConnection), each object derives from the same base class that implements identical interfaces. Given this, you are correct to assume that once you learn how to work with one data provider, the remaining providers are quite straightforward.

Note As a naming convention, the objects in a specific data provider are prefixed with the name of the related DBMS.

Figure 22-1 illustrates the big picture behind ADO.NET data providers. Note that in the diagram, the “Client Assembly” can literally be any type of .NET application: console program, Windows Forms application, ASP.NET web page, XML web service, .NET code library, and so on.

762 CHAPTER 22 DATABASE ACCESS WITH ADO.NET

Figure 22-1. ADO.NET data providers provide access to a given DBMS.

Now, to be sure, a data provider will supply you with other types beyond the objects shown in Figure 22-1. However, these core objects define a common baseline across all data providers.

Microsoft-Supplied Data Providers

As of version 2.0, Microsoft’s .NET distribution ships with numerous data providers, including a provider for Oracle, SQL Server, and ODBC-style connectivity. Table 22-2 documents the namespace and containing assembly for each Microsoft ADO.NET data provider.

Table 22-2. Microsoft ADO.NET Data Providers

Data Provider

Namespace

Assembly

OLE DB

System.Data.OleDb

System.Data.dll

Microsoft SQL Server

System.Data.SqlClient

System.Data.dll

Microsoft SQL Server Mobile

System.Data.SqlServerCe

System.Data.SqlServerCe.dll

ODBC

System.Data.Odbc

System.Data.dll

Oracle

System.Data.OracleClient

System.Data.OracleClient.dll

 

 

 

 

 

 

Note There is no specific data provider that maps directly to the Jet engine (and therefore Microsoft Access). If you wish to interact with an Access data file, you can do so using the OLE DB or ODBC data provider.

CHAPTER 22 DATABASE ACCESS WITH ADO.NET

763

The OLE DB data provider, which is composed of the types defined in the System.Data.OleDb namespace, allows you to access data located in any data store that supports the classic COM-based OLE DB protocol. Using this provider, you may communicate with any OLE DB–compliant database simply by tweaking the “Provider” segment of your connection string. Be aware, however, that the OLE DB provider interacts with various COM objects behind the scenes, which can affect the performance of your application. By and large, the OLE DB data provider is only useful if you are interacting with a DBMS that does not define a specific .NET data provider.

The Microsoft SQL Server data provider offers direct access to Microsoft SQL Server data stores, and only SQL Server data stores (version 7.0 and greater). The System.Data.SqlClient namespace contains the types used by the SQL Server provider and offers the same basic functionality as the OLE DB provider. The key difference is that the SQL Server provider bypasses the OLE DB layer and thus gives numerous performance benefits. As well, the Microsoft SQL Server data provider allows you to gain access to the unique features of this particular DBMS.

Note If you are interested in making use of the System.Data.SqlServerCe, System.Data.Odbc, or System. Data.Oracle namespaces, check out the details as you see fit using the .NET Framework 2.0 SDK documentation.

Select Third-Party Data Providers

In addition to the data providers that ship from Microsoft, numerous third-party data providers exist for various open source and commercial databases. Table 22-3 documents where to obtain managed providers for several databases that do not directly ship with Microsoft .NET 2.0 (please note that the provided URLs are subject to change).

Table 22-3. Third-Party ADO.NET Data Providers

Data Provider

Website

Firebird Interbase

http://www.mono-project.com/Firebird_Interbase

IBM DB2 Universal Database

http://www-306.ibm.com/software/data/db2

MySQL

http://dev.mysql.com/downloads/connector/net/1.0.html

PostgreSQL

http://www.mono-project.com/PostgreSQL

Sybase

http://www.mono-project.com/Sybase

 

 

 

 

Note Given the large number of ADO.NET data providers, the examples in this chapter will make use of the Microsoft SQL Server data provider (System.Data.SqlClient). If you intend to use ADO.NET to interact with another DBMS, you should have no problem doing so once you understand the material presented in the pages that follow.

Additional ADO.NET Namespaces

In addition to the .NET namespaces that define the types of a specific data provider, the base class libraries provide a number of additional ADO.NET-centric namespaces, as shown in Table 22-4.