- •brief contents
- •about this book
- •The Windows Forms namespace
- •Part 1: Hello Windows Forms
- •Part 2: Basic Windows Forms
- •Part 3: Advanced Windows Forms
- •Who should read this book?
- •Conventions
- •Action
- •Result
- •Source code downloads
- •Author online
- •acknowledgments
- •about .NET
- •Casting the .NET
- •Windows Forms overview
- •about the cover illustration
- •Hello Windows Forms
- •1.1 Programming in C#
- •1.1.1 Namespaces and classes
- •1.1.2 Constructors and methods
- •1.1.3 C# types
- •1.1.4 The entry point
- •1.1.5 The Application class
- •1.1.6 Program execution
- •1.2 Adding controls
- •1.2.1 Shortcuts and fully qualified names
- •1.2.2 Fields and properties
- •1.2.3 The Controls property
- •1.3 Loading files
- •1.3.1 Events
- •1.3.2 The OpenFileDialog class
- •1.3.3 Bitmap images
- •1.4 Resizing forms
- •1.4.1 Desktop layout properties
- •1.4.2 The Anchor property
- •1.4.3 The Dock property
- •1.5 Recap
- •2.1 Programming with Visual Studio .NET
- •2.1.1 Creating a project
- •Action
- •Result
- •2.1.2 Executing a program
- •Action
- •Result
- •2.1.3 Viewing the source code
- •View the code generated by Visual Studio .NET
- •Action
- •Result
- •2.2 Adding controls
- •2.2.1 The AssemblyInfo file
- •Action
- •Results
- •2.2.2 Renaming a form
- •Action
- •Result
- •2.2.3 The Toolbox window
- •Action
- •Result
- •2.3 Loading files
- •2.3.1 Event handlers in Visual Studio .NET
- •Action
- •Result
- •2.3.2 Exception handling
- •Action
- •Result
- •Action
- •Results and Comments
- •2.4 Resizing forms
- •2.4.1 Assign the Anchor property
- •Action
- •Result
- •2.4.2 Assign the MinimumSize property
- •Action
- •Result
- •2.5 Recap
- •Basic Windows Forms
- •Menus
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •3.3 Click events
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •3.5 Context menus
- •Action
- •Result
- •Action
- •Result
- •3.6 Recap
- •Status bars
- •4.1 The Control class
- •4.2 The StatusBar class
- •Action
- •Result
- •Action
- •Result
- •4.3.1 Adding panels to a status bar
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •4.5 Recap
- •Reusable libraries
- •5.1 C# classes and interfaces
- •5.2 Class libraries
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •5.3 Interfaces revisited
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •5.4 Robustness issues
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Common file dialogs
- •Action
- •Results
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •6.3 Paint events
- •Action
- •Result
- •Action
- •Result
- •6.4 Context menus revisited
- •Action
- •Result
- •Action
- •Result
- •6.5 Files and paths
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •6.6 Save file dialogs
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •6.7 Open file dialogs
- •Action
- •Result
- •Action
- •Result
- •6.8 Recap
- •Drawing and scrolling
- •7.1 Form class hierarchy
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •7.4 Panels
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Dialog boxes
- •8.1 Message boxes
- •Action
- •Result
- •Action
- •Result
- •8.1.4 Creating A YesNoCancel dialog
- •Action
- •Result
- •Action
- •Result
- •8.2 The Form.Close method
- •8.2.1 The relationship between Close and Dispose
- •Action
- •Result
- •8.3 Modal dialog boxes
- •Action
- •Result
- •Action
- •Result
- •8.3.2 Preserving caption values
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Basic controls
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •9.1.2 Creating a derived form
- •Action
- •Result
- •9.2 Labels and text boxes
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •9.3.6 Adding AlbumEditDlg to our main form
- •Action
- •Result
- •Action
- •Result
- •9.4 Recap
- •List controls
- •10.1 List boxes
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •10.2 Multiselection list boxes
- •10.2.1 Enabling multiple selection
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •10.3 Combo boxes
- •Action
- •Result
- •Action
- •Result
- •10.4 Combo box edits
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •10.5 Owner-drawn lists
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •More controls
- •Action
- •Result
- •Action
- •Result
- •11.2 Tab pages
- •Action
- •Result
- •Action
- •Result
- •11.3.1 Dates and times
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •11.5 Recap
- •A .NET assortment
- •12.1 Keyboard events
- •Action
- •Result
- •Action
- •Result
- •12.2 Mouse events
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •12.3 Image buttons
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •12.4 Icons
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •12.5 Recap
- •Toolbars and tips
- •13.1 Toolbars
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •13.4.2 Creating tool tips
- •Action
- •Result
- •Action
- •Result
- •Advanced Windows Forms
- •List views
- •14.2 The ListView class
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •14.2.3 Populating a ListView
- •Action
- •Result
- •Action
- •14.3 ListView columns
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •14.6 Recap
- •Tree views
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •15.3 Dynamic tree nodes
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •15.4 Node selection
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •15.5 Fun with tree views
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Multiple document interfaces
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •16.3 Merged menus
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •16.4 MDI children
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •16.5 MDI child window management
- •Action
- •Result
- •Action
- •Result
- •16.6 Recap
- •Data binding
- •17.1 Data grids
- •Action
- •Result
- •Action
- •Result
- •17.2 Data grid customization
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Odds and ends .NET
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •18.2 Timers
- •Action
- •Result
- •Action
- •Result
- •18.3 Drag and drop
- •Action
- •Result
- •Action
- •Result
- •18.4 ActiveX controls
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •18.5 Recap
- •C# primer
- •A.1 C# programs
- •A.1.1 Assemblies
- •A.1.2 Namespaces
- •A.2 Types
- •A.2.1 Classes
- •A.2.2 Structures
- •A.2.3 Interfaces
- •A.2.4 Enumerations
- •A.2.5 Delegates
- •A.3 Language elements
- •A.3.1 Built-in types
- •A.3.2 Operators
- •A.3.3 Keywords
- •A.4 Special features
- •A.4.1 Exceptions
- •A.4.2 Arrays
- •A.4.3 Main
- •A.4.4 Boxing
- •A.4.5 Documentation
- •.NET namespaces
- •B.1 System.Collections
- •B.2 System.ComponentModel
- •B.3 System.Data
- •B.4 System.Drawing
- •B.5 System.Globalization
- •B.6 System.IO
- •B.7 System.Net
- •B.8 System.Reflection
- •B.9 System.Resources
- •B.10 System.Security
- •B.11 System.Threading
- •B.12 System.Web
- •B.13 System.Windows.Forms
- •B.14 System.XML
- •Visual index
- •C.1 Objects
- •C.2 Marshal by reference objects
- •C.3 Components
- •C.4 Common dialogs
- •C.7 Event data
- •C.8 Enumerations
- •For more information
- •bibliography
- •Symbols
- •Index
CUSTOMIZE THE COLUMNS TO APPEAR IN THE DATA GRID
|
|
Action |
Result |
||
|
|
|
|
|
|
1 |
Locate the OnLoad method in the |
protected override void OnLoad(EventArgs e) |
|||
|
MainForm.cs code window. |
{ |
|||
|
|
|
|
|
. . . |
|
|
|
|
|
|
2 |
Create a column style for the |
// Table style for PhotoAlbum data source |
|||
|
Caption property. |
. . . |
|||
|
How-to |
|
|
// Column styles for PhotoAlbum source |
|
|
Use the DataGridTextBoxCol- |
DataGridColumnStyle captionCol |
|||
|
umn class and assign the Map- |
= new DataGridTextBoxColumn(); |
|||
|
captionCol.MappingName = "Caption"; |
||||
|
pingName to match the Caption |
||||
|
captionCol.HeaderText = "Caption"; |
||||
|
property name. |
|
|
captionCol.Width = 100; |
|
|
|
|
|
|
|
3 |
Create column styles for the |
DataGridColumnStyle validCol |
|||
|
IsImageValid, DateTaken, |
= new DataGridBoolColumn(); |
|||
|
Photographer, and FileName |
validCol.MappingName = "IsImageValid"; |
|||
|
validCol.HeaderText = "Valid?"; |
||||
|
properties as well. |
||||
|
validCol.ReadOnly = true; |
||||
|
How-to |
|
|
validCol.Width = 30; |
|
|
|
|
|
||
|
Use the class specified for each |
DataGridTextBoxColumn dateCol |
|||
|
property in the following table. |
= new DataGridTextBoxColumn(); |
|||
|
|
Column Style Classes |
dateCol.MappingName = "DateTaken"; |
||
|
|
dateCol.HeaderText = "Date Taken"; |
|||
|
|
|
|
|
dateCol.Alignment |
|
|
Property |
Class |
||
|
|
= HorizontalAlignment.Center; |
|||
|
|
|
|
|
|
|
|
IsImageValid |
BoolColumn |
dateCol.Format = "d"; |
|
|
|
DateTaken |
TextBoxColumn |
dateCol.Width = 80; |
|
|
|
|
|||
|
|
Photographer |
TextBoxColumn |
DataGridColumnStyle photographerCol |
|
|
|
FileName |
TextBoxColumn |
= new DataGridTextBoxColumn(); |
|
|
|
photographerCol.MappingName ="Photographer"; |
|||
|
|
|
|
|
photographerCol.HeaderText = "Photographer"; |
|
|
|
|
|
photographerCol.Width = 100; |
|
|
|
|
|
DataGridColumnStyle fileNameCol |
|
|
|
|
|
= new DataGridTextBoxColumn(); |
|
|
|
|
|
fileNameCol.MappingName = "FileName"; |
|
|
|
|
|
fileNameCol.HeaderText = "Image File Name"; |
|
|
|
|
|
fileNameCol.ReadOnly = true; |
|
|
|
|
|
fileNameCol.Width = 200; |
|
|
|
|
|
|
4 |
Add the new column styles to |
// Add the column styles to the table style |
|||
|
the GridColumnStyles property |
albumStyle.GridColumnStyles.AddRange( |
|||
|
of the existing table style object. |
new DataGridColumnStyle[] { |
|||
|
captionCol, |
||||
|
|
|
|
|
|
|
How-to |
|
|
validCol, |
|
|
Use the AddRange method to |
dateCol, |
|||
|
photographerCol, |
||||
|
add all column styles at once. |
||||
|
fileNameCol |
||||
|
|
|
|
|
}); |
|
|
|
|
|
// Assign the table style to the data grid |
|
|
|
|
|
gridPhotoAlbum.TableStyles.Add(albumStyle); |
|
|
|
|
|
} |
|
|
|
|
|
|
This adds the new column styles to the existing table style object. When a data source of type PhotoAlbum is displayed, the new styles specify which columns should
578 |
CHAPTER 17 DATA BINDING |
appear and how they should look. For example, the column style based on the IsImageValid property is as follows:
DataGridColumnStyle validCol = new DataGridBoolColumn();
validCol.MappingName = "IsImageValid";
validCol.HeaderText = "Valid?";
validCol.ReadOnly = true;
validCol.Width = 30;
This column will appear as read-only with a width of 30 pixels. The column header is modified to use the string "Valid?" rather than the property name. This column is our only column based on the DataGridBoolColumn class. This class appears as a check box, which is checked only if the corresponding value is true. In fact, the displayed check box is a three-state check box in order to support a null state in addition to true and false.
The remaining column styles are all based on the DataGridTextBoxColumn class. A summary of this class appears in .NET Table 17.4. Of particular note is the date column, which uses the Format property in this class to display the date value as a short date string. The Alignment property from the base class is also assigned for this column in order to center the displayed date.
DataGridTextBoxColumn dateCol = new DataGridTextBoxColumn();
dateCol.MappingName = "DateTaken";
dateCol.HeaderText = "Date Taken";
dataCol.Alignment = HorizontalAlignment.Center;
dateCol.Format = "d";
dateCol.Width = 80;
Compile and run the application to see your code in action. The application should appear as in figure 17.4 at the start of this section.
.NET Table 17.4 DataGridTextBoxColumn class
The DataGridTextBoxColumn class represents a data grid column style for string data. This class hosts, or manages within a cell of the DataGrid control, a TextBox instance to support editing of string values within the table. This class is part of the System.Windows.Forms namespace, and inherits from the DataGridColumnStyle class. See .NET Table 17.3 for a list of members inherited from this class.
|
Format |
Gets or sets a string specifying how text should be |
|
|
formatted within the cell. |
|
FormatInfo |
Gets or sets an IFormatProvider interface that is |
Public Properties |
|
used to interpret the Format setting. |
|
TextBox |
Gets the TextBox object hosted by this column style. |
|
|
This object is an instance of the DataGridTextBox |
|
|
class, which is derived from TextBox. |
|
|
|
DATA GRID CUSTOMIZATION |
579 |
The DataGridBoolColumn class has an alternate set of properties appropriate for boolean columns. Check out the .NET documentation for detailed information on this class.
TRY IT! Modify the MappingName setting for the table style to use a name other than the "PhotoAlbum" string. Verify that the DataGrid displays the album data in the default format shown in section 17.1.
If you are feeling ambitious, create the AlbumCollection class mentioned earlier in the chapter. This class should derive from the CollectionBase class and encapsulate a set of PhotoAlbum objects. You can copy much of the code from the PhotoAlbum class implementation by modifying the use of Photograph to use PhotoAlbum instead. The default constructor should use the PhotoAlbum.DefaultDir value. You can also add a constructor that accepts a directory name. Modify the MyAlbumData application to use this class to display a collection of PhotoAlbum objects. Create a second DataGridTableStyle object to configure how an AlbumCollection object should look as opposed to our style for the PhotoAlbum object. Add this new style to the TableStyles property for the grid, and verify that the correct table style displays based on the type of data source assigned to the control.
As we mentioned earlier, some of our columns are configured as read-only while some of them can be edited. You can see this in the existing application by clicking on an editable cell and modifying its contents. Unfortunately, changing the contents of a cell has no effect at the moment since we are not saving the modified values in our album file. Saving such changes properly requires the use of the IEditableObject interface, which is our next topic.
17.3EDITABLE OBJECTS
So far we have bound a PhotoAlbum object to our DataGrid control and customized the table and columns that appear in the grid. At the moment, any changes made by the user to the PhotoAlbum object are discarded when the displayed album changes or the application exits. This is not really desirable, so let’s discuss how to properly save changes made to the grid.
There are three areas for discussion here. The first is the way in which data grids support editing of their contents. The second is how to enable such support in the Photograph objects displayed by our table. The third is how to actually save the data into an album file once such editing is possible. We will discuss each topic separately.
17.3.1THE IEDITABLEOBJECT INTERFACE
The editing of rows in a grid is handled by the DataGrid control directly using the discovered properties associated with our PhotoAlbum object. When the user changes a caption, the Caption property is called automatically by the grid to
580 |
CHAPTER 17 DATA BINDING |
update the corresponding Photograph object with the new value. Similarly, when the photographer is changed, the Photographer property is called. The control even handles the DateTaken property gracefully so that an invalid date value is never assigned to the object.
The problem is that our updated Photograph objects are never saved in the corresponding album file. A quick and easy solution would be to forcibly save the album whenever a new album is selected. For example, the SelectedIndexChanged event handler could be altered as follows, with the modified lines in bold.
private void cmbxAlbum_SelectedIndexChanged (object sender, System.EventArgs e)
{
string albumFile = cmbxAlbum.SelectedItem.ToString();
// Forcibly save previous album – not our approach if (_album != null)
{
_album.Save(); _album.Dispose();
}
_album.Clear(); try
. . .
}
This would ensure that the album is always saved, even if the user does not wish to save the changes. Not the best solution, although it does work. A better solution would only save the album if it has been modified, and give the user an opportunity to elect not to save the changes. In order to do this we must know when a Photograph has been changed, and then use this information when a new album is selected.
Implementing this change requires the IEditableObject interface, summarized in .NET Table 17.5. This interface defines a mechanism for modifying an object in a transactional manner, so that either all changes to an object are made or none of the changes are made. This is especially important in databases, where the fields of a row may be dependent on one another, or in multi-user environments, where different users may wish to update the same object at the same time. For example, in a customer order database, you would not want to modify the shipping method without also updating the shipping costs. The IEditableObject interface is used to ensure that this happens.
As an example, the DataRowView class in the System.Data namespace supports the IEditableObject interface to ensure transactional update to the rows in a database. We are not building a database here, but we would like to update the toAlbum object in a consistent manner. The IEditableObject interface provides a way for us to do this over the course of this section.
EDITABLE OBJECTS |
581 |
.NET Table 17.5 IEditableObject interface
The IEditableObject interface represents an interface for performing transactional operations on an object. This interface is used by various .NET classes such as the Windows Forms DataGrid control to allow an object to track and enforce transactional behavior. This interface is part of the System.ComponentModel namespace.
|
BeginEdit |
Initiates an edit operation on an object. |
|
CancelEdit |
Discards any changes made since the last edit operation |
|
|
began, including any new objects added to the list with |
Public Methods |
|
the IBindingList.AddNew method. |
|
|
|
|
EndEdit |
Finalizes an edit operation. Any changes made since the |
|
|
last edit operation began are made permanent in the |
|
|
object, including any new objects added with the |
|
|
IBindingList.AddNew method. |
|
|
|
17.3.2SUPPORTING THE IEDITABLEOBJECT INTERFACE
Looking at the Photograph class, there are four modifiable properties. These are the Caption, Photographer, DateTaken, and Notes properties. As a result, these are the properties we need to consider in our IEditableObject implementation. The following table summarizes the implementation of the required methods:
Implementation of IEditableObject methods for the Photograph class
Method |
Implementation notes |
|
|
BeginEdit |
Should record the existing values of the modifiable properties and |
|
place the photo in an editing state. |
CancelEdit |
Should reinstate the recorded values from BeginEdit, and place the |
|
photo in a nonediting state. |
EndEdit |
Should discard the recorded values from BeginEdit, note if the |
|
photo has been changed, and place the photo in a nonediting state. |
|
|
Our implementation will not be something you would present at a computer science convention. In particular, a Photograph object can be modified without using our edit methods, which kind of defeats the whole purpose of the interface. The code presented here is intended to illustrate the behavior of these methods and indicate how they are used by the DataGrid control.
With this excuse in mind, let’s see how to support the editable object interface for our Photograph class.
582 |
CHAPTER 17 DATA BINDING |