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

Beginning Visual C++ 2005 (2006) [eng]-1

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

Accessing Data Sources in a Windows Forms Application

Figure 22-6

Each column has its width set so that it accommodates the longest string that the column contains. Overall, the resultant application window is not too bad, but you can do a lot more programmatically to personalize how it looks.

Customizing a DataGridView Control

As I said earlier in the chapter, the appearance of a DataGridView control is highly customizable. You will explore aspects of this using the control in unbound mode, but everything you’ll learn in this context applies equally well to using the control in bound mode. The appearance of each of the cells in a DataGridView control is determined by an object of type DataGridViewCellStyle that has the following properties:

Property

Description

 

 

BackColor

The value is a System::Drawing::Color object that determines the

 

background color of a cell. The Color class defines a range of standard

 

colors as static members. The default value is Color::Empty.

ForeColor

The value is a Color object that determines the foreground color of a

 

cell. The default value is Color::Empty.

SelectionBackColor

The value is a Color object that determines the background color of a

 

cell when it is selected. The default value is Color::Empty.

SelectionForeColor

The value is a Color object that determines the foreground color of a

 

cell when it is selected. The default value is Color::Empty.

Font

The value is a System::Drawing::Font object that determines the

 

font to be used to display text in the cell. The default value is null.

 

 

 

Table continued on following page

1099

Chapter 22

Property

Description

 

 

Alignment

The value determines the alignment of the contents of the cell. The val-

 

ues are defined by the DataGridViewAlignment enumeration so the

 

value can be any of the following constants:

 

BottomCenter, BottomLeft, BottomRight,

 

MiddleCenter, MiddleLeft, MiddleRight,

 

TopCenter, TopLeft, TopRight, NotSet

 

The default value is NotSet.

WrapMode

The value determines whether the text in the cell is wrapped when it is

 

too long to fit in the cell. The value is one of the constants defined by

 

the DataGridViewTriState enumeration and can be:

 

True, False, NotSet

 

The default value is NotSet.

Padding

The value is an object of type System::Windows::Forms::Padding

 

that determines the space between the cell contents and the edge of the

 

cell. The Padding class constructor requires an argument of type int

 

that is the padding measured in pixels. The default corresponds to no

 

padding in the cell.

Format

The value is a format string that determines how the content of the

 

string is formatted. This is the same kind of formatting as you have

 

been using in the Console::WriteLine() function. The default value

 

is an empty string.

 

 

This is not an exhaustive list of the properties of a DataGridViewCellStyle object, just those that relate to the appearance of a cell.

The way in which the appearance of a particular cell is determined is quite complicated because you have a number of different properties that you can set in a DataGridView control that all determine how a given cell or group of cells is displayed, and several of these can be in effect at any given time. For example, you can set property values that specify the appearance of a row of cells, or a column of cells, or all the cells in the control, and these can all be in effect concurrently. Clearly, because a row and column always intersect, all three of these possibilities apply to any given cell, so you have an apparent conflict.

Each cell in a DataGridView control is represented by a System::Windows::Forms::DataGridViewCell object, and the appearance of any given cell, including header cells, is determined by the value of its InheritedStyle property. The value of the InheritedStyle property for a cell is arrived at by looking at all the possible properties that return a value that is a DataGridViewCellStyle object that applies to the cell and then considering these properties in a priority sequence; the first property in the sequence that is found to be set is the one that takes effect. The determination of the value of the InheritedStyle property for header cells for rows and columns is handled differently from the InheritedStyle property for other cells, so I’ll discuss them separately, starting with header cells.

1100

Accessing Data Sources in a Windows Forms Application

Customizing Header Cells

The InheritedStyle property value for each header cell in the control is determined by considering the values of following properties in sequence:

The Style property for the DataGridViewCell object that represents the cell.

The ColumnHeadersDefaultCellStyle property or the RowHeadersDefaultCellStyle property for the control object.

The DefaultCellStyle property for the control object.

So if the Style property value for the cell object has been set, the InheritedStyle property for the cell takes this value and determines the appearance of the cell. If not, the next candidate takes effect if the value for that has been set. If the second choice has not been set, the DefaultCellStyle property for the control is applied.

Don’t forget that the value of the InheritedStyle property is an object of type DataGridViewCellStyle, which itself has properties that determine various aspects of the appearance of the cell. The process of going through the priority sequence applies to each of the properties of the DataGridViewCellStyle object, so overall there may be contributions from more than one of the properties in the priority sequence.

Customizing Non-Header Cells

The InheritedStyle property value for each non-header cell in the control (the non-header cells being the cells containing data) is determined from the following properties in the DataGridView object in sequence:

The Style property for the DataGridViewCell object that represents the cell.

