- •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
10.4COMBO BOX EDITS
The ComboBox created in the previous section used a fixed set of list entries taken from a directory on the disk. This permitted us to use the DataSource property for the list of items, and the DropDownList style to prevent the user from editing the text entry.
In this section we will create another ComboBox that permits manual updates to its contents by the user. Such a control is very useful when there are likely to be only a few possible entries, and you want the user to create additional entries as necessary. It so happens that we have just this situation for the Photographer property of our
Photograph class.
Within a given album, there are likely to be only a handful of photographers for the images in that album. A combo box control is a good choice to permit the user to select the appropriate entry from the drop-down list. When a new photographer is required, the user can enter the new name in the text box.
Figure 10.6 shows how this combo box will look. You may notice that this list only displays four photographers, whereas our previous album combo box displayed eight album files at a time. A ComboBox control displays eight items by default. We will shorten the size here so that the list does not take up too much of the dialog window.
Figure 10.6
Note how the dropdown for the ComboBox extends outside of the Panel control. This is permitted even though the control is contained by the panel.
We will add this control to the MyAlbumEditor application in two parts. First we will create and initialize the contents of the control, and then we will support the addition of new photographers by hand.
COMBO BOX EDITS |
339 |
10.4.1REPLACING THE PHOTOGRAPHER CONTROL
The creation of our combo box within the PhotoEditDlg form is much like the one we created for the MyAlbumEditor application, with the exception of a few settings. The steps required to create this control are shown in the following table:
Set the version number of the MyPhotoAlbum library to 10.4.
ADD THE PHOTOGRAPHER COMBO BOX
|
|
Action |
Result |
||
|
|
|
|
|
|
1 |
In the PhotoEditDlg.cs [Design] |
The control is removed from the form, and the code |
|||
|
window, delete the TextBox control |
generated by Visual Studio is removed as well. The |
|||
|
associated with the Photographer |
subsequent steps modify the manually entered |
|||
|
label. |
|
|
code associated with this control. |
|
|
|
|
|
|
|
2 |
Place a ComboBox control on the form |
The MaxDropDown property here specifies that the |
|||
|
where the text box used to be. |
list portion of the combo box displays at most four |
|||
|
|
Settings |
items at a time, with any remaining items |
||
|
|
accessible via the scroll bar. |
|||
|
|
|
|
|
|
|
|
Property |
Value |
|
|
|
|
(Name) |
cmbxPhotographer |
|
|
|
|
MaxDropDown |
4 |
|
|
|
|
Sorted |
True |
|
|
|
|
Text |
photographer |
|
|
|
|
|
|
|
|
3 |
Modify the ResetSettings method to |
protected override void ResetSettings() |
|||
|
initialize the items in the new combo |
{ |
|||
|
box if necessary |
|
|
// Initialize the ComboBox settings |
|
|
|
|
if (cmbxPhotographer.Items.Count == 0) |
||
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
4 |
First add the “unknown” |
// Create the list of photographers |
|||
|
photographer to ensure that the list is |
cmbxPhotographer.BeginUpdate(); |
|||
|
never empty. |
|
|
cmbxPhotographer.Items.Clear(); |
|
|
|
|
cmbxPhotographer.Items. |
||
|
|
|
|
|
|
|
|
|
|
|
Add("unknown"); |
|
|
|
|
|
|
5 |
Then add to the ComboBox control any |
foreach (Photograph ph in _album) |
|||
|
other photographers found in the |
{ |
|||
|
album. |
|
|
if (ph.Photographer != null |
|
|
|
|
&& !cmbxPhotographer.Items. |
||
|
|
|
|
|
|
|
How-to |
|
|
Contains(ph.Photographer)) |
|
|
Use the Items.Contains method to |
{ |
|||
|
cmbxPhotographer.Items. |
||||
|
check that a photographer is not |
||||
|
Add(ph.Photographer); |
||||
|
already in the list. |
|
|
} |
|
|
Note: This code is not terribly effi- |
} |
|||
|
cmbxPhotographer.EndUpdate(); |
||||
|
cient, since it rescans the entire list |
} |
|||
|
each time the method is called. A |
|
|||
|
better solution might be to modify |
|
|||
|
the PhotoAlbum class to maintain |
|
|||
|
the list of photographers assigned to |
|
|||
|
Photograph objects in the album. |
|
|||
|
|
|
|
|
|
340 |
CHAPTER 10 LIST CONTROLS |
ADD THE PHOTOGRAPHER COMBO BOX (continued)
|
Action |
Result |
|
|
|
6 |
Select the photographer of the current |
Photograph p = _album.CurrentPhoto; |
|
photo in the combo box. |
if (p != null) |
|
|
|
|
|
{ |
|
|
txtPhotoFile.Text = p.FileName; |
|
|
txtCaption.Text = p.Caption; |
|
|
txtDate.Text |
|
|
= p.DateTaken.ToString(); |
|
|
cmbxPhotographer.SelectedItem |
|
|
= p.Photographer; |
|
|
txtNotes.Text = p.Notes; |
|
|
} |
|
|
} |
|
|
|
7 |
Update the SaveSettings method to |
protected override bool SaveSettings() |
|
save the photographer entered into |
{ |
|
the combo box. |
Photograph p = _album.CurrentPhoto; |
|
|
|
|
Note: We will stop ignoring the txt- |
if (p != null) |
|
Date setting in the next chapter. |
{ |
|
p.Caption = txtCaption.Text; |
|
|
|
|
|
|
// Ignore txtDate setting for now |
|
|
p.Photographer |
|
|
= cmbxPhotographer.Text; |
|
|
p.Notes = txtNotes.Text; |
|
|
} |
|
|
return true; |
|
|
} |
|
|
|
Note how this code uses both the SelectedItem and Text properties for the ComboBox control. The SelectedItem property retrieves the object corresponding to the item selected in the list box, while the Text property retrieves the string entered into the text box. Typically these two values correspond to each other, but this is not always true, especially when the user manipulates the text value directly, as we shall see next.
10.4.2UPDATING THE COMBO BOX DYNAMICALLY
With our control on the form, we now need to handle manual entries in the text box. This is normally handled via events associated with the ComboBox control. The
Validated event, discussed in chapter 9, can be used to verify that a user-provided entry is part of the list and also add it to the list if necessary. The TextChanged event can be used to process the text while the user is typing.
We will handle both of these events in our code. First, let’s add a Validated event handler, and then add code to auto-complete the entry as the user types.
COMBO BOX EDITS |
341 |
VALIDATE THE PHOTOGRAPHER ENTRY
|
Action |
Result |
|
|
|
1 |
Add a Validated event handler for the |
private void cmbxPhotographer_Validated |
|
cmbxPhotographer control. |
(object sender, System.EventArgs e) |
|
|
{ |
|
|
|
2 |
To implement this handler, get the |
string pg = cmbxPhotographer.Text; |
|
text currently entered in the control. |
|
|
|
|
3 |
If the cmbxPhotographer control |
if (!cmbxPhotographer.Items.Contains(pg)) |
|
does not contain this text, then add |
{ |
|
the new string to the combo box. |
_album.CurrentPhoto.Photographer = pg; |
|
cmbxPhotographer.Items.Add(pg); |
|
|
|
|
|
|
} |
|
|
|
4 |
Set the selected item to the new |
cmbxPhotographer.SelectedItem = pg; |
|
text. |
} |
|
|
|
Our ComboBox is now updated whenever the user enters a new photographer, and the new entry will be available to other photographs in the same album.
Another change that might be nice is if the dialog automatically completed a partially entered photographer that is already on the list. For example, if the photographer “Erik Brown” is already present, and the user types in “Er,” it would be nice to complete the entry on the user’s behalf.
Of course, if the user is typing “Erin Smith,” then we would not want to prevent the user from doing so. This can be done by causing the control to select the autofilled portion of the name as the user types. You will be able to experiment with this behavior yourself after following the steps in the subsequent table.
AUTO-COMPLETE THE TEXT ENTRY AS THE USER TYPES
|
Action |
Result |
|
|
|
5 |
Add a TextChanged event |
private void cmbxPhotographer_TextChanged |
|
handler for the |
(object sender, System.EventArgs e) |
|
cmbxPhotographer |
{ |
|
|
|
|
control. |
|
|
|
|
6 |
Search for the current text |
string text = cmbxPhotographer.Text; |
|
in the list portion of the |
int index |
|
combo box. |
= cmbxPhotographer.FindString(text); |
|
|
|
|
|
|
7 |
If found, then adjust the |
if (index >= 0) |
|
text in the control to |
{ |
|
include the remaining |
// Found a match |
|
string newText = cmbxPhotographer. |
|
|
portion of the matching |
|
|
Items[index].ToString(); |
|
|
entry. |
cmbxPhotographer.Text = newText; |
|
|
cmbxPhotographer.SelectionStart |
|
|
= text.Length; |
|
|
cmbxPhotographer.SelectionLength |
|
|
= newText.Length - text.Length; |
|
|
} |
|
|
} |
|
|
|
342 |
CHAPTER 10 LIST CONTROLS |