D ATA GRIDS 567.NET Table 17.1 DataGrid class The DataGrid class represents a control that displays a collection of data as a grid of rows and columns.. DataMember Gets or sets which li
Trang 1Shows the name of each column.
The ListView class discussed in chapter 14 can also be used to present a table ofinformation The Windows Forms namespace provides explicit classes to representthe rows and columns in a list view As you may recall, each item, or row, in the list isrepresented by the ListViewItem class instance, and each column by a ListView- SubItem instance and is presented based on a ColumnHeader instance
As illustrated by figure 17.2, the DataGrid class takes a somewhat differentapproach The contents of the grid are contained in a single collection, such as anarray, a photo album, or a database table Classes exist to configure the style in whichthe provided data is displayed, including colors, column ordering, and other proper-ties We will discuss the details of these style classes later in the chapter
Figure 17.2 This chapter will discuss many of the terms and classes related to data grids.
Trang 2D ATA GRIDS 567
.NET Table 17.1 DataGrid class
The DataGrid class represents a control that displays a collection of data as a grid of rows and columns The data displayed and the style in which it is presented is fully configurable This class is part of the System.Windows.Forms namespace, and inherits from the Control class See NET Table 4.1 on page 104 for a list of members inherited by this class.
CaptionVisible Gets or sets whether the caption is visible
Other properties related to this and other grid areas are also provided.
CurrentCell Gets or sets a DataGridCell structure
representing the cell in the grid that has the focus.
CurrentRowIndex Gets or sets the index of the selected row DataMember Gets or sets which list in the assigned data
source should be displayed in the grid DataSource Gets or sets the source of data for the grid Item Gets or sets the value of a cell This property is
the C# indexer for this class.
ReadOnly Gets or sets whether the grid is in read-only
BeginEdit Attempts to begin an edit on the grid.
HitTest Returns location information within the grid of
a specified point on the screen This works much like the HitTest method in the MonthCalendar class.
SetDataBinding Assigns the DataSource and DataMember
properties to the given values at run time Unselect Deselects a specified row.
Public Events
CurrentCellChanged Occurs when the current cell has changed DataSourceChanged Occurs when a new data source is assigned Navigate Occurs when the user navigates to a new
table.
Scroll Occurs when the user scrolls the data grid.
Trang 317.1.1 C REATING THE M Y A LBUM D ATA PROJECT
While the DataGrid class includes numerous members for customizing the ance and behavior of the control, it is possible to create a very simple grid with only afew lines of code We will begin with such an application, and enhance it over thecourse of the chapter
appear-The following table lays out the creation and initial layout of our new application
C REATE THE M Y A LBUM D ATA PROJECT
1 Create a new project and solution in
Visual Studio NET called
“MyAlbumData.”
The new solution is shown in the Solution Explorer window, with the default Form1.cs [Design] window displayed.
2 Rename the Form1.cs file and
related class file to our standard MainForm class and assign some initial settings for this form.
3 Drag a Label , ComboBox ,
DataGrid , and Button control onto the form Arrange these controls as shown in the graphic.
4 Create a Click event handler for
the Close button to shut down the application.
private void btnClose_Click (object sender, System.EventArgs e) {
Close();
}
Settings Property Value
(Name) MainForm Size 450, 300 Text MyAlbumData
Settings Control Property Value
Label Text &Album
ComboBox (Name) cmbxAlbum
Anchor Top, Left, Right
DataGrid
(Name)
gridPhoto-Album Anchor Top, Bottom,
Left, Right
Button
(Name) btnClose Anchor Bottom, Right Text &Close
Trang 4D ATA GRIDS 569
With our initial window in place, we are ready to display some album data in thewindow
17.1.2 D ISPLAYING DATA IN A DATA GRID
Our main window in the MyAlbumData application contains a combo box and a datagrid Our ComboBox control will contain a list of the albums located in the defaultalbum directory, while our DataGrid control will display the contents of the selectedalbum This section will make the changes required for this behavior Section 17.2 willlook at customizing the information displayed by the grid
Fortunately, our existing MyPhotoAlbum project can do most of the work here.The changes required are detailed in the following steps
Set the version number of the MyAlbumData application to 17.1.
D ISPLAY ALBUM DATA IN THE M Y P HOTO A LBUM APPLICATION
1 In the Solution Explorer window,
add the MyPhotoAlbum project
to the solution and reference it from the MyAlbumData project.
2 At the top of the MainForm.cs
code window, add a using statement for the new project and the System.IO namespace.
using System.IO;
using Manning.MyPhotoAlbum;
3 Define a private PhotoAlbum
field within the MainForm class.
private PhotoAlbum _album;
Trang 54 Override the OnLoad method to:
a Add the version number to the title bar.
b Initialize the photo album.
c Set the album file names to appear in the ComboBox control.
Note: The assignment of the
DataSource property here is an example of data binding In this case, we are binding the collec- tion of objects for the ComboBox control to an array of directory strings.
protected override void OnLoad(EventArgs e) {
Version ver = new Version(Application.ProductVersion); Text = String.Format(
"MyAlbumData {0:#}.{1:#}", ver.Major, ver.Minor);
_album = new PhotoAlbum();
cmbxAlbum.DataSource = Directory.GetFiles( PhotoAlbum.DefaultDir, "*.abm");
}
5 Handle the
SelectedIndex-Changed event for the ComboBox object.
private void cmbxAlbum_SelectedIndexChanged (object sender, System.EventArgs e) {
6 In this handler, retrieve the string
selected in the combo box and dispose of any existing album.
string albumFile = cmbxAlbum.SelectedItem.ToString();
if (_album != null) _album.Dispose();
7 Open the selected album file _album.Clear();
try { _album.Open(albumFile);
8 If the album opens successfully,
assign the album title as the caption text for the DataGrid control.
gridPhotoAlbum.CaptionText = _album.Title; }
9 If the album cannot be opened,
clear the album and assign an error message as the caption text.
catch (Exception) {
_album.Clear();
gridPhotoAlbum.CaptionText = "Unable to open album";
}
10 Bind the contents of the
resulting album to appear in the DataGrid control.
gridPhotoAlbum.SetDataBinding(null, null); gridPhotoAlbum.SetDataBinding(_album, null); }
Note: Since the value of the _album field does not actually change, we force the data grid to reload the album data by binding it to null and then rebinding
to our PhotoAlbum instance We will do this a bit more elegantly later in the chapter.
Trang 6D ATA GRIDS 571
This code opens the selected album and assigns the title of the album to appear in thecaption area of the data grid The collection of photographs in the album is bound tothe contents of the data grid The result is shown in figure 17.3
The caption area at the top of the control is assigned using the CaptionText erty In the figure, the title of the colors.abm album is “Lots of Colors” as is shown inthe caption The data source for the grid is assigned using the SetDataBindingmethod This method has the following signature:
public void SetDataBinding(object dataSource, string dataMember);
The dataSource parameter is assigned to the DataSource property of the control,while the dataMember parameter is assigned to the DataMember property In ourapplication, the control recognizes our PhotoAlbum object as an IList interfacecontaining a collection of Photograph objects This is performed internally usingthe GetType method available on all object instances
The properties of the Photograph object are determined internally using the bers of the System.Reflection namespace These properties are then used as the col-umns in the grid, and each Photograph in the album is presented as a row in the grid
mem-We will not discuss the System.Reflection namespace in detail here Thisnamespace permits NET objects such as the DataGrid control to determine the type
of object and members of that type at runtime In this way our data grid can stand how the PhotoAlbum object is organized, and automatically create an appro-priate grid structure
under-Since the order of columns in the grid corresponds to the internal order of erties in the PhotoAlbum class, your columns might be ordered differently than isshown in figure 17.3 Also note that properties which only provide a get accessmethod are treated as read-only, while properties with both a get and set accessmethod are modifiable As a result the Image and IsImageValid columns in our gridare read-only, while the Photographer and Notes columns can be modified We willlook at how to update the class with these changes shortly
prop-Figure 17.3 The DataGrid control sup- ports two types of entries by default Boolean values, such
as the IsImageValid property, appear as check boxes All other properties display the result of their ToString prop- erty as a text entry.
Trang 7Back to our SetDataBinding method, there are a number of different classesthat can serve as a source of data, depending on the type of C# interfaces they support.
A summary of data sources for the DataGrid class is given in the following table
Data sources for the data grid control
IList A homogenous collection of objects The
first item in the list determines the type
The first property in that type is displayed as the only column when bound to a data grid.
This includes any simple array in C#, and all classes based on the Array object.
typed IList A typed collection, such as our
PhotoAlbum class The type returned by the Item property is used as the assigned type, and all properties in this type can be displayed in a data grid
These can be bound to a data grid only at run time.
Most notably, classes derived from CollectionBase , such as our PhotoAlbum class Other classes with an indexer of a fixed type will also work here.
IList and
IComponent
With both interfaces available, the class may appear in Visual Studio NET in the component tray and be bound to a data grid at design time.
Integrating a collection class with Visual Studio NET is beyond the scope of this book A control can be added to the Toolbox using the Customize entry in the Toolbox window’s popup menu This often requires members of the System.Windows.Forms.Design namespace to properly interact with the Windows Forms Designer and Property windows.
IBindingList This interface permits two-way
notification of changes, both from the control to the class and from the class to the control.
The DataView class in the System.Data namespace implements this interface, allowing a data grid to update its contents when the underlying database is modified.
IEditableObject Classes implementing this interface are
permitted to roll back, in a oriented manner, a changes made to an object.
transaction-a. The term transaction indicates that a series of steps either fully completes or appears to never have
hap-pened For example, when transferring money between bank accounts, you must debit the first account and then credit the second account By making such a transfer transactional, you ensure that the first account is never debited without guaranteeing that the second account is also credited Aborting an
operation part-way through the required steps is said to roll back, or undo, the operation.
The DataRowView class is a customized view of a row that supports transactional changes to the elements of the row.
IDataErrorInfo Objects can offer custom error
information that controls can bind to.
The DataRowView class supports this interface as well in order to provide appropriate feedback in a DataGrid control when an error occurs.
Trang 8D ATA GRID CUSTOMIZATION 573
For our purposes, we will continue to use the typed IList interface supported byour PhotoAlbum class Later in the chapter we will add support for the IEdit- ableObject interface in order to properly save modifications made in our data grid.The next section discusses various ways of customizing what appears in the grid
17.2 D ATA GRID CUSTOMIZATION
One of the obvious drawbacks of letting NET do all the work in laying out the tents of a data grid is that we have no control over the selection and order of columns
con-to appear in the grid In this section we will look at how con-to cuscon-tomize the contents of
a data grid for a particular data source using table and column styles This will enable
us to build the application shown in figure 17.4
In our current application we display a single kind of table, namely one based on thePhotoAlbum class In general, the data displayed in a data grid may vary depending
on the actions of the user For example, just as our ListView control in chapter 14displayed both albums and photographs, we could create an AlbumCollectionclass derived from CollectionBase to contain the set of albums located in a givendirectory We could then use this class to display both album files and the contents ofalbums in our data grid
More commonly, a data grid is filled with information from a database, whichincludes one or more tables An employee database at a company might have one tablecontaining department information, another table containing the employees assigned
to each department, and another containing the projects each employee is assigned to
A single data grid could display all three types of tables based on a set of options, and
it would be nice to customize the appearance of each type of table
Figure 17.4 This data grid displays only certain properties of photographs, and the size and content of each column are somewhat customized compared with the application
in the previous section.
Trang 9The TableStyles property in the DataGrid class supports this notion of figuring the appearance of multiple tables This property contains a collection of Data- GridTableStyleobjects, each of which describes the configuration for a table thatmight be displayed by the grid The DataGridTableStyleclass, in turn, provides aGridColumnStylesproperty that contains a collection of DataGridColumnStyleobjects We will discuss each of these classes separately.
con-17.2.1 C USTOMIZING TABLE STYLES
The DataGridTableStyle class permits a custom style for a specific type of table
to be defined Many of the members of this class are duplicates of similar members inthe DataGrid class The members of the active table style always override the defaultsettings for the data grid A summary of this class appears in NET Table 17.2
.NET Table 17.2 DataGridTableStyle class
The DataGridTableStyle class represents the style in which to display a particular table that can appear in a DataGrid control It configures not only the general properties for the table but also the individual columns that should appear in the table This class is part of the System.Win- dows.Forms namespace, and inherits from the System.ComponentModel.Component class.
Public Properties
AllowSorting Gets or sets whether sorting is allowed on the grid when this
DataGridTableStyle is used.
Color
AlternatingBack-Gets or sets the background color for alternating rows in the grid when this DataGridTableStyle is used.
DataGrid Gets or sets the DataGrid control containing this style GridColumn-
Styles
Gets or sets the collection of DataGridColumnStyle objects
to use for the grid when this style is used.
LinkColor Gets or sets the color of link text to use in the grid when this
style is used.
MappingName Gets or sets the name used to associate this table style with
a specific data source For a data source based on an IList interface, the name of the list is specified, as in
myList.GetType().Name For a data source based on a DataSet instance, a valid table name in the data set should
be specified.
ReadOnly Gets or sets whether columns can be edited in the grid when
this style is used.
Width
RowHeader-Gets or sets the width of row headers in the grid when this style is used.
Public Methods
BeginEdit Requests an edit operation on a row in the grid.
EndEdit Requests an end to an edit operation in the grid.
ResetBackColor Resets the BackColor property to its default value A
number of other reset methods exist with a similar purpose.
Public Events
Changed
AllowSorting-Occurs when the AllowSorting property value changes A number of other changed events exist with a similar purpose.
Trang 10D ATA GRID CUSTOMIZATION 575
There are two keys to understanding the DataGridTableStyle class The first isthe MappingName property When a new source of data is assigned to a DataGridcontrol, the list of table styles is examined to locate a style whose MappingName set-ting matches the name of the table If one is found, then that style is used to displaythe grid If no match is found, then the default settings for the grid control are used
It is an error to assign identical mapping names to multiple styles within the samedata grid
The second key to understanding this class is the GridColumnStyles property.This property is a collection of DataGridColumnStyle objects and specifies theselection and order of columns to display in the grid If the GridColumnStylesproperty is null, then the default set of columns is displayed
We can use the DataGridTableStyle class to modify the appearance of ourDataGrid control when a PhotoAlbum is displayed We will make the very simplechange of providing an alternating background color for the table The steps requiredare presented in the following table
Set the version number of the MyAlbumData application to 17.2
This very simple change causes the application to display as is shown in figure 17.5
Of course, the AlternatingBackColor and RowHeaderWidth properties areavailable in the DataGrid class and can be set explicitly for this class Assigning them
in a table style uses these properties only when a matching table name is displayed, inthis case a PhotoAlbum object
Note that our choice of light gray may not work very well with some user’s desktopcolors You can use an alternate color if you prefer, or a system color such as System- Colors.ControlLight In your own applications, make sure you carefully select
P ROVIDE A CUSTOM TABLE STYLE WHEN A P HOTO A LBUM IS DISPLAYED
1 In the MainForm.cs code
window, create a table style instance in the OnLoad method.
Note: A table style can also be
created in the [Design] window
by clicking on the … button in
the TableStyles property
Here we elect to create the table style by hand.
protected override void OnLoad(EventArgs e) {
// Table style for PhotoAlbum data source DataGridTableStyle albumStyle
= new DataGridTableStyle();
2 Configure the new style for a
PhotoAlbum table with an alternating background color of LightGray
albumStyle.MappingName = "PhotoAlbum"; albumStyle.AlternatingBackColor = Color.LightGray;
albumStyle.RowHeaderWidth = 15;
3 Assign the new style to the
existing DataGrid control.
// Assign the table style to the data grid gridPhotoAlbum.TableStyles.Add(albumStyle); }
Trang 11color choices for settings such as this, and use system settings where possible coding a specific color such as we do here is not typically recommended, since differentusers may configure their desktops to appear using different sets of conflicting colors.
Hard-Note that the assignment of the MappingName is critical here Using a name otherthan PhotoAlbum would have no effect on our table since the name of the tablewould not match the mapping name of the table style
Of course, our table still uses the default set of columns since we have not yetassigned any column styles to the GridColumnStyles property Customizing thecolumns in our table is our next topic
Now that we know how to customize the properties of a table, let’s look at how tocustomize the columns that appear in the table The DataGridColumnStyle class
is used for this purpose, and is summarized in NET Table 17.3 This is an abstractclass from which various types of columns are derived The NET Framework cur-rently provides classes to represent boolean and text columns, namely the Data- GridBoolColumn and DataGridTextBoxColumn classes
Figure 17.5 This figure shows an alternating background color of light gray
to present a ledger-like appearance.
Trang 12D ATA GRID CUSTOMIZATION 577
The order in which columns are assigned to a table style determines the order inwhich they will appear in the data grid We will use this feature to extend the tablestyle we created for our form to display only a subset of the available columns.The code to make this change is detailed in the following table Note that thiscode uses the DataGridBoolColumnand DataGridTextBoxColumnclasses Wewill discuss these classes in more detail in a moment
.NET Table 17.3 DataGridColumnStyle class
The DataGridColumnStyle class represents a specific column that should appear when a specific style table is displayed in a DataGrid control This object is typically contained within
a DataGridTableStyle object, and indicates the position and style for the corresponding umn when a table of the specified type is displayed This class is part of the System.Win- dows.Forms namespace, and inherits from the System.ComponentModel.Component class.
col-A DataGridColumnStyle object cannot be instantiated, as this is an abstract class The DataGridBoolColumn and DataGridTextBoxColumn classes derived from this class are used to represent a column of boolean or textual values, respectively Custom column styles derived from this class may also be created.
Public Properties
Alignment Gets or sets the alignment of data within the column DataGridTableStyle Gets the table style containing this column style HeaderText Gets or sets the header text for this column when the
associated table style is used.
MappingName Gets or sets the name used to associate this column
style with a specific data value in an associated data source For an IList data source, a valid property name
in the list should be specified For a DataSet data source, a valid column name in the associated table should be provided
NullText Gets or sets the text that is displayed when the column
contains a null reference.
PropertyDescriptor Gets or sets the PropertyDescriptor object
containing attributes of the data displayed by this column style.
ReadOnly Gets or sets whether to treat the column as read-only Width Gets or sets the width in pixels for this column.
Public Methods
ResetHeaderText Resets the HeaderText property to its default value,
which is a null reference.
Public Events
AlignmentChanged Occurs when the Alignment property for the column
style changes.
FontChanged Occurs when the column’s font changes A number of
other changed events exist with a similar purpose.
Trang 13This 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
C USTOMIZE THE COLUMNS TO APPEAR IN THE DATA GRID
1 Locate the OnLoad method in the
MainForm.cs code window.
protected override void OnLoad(EventArgs e) {
// Table style for PhotoAlbum data source
// Column styles for PhotoAlbum source DataGridColumnStyle captionCol = new DataGridTextBoxColumn();
How-to
Use the class specified for each property in the following table.
DataGridColumnStyle validCol = new DataGridBoolColumn();
validCol.MappingName = "IsImageValid"; validCol.HeaderText = "Valid?";
validCol.ReadOnly = true;
validCol.Width = 30;
DataGridTextBoxColumn dateCol = new DataGridTextBoxColumn();
dateCol.MappingName = "DateTaken";
dateCol.HeaderText = "Date Taken";
dateCol.Alignment = HorizontalAlignment.Center;
dateCol.Format = "d";
dateCol.Width = 80;
DataGridColumnStyle photographerCol = 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
the GridColumnStyles property
of the existing table style object.
validCol, dateCol, photographerCol, fileNameCol });
// Assign the table style to the data grid gridPhotoAlbum.TableStyles.Add(albumStyle); }
Column Style Classes Property Class
IsImageValid BoolColumn DateTaken TextBoxColumn Photographer TextBoxColumn FileName TextBoxColumn
Trang 14D ATA GRID CUSTOMIZATION 579
appear and how they should look For example, the column style based on the ageValid property is as follows:
DataGridColumnStyle validCol = new DataGridBoolColumn();
The remaining column styles are all based on the DataGridTextBoxColumnclass A summary of this class appears in NET Table 17.4 Of particular note is thedate 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 assignedfor this column in order to center the displayed date
DataGridTextBoxColumn dateCol = new DataGridTextBoxColumn();
.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.
Public Properties
Format Gets or sets a string specifying how text should be
formatted within the cell.
FormatInfo Gets or sets an IFormatProvider interface that is
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
Trang 15The DataGridBoolColumn class has an alternate set of properties appropriate forboolean columns Check out the NET documentation for detailed information onthis 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 bum data in the default format shown in section 17.1
al-If you are feeling ambitious, create the AlbumCollection class tioned earlier in the chapter This class should derive from the Collec- tionBase class and encapsulate a set of PhotoAlbum objects You cancopy much of the code from the PhotoAlbum class implementation bymodifying the use of Photograph to use PhotoAlbum instead The de-fault constructor should use the PhotoAlbum.DefaultDir value Youcan also add a constructor that accepts a directory name Modify the MyAl-bumData application to use this class to display a collection of PhotoAlbum
men-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 forthe grid, and verify that the correct table style displays based on the type ofdata 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 aneditable cell and modifying its contents Unfortunately, changing the contents of acell has no effect at the moment since we are not saving the modified values in ouralbum file Saving such changes properly requires the use of the IEditableObjectinterface, which is our next topic
There are three areas for discussion here The first is the way in which data gridssupport editing of their contents The second is how to enable such support in thePhotograph objects displayed by our table The third is how to actually save the datainto an album file once such editing is possible We will discuss each topic separately
17.3.1 T HE IE DITABLE O BJECT INTERFACE
The editing of rows in a grid is handled by the DataGrid control directly using thediscovered properties associated with our PhotoAlbum object When the userchanges a caption, the Caption property is called automatically by the grid to
Trang 16E DITABLE OBJECTS 581
update the corresponding Photograph object with the new value Similarly, whenthe photographer is changed, the Photographer property is called The controleven handles the DateTaken property gracefully so that an invalid date value is neverassigned to the object
The problem is that our updated Photograph objects are never saved in the responding album file A quick and easy solution would be to forcibly save the albumwhenever a new album is selected For example, the SelectedIndexChanged eventhandler 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
Implementing this change requires the IEditableObject interface, rized in NET Table 17.5 This interface defines a mechanism for modifying an object
summa-in a transactional manner, so that either all changes to an object are made or none ofthe changes are made This is especially important in databases, where the fields of arow may be dependent on one another, or in multi-user environments, where differentusers may wish to update the same object at the same time For example, in a customerorder database, you would not want to modify the shipping method without alsoupdating the shipping costs The IEditableObject interface is used to ensure thatthis happens
As an example, the DataRowView class in the System.Data namespace ports the IEditableObject interface to ensure transactional update to the rows in
sup-a dsup-atsup-absup-ase We sup-are not building sup-a dsup-atsup-absup-ase here, but we would like to updsup-ate the toAlbum object in a consistent manner The IEditableObject interface provides
Pho-a wPho-ay for us to do this over the course of this section
Trang 1717.3.2 S UPPORTING THE IE DITABLE O BJECT INTERFACE
Looking at the Photograph class, there are four modifiable properties These are theCaption, Photographer, DateTaken, and Notes properties As a result, these arethe properties we need to consider in our IEditableObject implementation Thefollowing table summarizes the implementation of the required methods:
Our implementation will not be something you would present at a computer scienceconvention In particular, a Photograph object can be modified without using ouredit methods, which kind of defeats the whole purpose of the interface The code pre-sented here is intended to illustrate the behavior of these methods and indicate howthey are used by the DataGrid control
With this excuse in mind, let’s see how to support the editable object interfacefor our Photograph class
.NET Table 17.5 IEditableObject interface
The IEditableObject interface represents an interface for performing transactional tions 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
opera-is part of the System.ComponentModel namespace.
Public Methods
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 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.
Implementation of IEditableObject methods for the Photograph class
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.
Trang 18E DITABLE OBJECTS 583
Set the version number of the MyPhotoAlbum library to 17.3.
S UPPORT THE IE DITABLE O BJECT INTERFACE IN THE P HOTOGRAPH CLASS
1 In the Photograph.cs code
window, indicate that we will use members of the System.ComponentModel namespace.
using System.ComponentModel;
2 Add IEditableObject to the
list of supported interfaces for this class.
public class Photograph : IDisposable,
IEditableObject
{ .
3 Add internal fields to track when
the object is in an editable state
or has been modified.
private bool _modified;
private bool _editing;
4 Initialize these fields in the
constructor.
public Photograph(string fileName) {
_modified = false;
_editing = false;
}
5 Reset these values when the
photograph is saved into a StreamWriter object.
public void Write(StreamWriter sw) {
_modified = false;
_editing = false;
}
6 Add internal fields to record the
existing values of the four modifiable properties.
private string _editCaption;
private string _editPhotographer;
private DateTime _editDateTaken;
private string _editNotes;
7 Implement the BeginEdit
method.
How-to
If editing is not already enabled, record the current values and enable editing.
Note: Ideally, we would permit
nesting of these calls In this example we will avoid this addi- tional complexity.
public void BeginEdit() {
if (!_editing) {
8 Implement the CancelEdit
method.
How-to
If editing is enabled, restore the recorded values and disable editing.
public void CancelEdit() {
if (_editing) {
Trang 19The IEditableObject interface is now fully implemented Another useful change
in our library is the ability to identify if a PhotoAlbum, and not just a Photograph,has been modified We can do this by continuing the previous steps to add aHasEdits method in the PhotoAlbum class
The MyPhotoAlbum library is ready to go Make sure the library compiles with noerrors The next step is to make use of these changes in our MyAlbumData applica-tion This is taken up in the next section
17.3.3 U SING EDITABLE OBJECTS
Typically, you do not actually use the editable object methods directly These are usedinternally by Windows Forms as required for the task at hand In this case, our Data- Grid control automatically recognizes that our PhotoAlbum object supports thisinterface, and calls BeginEdit whenever a user initiates a change to a row in the grid
If a user cancels an edit by pressing the Esc key, then CancelEdit is called When the
9 Implement the EndEdit
method.
How-to
If editing is enabled, record whether the data has been modified and disable editing.
public void EndEdit() {
if (_editing) {
_modified |= ((Caption != _editCaption) || (Photographer != _editPhotographer) || (DateTaken != _editDateTaken) || (Notes != _editNotes));
_editing = false;
} }
10 Also add a HasEdits property to
report whether the object has been modified.
public bool HasEdits {
get { return _modified; } }
A DD A H AS E DITS PROPERTY TO THE P HOTO A LBUM CLASS
11 In the PhotoAlbum.cs code
window, Implement a HasEdits method in this class.
How-to
Use the Photograph.HasEdits method to determine the appropriate result.
public bool HasEdits {
get { foreach (Photograph p in this) {
if (p.HasEdits) return true;
} // No edits found return false;
} }
Trang 20E DITABLE OBJECTS 585
user finishes an edit by pressing the Enter key or selecting an alternate row, theEndEdit method is invoked The EndEdit method makes the changes to the objectpermanent within the object itself
In most applications, there is an operation or class that coordinates the ory version of an object with the permanent version of an object In our application,the in-memory version is our PhotoAlbum class, while the permanent version is ouralbum file The Save method updates the album file with the version in memory,while the Open method fills the in-memory version with the recorded version in thealbum file
in-mem-This is true in the System.Data namespace as well While we have avoided cussing this namespace in any real depth, it is useful to understand how the classes inthis namespace relate to our discussion The abstract DataAdaptor class is the coor-dinator between the in-memory version of the database, typically a DataSet instance,and the permanent version is an external database The DataAdaptor class provides
dis-a Fill method to populate a DataSet with the external values, and an Updatemethod to save modifications in the DataSet into the external database
For our purposes, we have provided the HasEdits method in our in-memoryobjects in order to identify whether any changes must be saved into the album file Wecan do this in the SelectedIndexChanged event handler before the new album isbound to our data grid
The following table details the steps required to save our PhotoAlbum instanceinto its associated album file:
Set the version number of the MyAlbumData application to 17.3.
S AVE A MODIFIED ALBUM
1 In the MainForm.cs window,
create a new SaveChanges method to store any changes to the displayed album into the album file.
private void SaveChanges() {
if (_album.HasEdits) {
DialogResult result = MessageBox.Show( "Do you wish to save your changes " + "to the album \'" + _album.Title + "\'?",
"Save Changes?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.Yes) _album.Save();
} }
Trang 21With these modifications, our edits are now saved Compile and run to verify that theapplication works as advertised.
More NET Additional information on the DataGrid control is available in the NET
documentation and in various sample programs freely available on the ternet One such example is the CdrCatalog program built by AndrewSkowronski that is available at http://cdrcatalog.sourceforge.net/ This pro-gram manages offline media, such as a collection of recordable compactdiscs, and makes use of a number of Windows Forms classes in addition tothe DataGrid control The data source used by this application is a
In-DataSet object loaded from a local XML file
17.4 S IMPLE DATA BINDING
Binding data to a data grid is referred to as complex data binding, since multiple values
are bound to a single control Complex data binding also refers to binding objects to
a list control, such as a list box or combo box For example, if we had actually mented the AlbumCollection class I keep mentioning to contain an array of Pho- toAlbum objects, then the line to assign the DataSource for our combo box couldinstead be written as:
cmbxAlbum.DataSource = myAlbumCollection;
cmbxAlbum.DataMember = "FileName";
This would display the collection of PhotoAlbum objects in the tion variable in the cmbxAlbum combo box, using the FileName property as the
myAlbumCollec-2 Use this new method when the
user selects a new album in the ComboBox control.
private void cmbxAlbum_SelectedIndexChanged (object sender, System.EventArgs e) {
string albumFile = cmbxAlbum.SelectedItem.ToString();
if (_album != null) {
3 Also make sure any changes are
saved when the application exits.
protected override void OnClosing (CancelEventArgs e)
{ SaveChanges();
base.OnClosing(e);
}
Trang 22S IMPLE DATA BINDING 587
name to display The result would be the same as that which currently appears in ourapplication This type of binding to a list control can also be done with databaseobjects, such as binding the entries in a ListBox control to the set of customernames found in one column of a database table
Simple data binding is used for binding single property values to a specific data
source This type of binding is supported by the Control class directly, and is fore inherited by and available in all controls in Windows Forms The concepts andtechniques for so-called simple data binding are fairly identical to those we havealready discussed for the DataGrid control
there-In this section we will alter our application to permit some simple data binding
to a photo album We will see how to perform simple binding; update bound controlsdynamically, including the image associated with a photograph; and save changes tobound controls
17.4.1 A LTERING THE M Y A LBUM D ATA APPLICATION
Before we get into the details of exactly how simple data binding is performed, let’swhip through some changes to our MyAlbumData application in preparation for thisdiscussion The change we will make is to place our existing DataGrid controlwithin a TabPage object, and add a new tab to display the Photograph informa-tion for an album one photo at a time Figure 17.6 shows the modified application
we will build throughout this and the next few sections
Figure 17.6 These controls on the Photo tab are bound to their
corre-sponding values in a Photograph object.
Trang 23In this section we will simply move our existing DataGrid control into an Albumtab, and create a Photo tab containing the controls shown in the figure The follow-ing steps implement this change.
Set the version number of the MyAlbumData application to 17.4.
C REATE THE CONTROLS WITHIN A TAB CONTROL OBJECT
1 In the MainForm.cs [Design] window,
alter the Size property for the form to
be 450×350.
2 Move the existing DataGrid control to
exist within a tab page.
How-to
a Create a tab control containing two tab pages.
b Set their properties as shown.
c Move the data grid into the Album tab page, and set its Dock property
to Fill
Settings Control Property Value
TabControl (Name) tcMain
Anchor Top, Bottom,
Left, Right TabPage (Album) (Name) tabAlbum
TabPage (Photo) (Name) tabPhoto
Text Photo
Trang 24S IMPLE DATA BINDING 589
3 Create and position the controls for
the Photo tab page as shown in the graphic.
Note: In the rather long Settings table
here, the Label controls are not shown For these controls, use the default name, the text shown in the graphic, and the same Anchor prop- erty as the related control.
Also note that all TextBox controls should have their Text property set to
an empty string.
Note: When assigning the Anchor property, you may find it easier to first create and posi- tion the controls and then use the following technique:
a Select a group of related controls by ging the mouse over a region of the form.
drag-b Assign the Anchor property for all controls
at once.
Settings Control Property Value
FileName (Name) txtFileName
ReadOnly True PictureBox (Name) pboxPhoto
Anchor Top, Bottom,
Left, Right BorderStyle FixedSingle Prev (Name) btnPrev
Anchor Bottom, Left Text Pre&v
Anchor Bottom, Right Text Nex&t Caption (Name) txtCaption
Anchor Top, Right Photo-
grapher
(Name)
txtPhoto-grapher Anchor Top, Right Date Taken (Name) dtpDateTaken
Anchor Top, Right Format Short Notes (Name) txtNotes
Anchor Top, Bottom,
Right Multiline True
Trang 25That took a bit of work As we mentioned earlier in the book, you can reduce theamount of time spent drawing forms in Visual Studio by sketching out your controls
on paper before using Visual Studio While not illustrated in these pages, I really didsketch the Photo tab page by hand before creating this page in Visual Studio NET.With our controls defined, we are ready for our data binding discussion
17.4.2 P ERFORMING SIMPLE BINDING
The binding of controls to data involves four distinct roles, each with a ing class These correspond to the work performed by the NET Framework onbehalf of bound controls, namely tracking which data has been bound to which con-trol, managing a bound data source, tracking specific bindings to a control, and man-aging the actual bindings A summary of these roles, along with the Windows Formsclass and property related to these roles, is outlined in the following table:
correspond-We will discuss these classes and properties in more detail as we build our example As
a brief explanation, the BindingContext class manages a collection of ManagerBase objects While any control can create an instance of this class in itsBindingContext property, the Form class creates one automatically to serve as the
Binding-4 Assign the tab order for the controls
within the Photo tab page as is shown
in the graphic.
Roles required for simple data binding
Tacking bound data BindingContext BindingContext property
Managing a bound data
source
BindingManagerBase Index into BindingContext collection:
BindingContext[ source ] BindingContext[ source, member ]
Managing bindings ControlBindingsCollection DataBindings property
Tracking a binding Binding Index into DataBindings collection:
DataBindings[ property ]