The DefaultCellStyle property for the DataGridViewRow object that represents the row containing the cell. You would typically reference the DataGridViewRow object by indexing the Rows property for the control object.

The AlternatingRowsDefaultCellStyle property for the control object; this applies only to cells in rows with odd index numbers.

The RowsDefaultCellStyle property for the control object.

The DefaultCellStyle property for the DataGridViewColumn object that contains the cell. You would typically access a DataGridViewColumn object by indexing the Columns property for the control object.

The DefaultCellStyle property for the control object.

Potentially you could have a different DataGridViewCellStyle object for each cell, but for efficiency you need to keep the number of such objects to a minimum.

The next Try It Out explores some of these possibilities in an example where you set up the DataGridView object yourself.

1101

Chapter 22

Try It Out

Setting the Appearance of the Control

Create a new CLR project using the Windows Forms template with the name Ex22_02. Add a DataGridView control to the form in the Design tab and change its (Name) property to dataGridView. This is the name of the handle in the class that references the control object. You can also change the Text property for the form to “My Other Book List.” For the rest of the example, you are going to be working with the code in the constructor.

The data that you display is similar to that in the previous example, but to extend the possibilities a little, you’ll add a date entry at the beginning of each row specifying a book, so the cells in the first column will contain references to objects of type System::DateTime, and the remaining columns will be strings. The DateTime class defines an instant in time that you typically specify as a date plus the time of day. In the example, only the date is of interest, so you’ll use a constructor that accepts only three arguments: the year, the month, and the day.

Setting Up the Data

The first step is to create the data to be displayed. Add the following code to the Form1 constructor, after the call to InitializeComponent():

// Create book data, one book per array

array<Object^>^ book1 = {gcnew DateTime(1999,11,5), L”0-09-174271-4”, L”Wonderful Life”, L”Stephen Jay Gould”, L”Hutchinson Radius”};

array<Object^>^ book2 = {gcnew DateTime(2001,10,25), L”0-09-977170-5”,

L”The Emperor’s New Mind”, L”Roger Penrose”, L”Vintage”}; array<Object^>^ book3 = {gcnew DateTime(1993,1,15), L”0-14-017996-8”,

L”Metamagical Themas”, “Douglas R. Hofstadter”, L”Penguin”}; array<Object^>^ book4 = {gcnew DateTime(1994,2,7), L”0-201-36080-2”,

L”The Meaning Of It All”, L”Richard P. Feynman”, L”Addison-Wesley”}; array<Object^>^ book5 = {gcnew DateTime(1995,11,6), L”0-593-03449-X”,

L”The Walpole Orange”, “Frank Muir”, L”Bantam Press”}; array<Object^>^ book6 = {gcnew DateTime(2004,7,16), L”0-439-99358-X”,

L”The Amber Spyglass”, L”Philip Pullman”, L”Scholastic Children’s Books”}; array<Object^>^ book7 = {gcnew DateTime(2002,9,18), L”0-552-13461-9”,

L”Pyramids”, L”Terry Pratchett”, L”Corgi Books”}; array<Object^>^ book8 = {gcnew DateTime(1998,2,27), L”0-7493-9739-X”,

L”Made In America”, L”Bill Bryson”, L”Minerva”};

// Create Array of books

array<array<Object^>^>^ books = {book1, book2, book3, book4, book5, book6, book7, book8};

The basic mechanics of this is the same as in the previous example. The differences here are due to each book having an extra item of type DateTime in the specification, so the array elements are of type Object^. You’ll recall that the Object class is a base class for every C++/CLI class so you can store a handle to an object of any class type in an element of type Object^.

You can add the following statement to the constructor next:

array<String^>^ headers = {L”Date”, L”ISBN”, L”Title”, L”Author”, L”Publisher”};

1102

Accessing Data Sources in a Windows Forms Application

This creates an array containing the text that is to appear as column headers in the control. You can add these headers to the control by adding the following code to the constructor:

dataGridView->ColumnCount = headers->Length;

// Set number of columns

for(int i = 0 ; i<headers->Length ; i++)

 

dataGridView->Columns[i]->Name = headers[i];

 

 

 

The first statement specifies the number of columns in the control by setting the value of the ColumnCount property; this also establishes the control in unbound mode. The for loop sets the Name property for each column object to the corresponding string in the headers array. The Columns property for the control returns a reference to the collection of columns, and you just index this to reference a particular column.

You can add the rows to the control in another loop:

for each(array<Object^>^ book in books)

dataGridView->Rows->Add(book);

The for each loop selects each of the elements from the books array in turn add passes it to the Add() method for the reference returned by the Rows property for the control. Each element in the books array is an array of strings, and there are as many strings in the array as there are columns in the control.

