- •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
12.3IMAGE BUTTONS
So far the buttons in this book have contained text strings only. In fact, both button and label controls support the display of an image instead of or in addition to text. In this section we will look at images on buttons. Supporting images on labels is quite similar.
For button controls, imaging support is defined by the ButtonBase class, so this discussion applies equally well to the Button, RadioButton, and CheckBox objects. A summary of this class was provided in .NET Table 9.4 on page 292. For our purposes, we will focus on the Image property for assigning an image to a button, and the ImageAlign property to specify how the image is aligned within the button. An index into a list of images can also be specified using the ImageIndex and ImageList properties. We will cover image lists when we discuss toolbars in chapter 13.
As our example, we will add the ability to move to the next or previous photograph in our PhotoEditDlg form. Currently, when a user wants to edit the properties of two different images, he or she must display and edit the dialogs separately from the main form, so this change provides a nice shortcut for this type task. Figure 12.2 shows the dialog with our new changes. As you can see, two small image buttons have been added to the top of the form.
Figure 12.2
Buttons can display text only, an image only, or both an image and text. The Prev and Next buttons in this figure display both types of data.
This change will require more modifications than you might imagine. Our current dialog code does not make any allowances for more than a single photo. To take a somewhat incremental approach to this change, we will first add these buttons with only text displayed, and later replace the text with an image.
12.3.1IMPLEMENTING NEXT AND PREV BUTTONS
We have been placing buttons on forms since chapter 1, so this section will run through the steps required to add and manage our new buttons. A number of steps are required here to convert our dialog from using the positional methods in the PhotoAlbum class to an index-based scheme that can support our new buttons.
IMAGE BUTTONS |
393 |
To begin, let’s add the new buttons to our window. In order to fit additional controls in our PhotoEditDlg form, we need to do some redecorating. As you’ll recall, this form inherits its size from the BaseEditDlg form, so we are not able to resize the form itself. Our first task, then, is to squeeze the existing controls together a bit and insert our new buttons at the top of the form.
Set the version number of the MyPhotoAlbum library to 12.3.
ADD THE NEXT AND PREV BUTTONS
|
|
|
Action |
|
|
|
Result |
||
|
|
|
|
|
|
|
|
||
1 |
In the PhotoEditDlg.cs [Design] window, adjust |
Note: The instructions here simply |
|||||||
|
the existing controls so there is room for the |
reflect how I made this change, using |
|||||||
|
new buttons at the top of the form. |
the default grid size of 8. Your form |
|||||||
|
How-to |
|
|
|
|
may vary a little, so do whatever |
|||
|
|
|
|
|
appears to work best. |
||||
|
a. Resize the Notes text box to be one grid |
||||||||
|
|
||||||||
|
size smaller in height. |
|
|
|
|
|
|||
|
b. Move the Notes label down as well. |
|
|||||||
|
c. Resize the base of the Panel to be closer |
|
|||||||
|
to the bottom control. |
|
|
|
|
|
|||
|
d. Move the Panel down two grid sizes. |
|
|||||||
|
|
|
|
|
|
|
|
||
2 |
Add a Next button to the top of the form. |
|
|||||||
|
|
|
Settings |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Property |
|
|
Value |
|
||
|
|
|
(Name) |
|
btnNext |
|
|||
|
|
|
Size |
|
|
60, 20 |
|
|
|
|
|
|
Text |
|
|
N&ext |
|
||
|
|
|
|
|
|
|
|
|
|
3 |
Add a Prev button to the left of the Next |
|
|||||||
|
button. |
|
|
|
|
|
|||
|
|
|
Settings |
|
|
|
|
||
|
|
|
|
|
|
|
|
||
|
|
|
Property |
|
|
Value |
|
||
|
|
|
(Name) |
|
|
btnPrev |
|
||
|
|
|
Size |
|
|
60,20 |
|
|
|
|
|
|
Text |
|
|
Pre&v |
|
||
|
|
|
|
|
|
|
|
|
|
4 |
Adjust the tab order for the form’s controls to |
Note: The order does not affect our |
|||||||
|
have a reasonable sequence. |
discussion, so use whatever sequence |
|||||||
|
|
|
|
|
|
|
|
|
makes the most sense to you. |
|
|
|
|
|
|
|
|
|
|
Each of these buttons will require a Click handler, and this is where it gets a bit tricky. You might be tempted to implement these handlers as follows:
private void btnNext_Click(object sender, System.EventArgs e)
{
// Increment the current position (not our approach) _album.CurrentNext();
}
394 |
CHAPTER 12 A .NET ASSORTMENT |
private void btnPrev_Click(object sender, System.EventArgs e)
{
// Decrement the current position (not our approach) _album.CurrentPrev();
}
Unfortunately, life is not so simple. There are two major problems with this approach. The first is that the parent form, in this case the main form of our MyPhotos application, may rely on the existing value of the current album position. This code might adversely affect some activity in the application, or even cause a fatal error.
The second problem is that any changes made to the existing photograph are discarded whenever the user views a new photograph. Clicking the Next button should not throw out the changes already made.
As a result, we need to take a different approach. To resolve the first problem, that of adversely affecting the parent form, we will use a direct index into the album rather than modifying the current album position. We will address the second problem, that of not discarding user changes, in a moment.
ACCESS THE ALBUM USING AN INDEX VALUE
|
Action |
Result |
|
|
|
5 |
Create an _index member in the |
private int _index; |
|
PhotoEditDlg class. |
|
|
|
|
6 |
Initialize this field in the constructor. |
public PhotoEditDlg(PhotoAlbum album) |
|
Note: This field must be initialized before |
{ |
|
. . . |
|
|
the call to the ResetSettings method. |
// Initialize the dialog settings |
|
|
_album = album; |
|
|
_index = album.CurrentPosition; |
|
|
ResetSettings(); |
|
|
} |
|
|
|
7 |
Update the remainder of the file to access |
For example, in the ResetSettings method: |
|
the current photograph using this new |
protected override void ResetSettings() |
|
field rather than the CurrentPhoto |
|
|
{ |
|
|
method. |
. . . |
|
Note: This requires updating the Reset- |
Photograph photo = _album[_index]; |
|
Settings, SaveSettings, and |
. . . |
|
cmbxPhotographer_Validated |
|
|
|
|
|
methods. |
|
|
|
|
8 |
Also in the ResetSettings method, |
btnPrev.Enabled = !(_index == 0); |
|
enable or disable the Next and Prev |
btnNext.Enabled |
|
buttons based on the current index. |
= !(_index == _album.Count - 1); |
|
} |
|
|
|
|
|
|
|
These changes will allow us to modify the index value in our event handlers, and the current album position will not be affected. The second problem, that of saving any changes made, requires that we save the existing photograph before moving on to the next or previous one. Of course, we do not want to do this unless the user has actually
IMAGE BUTTONS |
395 |
made some changes. As a result, we will track the original values for the photograph, and later compare these against the new values when moving to a new photograph.
The following steps continue our changes and build the infrastructure needed to do this. Following this table, we will make use of this infrastructure to save any changes made by the user.
TRACK WHEN PHOTOGRAPH SETTINGS ARE MODIFIED
|
Action |
Result |
|
|
|
9 |
Create variables to hold the original |
private string _origCaption; |
|
caption, date taken, and |
private DateTime _origDateTaken; |
|
photographer values. |
private string _origPhotographer; |
|
|
|
|
Note: The file name cannot be |
|
|
changed, and the Notes value will |
|
|
require a different approach. |
|
|
|
|
10 |
Create a SetOriginals method to |
protected void SetOriginals() |
|
initialize these variables. |
{ |
|
|
Photograph photo = _album[_index]; |
|
|
if (photo != null) |
|
|
{ |
|
|
_origCaption = photo.Caption; |
|
|
_origDateTaken = photo.DateTaken; |
|
|
_origPhotographer = photo.Photographer; |
|
|
} |
|
|
} |
|
|
|
11 |
Ensure these values are initialized |
public PhotoEditDlg(PhotoAlbum album) |
|
in the constructor. |
{ |
|
|
. . . |
|
|
// Initialize the dialog settings |
|
|
_album = album; |
|
|
_index = album.CurrentPosition; |
|
|
ResetSettings(); |
|
|
SetOriginals(); |
|
|
} |
|
|
|
You may have noticed that the Notes text box value is suspiciously missing here. While comparing a string object to the Text property works fine for a single-line TextBox control, it is not the preferred method for multiline text boxes. Instead, the Lines property should be compared line by line with the original settings. This is a little more work than we want to do, so we will simply mark this text box modified whenever the user modifies the text.
Recording whether the Notes text has changed will allow us to conditionally save a photograph’s new settings, which we will do as part of the following steps:
396 |
CHAPTER 12 A .NET ASSORTMENT |