- •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
Set the version number of the MyPhotoAlbum library to 17.3.
SUPPORT THE IEDITABLEOBJECT INTERFACE IN THE PHOTOGRAPH CLASS
|
Action |
Result |
|
|
|
1 |
In the Photograph.cs code |
using System.ComponentModel; |
|
window, indicate that we will |
|
|
use members of the |
|
|
System.ComponentModel |
|
|
namespace. |
|
|
|
|
2 |
Add IEditableObject to the |
public class Photograph : IDisposable, |
|
list of supported interfaces for |
IEditableObject |
|
this class. |
{ |
|
. . . |
|
|
|
|
|
|
|
3 |
Add internal fields to track when |
private bool _modified; |
|
the object is in an editable state |
private bool _editing; |
|
or has been modified. |
|
|
|
|
4 |
Initialize these fields in the |
public Photograph(string fileName) |
|
constructor. |
{ |
|
|
. . . |
|
|
_modified = false; |
|
|
_editing = false; |
|
|
} |
|
|
|
5 |
Reset these values when the |
public void Write(StreamWriter sw) |
|
photograph is saved into a |
{ |
|
StreamWriter object. |
. . . |
|
_modified = false; |
|
|
|
|
|
|
_editing = false; |
|
|
} |
|
|
|
6 |
Add internal fields to record the |
private string _editCaption; |
|
existing values of the four |
private string _editPhotographer; |
|
modifiable properties. |
private DateTime _editDateTaken; |
|
private string _editNotes; |
|
|
|
|
|
|
|
7 |
Implement the BeginEdit |
public void BeginEdit() |
|
method. |
{ |
|
How-to |
if (!_editing) |
|
{ |
|
|
If editing is not already enabled, |
_editCaption = Caption; |
|
record the current values and |
_editDateTaken = DateTaken; |
|
_editPhotographer = Photographer; |
|
|
enable editing. |
|
|
_editNotes = Notes; |
|
|
Note: Ideally, we would permit |
_editing = true; |
|
} |
|
|
nesting of these calls. In this |
|
|
} |
|
|
example we will avoid this addi- |
|
|
tional complexity. |
|
|
|
|
8 |
Implement the CancelEdit |
public void CancelEdit() |
|
method. |
{ |
|
How-to |
if (_editing) |
|
{ |
|
|
If editing is enabled, restore the |
Caption = _editCaption; |
|
recorded values and disable |
Photographer = _editPhotographer; |
|
DateTaken = _editDateTaken; |
|
|
editing. |
|
|
Notes = _editNotes; |
|
|
|
_editing = false; |
|
|
} |
|
|
} |
|
|
|
EDITABLE OBJECTS |
583 |
SUPPORT THE IEDITABLEOBJECT INTERFACE IN THE PHOTOGRAPH CLASS (continued)
|
Action |
Result |
|
|
|
9 |
Implement the EndEdit |
public void EndEdit() |
|
method. |
{ |
|
How-to |
if (_editing) |
|
{ |
|
|
If editing is enabled, record |
_modified |= ((Caption != _editCaption) |
|
whether the data has been |
|| (Photographer != _editPhotographer) |
|
|| (DateTaken != _editDateTaken) |
|
|
modified and disable editing. |
|
|
|| (Notes != _editNotes)); |
|
|
|
_editing = false; |
|
|
} |
|
|
} |
|
|
|
10 |
Also add a HasEdits property to |
public bool HasEdits |
|
report whether the object has |
{ |
|
been modified. |
get { return _modified; } |
|
} |
|
|
|
|
|
|
|
The 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 a
HasEdits method in the PhotoAlbum class.
ADD A HASEDITS PROPERTY TO THE PHOTOALBUM CLASS
|
Action |
Result |
|
|
|
11 |
In the PhotoAlbum.cs code |
public bool HasEdits |
|
window, Implement a HasEdits |
{ |
|
method in this class. |
get |
|
{ |
|
|
|
|
|
How-to |
foreach (Photograph p in this) |
|
Use the Photograph.HasEdits |
{ |
|
if (p.HasEdits) |
|
|
method to determine the |
|
|
return true; |
|
|
appropriate result. |
} |
|
|
// No edits found |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
The MyPhotoAlbum library is ready to go. Make sure the library compiles with no errors. The next step is to make use of these changes in our MyAlbumData application. This is taken up in the next section.
17.3.3USING EDITABLE OBJECTS
Typically, you do not actually use the editable object methods directly. These are used
internally by Windows Forms as required for the task at hand. In this case, our DataGrid control automatically recognizes that our PhotoAlbum object supports this interface, 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
584 |
CHAPTER 17 DATA BINDING |
user finishes an edit by pressing the Enter key or selecting an alternate row, the EndEdit method is invoked. The EndEdit method makes the changes to the object permanent within the object itself.
In most applications, there is an operation or class that coordinates the in-mem- 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 our album 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 the album file.
This is true in the System.Data namespace as well. While we have avoided discussing this namespace in any real depth, it is useful to understand how the classes in this namespace relate to our discussion. The abstract DataAdaptor class is the coordinator between the in-memory version of the database, typically a DataSet instance, and the permanent version is an external database. The DataAdaptor class provides a Fill method to populate a DataSet with the external values, and an Update method to save modifications in the DataSet into the external database.
For our purposes, we have provided the HasEdits method in our in-memory objects in order to identify whether any changes must be saved into the album file. We can do this in the SelectedIndexChanged event handler before the new album is bound to our data grid.
The following table details the steps required to save our PhotoAlbum instance into its associated album file:
Set the version number of the MyAlbumData application to 17.3.
|
SAVE A MODIFIED ALBUM |
|
|
|
|
|
Action |
Result |
|
|
|
1 |
In the MainForm.cs window, |
private void SaveChanges() |
|
create a new SaveChanges |
{ |
|
method to store any changes to |
if (_album.HasEdits) |
|
{ |
|
|
the displayed album into the |
|
|
DialogResult result = MessageBox.Show( |
|
|
album file. |
"Do you wish to save your changes " |
|
How-to |
+ "to the album \'" + _album.Title |
|
+ "\'?", |
|
|
a. If the album has been modi- |
"Save Changes?", |
|
fied, prompt the user to see |
MessageBoxButtons.YesNo, |
|
MessageBoxIcon.Question); |
|
|
if he or she wishes to save |
|
|
if (result == DialogResult.Yes) |
|
|
the changes. |
_album.Save(); |
|
b. If yes, then save the album in |
} |
|
} |
|
|
the existing album file. |
|
|
|
|
|
|
|
EDITABLE OBJECTS |
585 |
SAVE A MODIFIED ALBUM (continued)
|
Action |
Result |
|
|
|
2 |
Use this new method when the |
private void cmbxAlbum_SelectedIndexChanged |
|
user selects a new album in the |
(object sender, System.EventArgs e) |
|
ComboBox control. |
{ |
|
string albumFile |
|
|
|
|
|
|
= cmbxAlbum.SelectedItem.ToString(); |
|
|
if (_album != null) |
|
|
{ |
|
|
SaveChanges(); |
|
|
_album.Dispose(); |
|
|
} |
|
|
_album.Clear(); |
|
|
try |
|
|
. . . |
|
|
} |
|
|
|
3 |
Also make sure any changes are |
protected override void OnClosing |
|
saved when the application |
(CancelEventArgs e) |
|
exits. |
{ |
|
SaveChanges(); |
|
|
|
|
|
|
base.OnClosing(e); |
|
|
} |
|
|
|
With these modifications, our edits are now saved. Compile and run to verify that the application 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 Internet. One such example is the CdrCatalog program built by Andrew Skowronski that is available at http://cdrcatalog.sourceforge.net/. This program manages offline media, such as a collection of recordable compact discs, and makes use of a number of Windows Forms classes in addition to the DataGrid control. The data source used by this application is a DataSet object loaded from a local XML file.
17.4SIMPLE 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 implemented the AlbumCollection class I keep mentioning to contain an array of PhotoAlbum objects, then the line to assign the DataSource for our combo box could instead be written as:
cmbxAlbum.DataSource = myAlbumCollection;
cmbxAlbum.DataMember = "FileName";
This would display the collection of PhotoAlbum objects in the myAlbumCollection variable in the cmbxAlbum combo box, using the FileName property as the
586 |
CHAPTER 17 DATA BINDING |
name to display. The result would be the same as that which currently appears in our application. This type of binding to a list control can also be done with database objects, such as binding the entries in a ListBox control to the set of customer names 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 therefore inherited by and available in all controls in Windows Forms. The concepts and techniques for so-called simple data binding are fairly identical to those we have already discussed for the DataGrid control.
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 controls dynamically, including the image associated with a photograph; and save changes to bound controls.
17.4.1ALTERING THE MYALBUMDATA APPLICATION
Before we get into the details of exactly how simple data binding is performed, let’s whip through some changes to our MyAlbumData application in preparation for this discussion. The change we will make is to place our existing DataGrid control within a TabPage object, and add a new tab to display the Photograph information 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 corresponding values in a Photograph object.
SIMPLE DATA BINDING |
587 |