The control has now been loaded with the data, so the number of rows and columns is determined and the contents of the column headers have been specified. You can now set about customizing the appearance of the control.

Setting Up the Control

You want the control to be docked in the client area of the form, and you can do this by setting the value of the Dock property:

dataGridView->Dock = DockStyle::Fill;

The Dock property must be set to one of the constants defined by the DockStyle enumeration; other possible values are Top, Bottom, Left, Right, or None, and these specify the sides of the control that are docked.

You can also relate the position of the control to the client area of the form by setting the Anchor property for the control.The value of Anchor property specifies the edges of the control that are to be attached to the client area of the form. The value is a bitwise combination of the constants defined by the AnchorStyles enumeration and can be any or all of Top, Bottom, Left, and Right. For example to anchor the top and left sides of the control, you would specify the value as AnchorStyles::Top & AnchorStyles::Left. Setting the Anchor property fixes the position of the control plus its scrollbars within the container at a given size, so when you resize the application, window the control and its scrollbars remain at a fixed size. If you set the Dock property as in the previous statement, resizing the application window exposes more or less of the control and the scrollbars adjust accordingly, so that is much better in this case.

You want the width of the columns to be adjusted to accommodate the data in the cells, and you can put this into effect by calling the AutoResizeColumns() function:

dataGridView->AutoResizeColumns();

1103

Chapter 22

This statement adjusts the width of all columns to accommodate the current contents, and this includes header cells. Note that this is effective at the time the function is called, so the contents need to be there when you call it. If the contents are changed subsequently, the column width is not adjusted. If you want the column widths to be adjusted whenever the contents of the cells change, you should also set the AutoSizeColumnsMode property for the control, like this:

dataGridView->AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode::AllCells;

The value must be one of the constants defined by the DataGridViewAutoSizeColumnsMode enumeration, and the other possible values are ColumnHeader, AllCellsExceptHeader, DisplayedCells,

DisplayedCellsExceptHeader, Fill, and None. Of course, these are also the property values in the list for this property that displays on the Properties page for the DataGridView control.

It may be that you want to allow only the columns width for specific columns to be automatically adjusted when the contents change; in this case, you set a value for the AutoSizeMode property for the column object.

There are two further overloaded versions of the AutoResizeColumns() function. One accepts an argument of type DataGridViewAutoSizeColumnsMode, and the cells affected are determined by the value of the argument. The other overload is protected and, therefore, for use in functions in a derived class; it accepts an additional argument of type bool that indicates whether the cell height is to be considered in calculating a new width.

You can set the default background color of all cells in the control like this:

dataGridView->DefaultCellStyle->BackColor = Color::Pink;

This sets the background color as the standard color Pink that is defined as a static member of the Color class. The properties of the DefaultCellStyle property for the control object only determine what applies to a cell in the absence of any higher priority cell style being in effect.

You could also set the default foreground color for all cells:

dataGridView->DefaultCellStyle->ForeColor = Color::DarkBlue;

To identify when cells have been selected, you can specify selection colors for the foreground and background. Here’s how you could define the background color when a cell is selected:

dataGridView->DefaultCellStyle->SelectionBackColor = Color::Green;

Of course, the point of setting property values programmatically is that it happens at run-time so you can set values depending on conditions and data values that you find when the application is running. Property values that you set through the Properties pane in the IDE are set once and for all — unless you have code that changes them subsequently.

That’s enough for the control for now. You’ll personalize the column headers next.

1104

Accessing Data Sources in a Windows Forms Application

Setting Up the Column Headers

If you want to determine the appearance of the column headers yourself, you need to set the value of the

EnableHeadersVisualStyles property for the control to false:

dataGridView->EnableHeadersVisualStyles = false;

The controls in a WindowsForms application are usually drawn according to the visual styles theme that is in effect, and this theme determines the appearance of the controls. If you are running the application under Windows XP, the controls are drawn according to the Windows XP theme. When the EnableHeadersVisualStyles property value is true, the visual styles for the column headers will be set according to the visual styles theme in effect for the application, and the styles you set are ignored.

You’ll be setting several properties for the appearance of the headers and an easy way to do this is to create a DataGridViewCellStyle object for which you can set the properties as you want them, and then make this object the one that determines the styles for the headers. You can create the

DataGridViewCellStyle object like this:

DataGridViewCellStyle^ headerStyle = gcnew DataGridViewCellStyle;

It would be nice to have the header text in a larger font, and you can set the font by setting a value for the Font property:

headerStyle->Font = gcnew System::Drawing::Font(“Times New Roman”, 12,

FontStyle::Bold);

The header text is now in 12-point bold characters in the Times New Roman font.

You can also set the background and foreground colors for the header cells:

