- •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
.NET Table 17.8 CurrencyManager class
The CurrencyManager class represents a binding manager that is associated with a data source supporting the IList interface. This class is part of the System.Windows.Forms namespace, and inherits from the BindingManagerBase class. See .NET Table 17.7 for the members inherited from the base class.
|
Refresh |
Forces a repopulation of all bound controls for a data |
Public Methods |
|
source that does not support notification when the |
|
|
underlying data changes. |
|
|
|
|
ItemChanged |
Occurs when an item in the associated data source is |
Public Events |
|
altered. This event will only occur if the associated data |
|
source supports two-way notification, such as the support |
|
|
|
|
|
|
provided by the IBindingList interface. |
|
|
|
From the table, it appears that the Refresh method is the solution we need. This method updates the bound controls with the underlying data. For classes that support the IBindingList interface, most notably the database-related objects, this method is not generally needed since the ItemChanged event will occur whenever the database object itself is modified. Do not confuse the ItemChanged event with the PositionChanged event, which occurs when a new item, or row, in the associated list is selected; or with the CurrentChanged event, which occurs when the control’s bound property is altered.
For our purposes, the default behavior that occurs when the Position or Current properties change will suffice. Since we do not support the IBindingList interface, we need to call the Refresh method directly when our PhotoAlbum is altered internally. In our current interface, this occurs each time a new album file is selected in the Album combo box control.
The following table continues our prior steps to alter our Changed event handler to invoke the Refresh method.
UPDATE THE SELECTEDINDEXCHANGED EVENT HANDLER
|
Action |
Result |
|
|
|
7 |
Move the |
protected override void OnLoad(EventArgs e) |
|
DataGrid.SetDataBinding call |
{ |
|
from the |
. . . |
|
gridPhotoAlbum.SetDataBinding(_album, |
|
|
SelectedIndexChanged event |
|
|
null); |
|
|
handler to the end of the OnLoad |
} |
|
method. |
Note: At the start of the chapter, we had to clear |
|
|
|
|
|
the binding and then rebind to the album each |
|
|
time the album changed. This is no longer |
|
|
required. |
|
|
|
SIMPLE DATA BINDING |
597 |
UPDATE THE SELECTEDINDEXCHANGED EVENT HANDLER (continued)
|
Action |
Result |
|
|
|
8 |
At the end of the Selected- |
private void cmbxAlbum_SelectedIndexChanged |
|
IndexChanged event handler, add |
(object sender, System.EventArgs e) |
|
a single empty Photograph |
{ |
|
. . . |
|
|
object to the cleared PhotoAlbum |
|
|
// Required to prevent binding exception |
|
|
instance when the album is |
if (_album.Count == 0) |
|
empty. |
_album.Add(new Photograph("")); |
|
|
Note: Our bound controls required that at least |
|
|
one object be present in the collection. Otherwise, |
|
|
the subsequent lines will throw an exception. A |
|
|
more elegant solution might be to unbind the con- |
|
|
trols and disable the tab control in this case. |
|
|
|
9 |
Add code to the end of the |
// Refresh the Photo tab controls |
|
method to retrieve the |
BindingManagerBase bm |
|
CurrencyManager object used to |
= this.BindingContext[_album]; |
|
CurrencyManager cm = bm as CurrencyManager; |
|
|
manage the _album data source. |
|
|
|
|
|
How-to |
|
|
Retrieve the BindingManager- |
|
|
Base for this data source from the |
|
|
Form and convert it to a |
|
|
CurrencyManager object. |
|
|
|
|
10 |
If the CurrencyManager was |
if (cm != null) |
|
located, refresh the bound |
cm.Refresh(); |
|
controls. |
|
|
|
|
11 |
Also call EnablePhotoButtons to |
EnablePhotoButtons(bm); |
|
enable or display the Next and |
} |
|
Prev buttons as required. |
|
|
|
|
The controls in the Photo tab now update properly in all cases. You can compile and run this to experience the magic. Note how the index into the album, based on the Position property, is preserved when you change albums. If the second item in an album is shown and a new album is selected, the second item in the new album is selected. What happens when the number of photos in the current album is more than the number in a newly selected album?
TRY IT! We have not discussed the data binding for list controls very much here. As a way to see this in action, replace the txtPhotographer control with a combo box called cmbxPhotographer. Use data binding to automatically fill this list with the Photographer entries from the current album. This should be done in the OnLoad method and should look like the following:
cmbxPhotographer.DataSource = _album;
cmbxPhotographer.DataMember = "Photographer";
598 |
CHAPTER 17 DATA BINDING |
As you will see if you compile this change, all photographers assigned to the album are listed, even if they occur multiple times. Even so, this is a good example of how to populate a list quickly with values from a data source.
To provide a more robust implementation, implement a GetPhotographers method in the PhotoAlbum class that returns an array of unique photographer strings in the album. This array can then be set as the DataSource for the combo box. Be careful here, as you will need to update this setting in the SaveChanges method to accommodate any changes made to the list.
As already mentioned, when binding to a database, the IBindingList interface will ensure that the bound properties and the database object stay in sync. When either object is modified, the other is automatically updated.
One issue that remains in our current application is the display of our image in the PictureBox control. We will address this topic next.
17.4.4DISPLAYING THE IMAGE
While our application is working quite well, there is the small matter of our PictureBox control. As a simple solution, we could modify the SizeMode property of our PictureBox control to use the StretchImage value. This would stretch our image to fit the window, but would not preserve the aspect ratio of our images.
A better solution, as we know very well, is to scale the image to preserve the aspect ratio within the window. We can do this if we paint the image ourselves rather than provide a value for the Image property. The Paint event for the PictureBox control can be used to perform this painting, although we still need to know which image to paint. For this, we will use the Tag property to keep track of the current image.
Of course, as we mentioned earlier in the book, it would be nice to build a “PhotoBox” control that did this automatically. Such a control would extend the Windows Forms PictureBox control to scale an image as we do in the subsequent table. Coding this by hand is not much extra work, so we did this explicitly here. A short discussion on how to build such a “PhotoBox” control is given at the end of section 18.2.
The following steps are required to scale the image within the pboxPhoto control.
MODIFY THE PICTUREBOX CONTROL TO DISPLAY A SCALED IMAGE
|
Action |
Result |
|
|
|
1 |
In the OnLoad method of the |
protected override void OnLoad(EventArgs e) |
|
MainForm.cs code window, |
{ |
|
replace the binding of the Image |
. . . |
|
txtNotes.DataBindings.Add( |
|
|
property in the pboxPhoto |
|
|
"Text", _album, "Notes"); |
|
|
control to use the Tag property |
pboxPhoto.DataBindings.Add( |
|
instead. |
"Tag", _album, "Image"); |
|
|
gridPhotoAlbum.SetDataBinding(_album, null); |
|
|
} |
|
|
|
SIMPLE DATA BINDING |
599 |
MODIFY THE PICTUREBOX CONTROL TO DISPLAY A SCALED IMAGE (continued)
|
Action |
Result |
|
|
|
2 |
Invalidate the PictureBox |
private void EnablePhotoButtons |
|
control in the |
(BindingManagerBase bm) |
|
EnablePhotoButtons method. |
{ |
|
btnNext.Enabled |
|
|
|
|
|
|
= (bm.Position < _album.Count - 1); |
|
|
btnPrev.Enabled = (bm.Position > 0); |
|
|
// Force image to repaint |
|
|
pboxPhoto.Invalidate(); |
|
|
} |
|
|
|
3 |
Add a handler for the Paint |
private void pboxPhoto_Paint |
|
event for the PictureBox |
(object sender, |
|
control. |
System.Windows.Forms.PaintEventArgs e) |
|
{ |
|
|
|
|
|
|
|
4 |
Convert the Tag property to a |
Bitmap image = pboxPhoto.Tag as Bitmap; |
|
Bitmap object. |
|
|
|
|
5 |
If no image is present, clear the |
if (image == null) |
|
graphics area. |
{ |
|
|
// No image, just clear the graphics |
|
|
e.Graphics.Clear(SystemColors.Control); |
|
|
return; |
|
|
} |
|
|
|
6 |
Retrieve the current |
// Load the current photo |
|
Photograph object from the |
BindingManagerBase bm |
|
binding manager. |
= BindingContext[_album]; |
|
Photograph photo = bm.Current as Photograph; |
|
|
|
|
|
How-to |
|
|
Use the Current property of |
|
|
the binding manager object. |
|
|
|
|
7 |
If for some reason the current |
Rectangle r = pboxPhoto.ClientRectangle; |
|
Photograph is not found, |
if (photo == null) |
|
simply draw the image in the |
{ |
|
// Something is wrong, just draw the image |
|
|
client rectangle. |
|
|
e.Graphics.DrawImage(image, r); |
|
|
|
} |
|
|
|
8 |
If the photo is found, draw the |
else |
|
image in the scaled rectangle. |
{ |
|
|
// Paint the image with proper aspect ratio |
|
|
e.Graphics.DrawImage( |
|
|
image, photo.ScaleToFit(r)); |
|
|
} |
|
|
} |
|
|
|
The photograph is now scaled and displayed within our PictureBox control. Note how we used the entire client rectangle rather than the rectangle provided in the Graphics object to ensure that the entire control area is redrawn.
It is worth noting here that binding to the Tag property is not strictly required. Since we access the current Photograph object here, we could simply load the associated image correctly. There may be a slight performance advantage here in having the image already available in the Tag property, so we selected the approach shown in the table.
600 |
CHAPTER 17 DATA BINDING |