headerStyle->BackColor = Color::AliceBlue;

headerStyle->ForeColor = Color::BurlyWood;

The text is now drawn in the color BurlyWood against an AliceBlue background. If you prefer something different, the Color class offers you a lot of choices, and Intellisense should show the list as you complete typing the scope resolution operator.

To set the appearance of the header cells to correspond with the properties that you’ve set for the headerStyle object, you need to add the following statement:

dataGridView->ColumnHeadersDefaultCellStyle = headerStyle;

This sets the value of the ColumnHeadersDefaultCellStyle property for the control to be the headerStyle handle. This replaces the existing DataGridViewCellStyle object that was in effect for the headers.

There is one other thing you should do in relation to the column headers. The larger font requires the height of the cells to be adjusted to accommodate it. Calling the AutoResizeColumnHeadersHeight() function for the control adjusts the heights of the header cells to accommodate their current contents:

dataGridView->AutoResizeColumnHeadersHeight();

1105

Chapter 22

The heights of all header cells is adjusted to fit the largest cell contents. If you just want the height of a particular column header to be adjusted, you can use the overloaded version of the function that accepts an argument specifying the index of the column to be adjusted.

If you don’t want the row or column headers to be visible you can make them disappear by setting the value of the RowHeadersVisible property and/or ColumnHeadersVisible property for the control to false.

Formatting a Column

The first column contains handles to a DateTime object. As it is, the application simply calls the ToString() function for the objects to get something to display, but you can do better than that. You can set the Format property for the DefaultCellStyle property for the column, and this format specification is then used to display the contents of the cells:

dataGridView->Columns[0]->DefaultCellStyle->Format = L”y”;

This sets the Format property to the string containing the y format specification for a DateTime object that presents the object in the short date form as month plus year. There are several other format specifiers for DateTime objects that you could use. For example, D displays the day as well as the month and year, and f and F displays the time as well as the date.

If you have added all that code to the Form1 class constructor, it’s time to don the sunglasses and give the example a whirl. If you compile and run it, you should see something like the application window shown in Figure 22-7.

Figure 22-7

I have resized the width of the window in Figure 22-7 to show more of the columns. Unfortunately, in the book the application window appears in shades of grey but you should see it on your screen in glorious Technicolor.

If you click one of the row headers on the left of the control, you should see the row highlighted as shown in Figure 22-8.

1106

Accessing Data Sources in a Windows Forms Application

Figure 22-8

The background color in the cells in the row is the one you set for the SelectionBackColor property of the DefaultCellStyle property for the control. You can also select an individual cell by clicking it, and the background color of the cell changes to green.

The ability to sort the rows based on any given column is built into the DataGridView control. You could try clicking a column header and see the rows sorted by the column you select. If you click the column header a second time, the rows are ordered in the opposite sense. You could add a tooltip to each of the columns to explain the sort possibility. Adding this loop to the Form1 class constructor does this:

for each(DataGridViewColumn^ column in dataGridView->Columns)

column->ToolTipText = L”Click to\nsort rows”;

The Columns property value is a collection of columns where each column is an object of type DataGridViewColumn. The loop iterates over each of the columns and sets the value of the ToolTipText property. Figure 22-9 shows the tooltip for one of the column headers.

Figure 22-9

1107

Chapter 22

These tooltips display only when the mouse cursor is over a column header cell. You can set a tooltip for any of the cells that display the data by setting the ToolTipText property for the cell object.

Customizing Alternate Rows

When you are displaying many rows that are similar in appearance, it can be difficult to see which row you are looking at. You can color alternate rows differently to help overcome the problem by setting a different color as the BackColor property for the AlternatingRowsDefaultCellStyle property for the control object:

dataGridView->AlternatingRowsDefaultCellStyle->BackColor = Color::Blue;

You will probably want to change the ForeColor property for the AlternatingRowsDefaultCellStyle property to get a reasonable contrast between the text and the background:

dataGridView->AlternatingRowsDefaultCellStyle->ForeColor = Color::White;

Now rows are displayed with colors alternating between pink and blue, as shown in Figure 22-10 (in shades of grey, of course, as there is no color in the book).

Figure 22-10

Now it’s easy to separate the rows and the white text against a blue background is clear. You can still select a row by clicking a row header and see the selected row in green. Clicking in the cell at the left end of the column headers selects all the rows.

Dynamically Setting Cell Styles

You have several possibilities for changing the appearance of cells by handling events for the DataGridView control. The CellFormatting event for a DataGridView control fires when the contents of a cell need to be formatted ready to be displayed, so by adding a handler for this event you can adjust the appearance of any cell depending on what the contents are. I want now to explore how you could extend Ex22_02 to do this.

1108