- •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
INITIALIZE THE MONTH CALENDAR CONTROL
|
Action |
Result |
|
|
|
1 |
In the MainForm.cs source file, |
private void UpdateCalendar() |
|
add an UpdateCalendar method |
{ |
|
to update the MonthCalendar |
// Initialize MonthCalendar control |
|
|
|
|
control in the Dates tab. |
|
|
|
|
2 |
In this method, calculate the range |
DateTime minDate = DateTime.MaxValue; |
|
of dates used by photographs in |
DateTime maxDate = DateTime.MinValue; |
|
this album. |
DateTime[] dates |
|
|
|
|
|
= new DateTime[_album.Count]; |
|
|
|
3 |
For each Photograph in the |
for (int i = 0; i < _album.Count; i++) |
|
album, record its date and adjust |
{ |
|
the minimum and maximum date |
DateTime newDate |
|
= _album[i].DateTaken; |
|
|
as required. |
|
|
dates[i] = newDate; |
|
|
Note: We could use a foreach |
if (newDate < minDate) |
|
loop here, of course. A for loop |
|
|
minDate = newDate; |
|
|
works a little better since an index |
if (newDate > maxDate) |
|
for the dates array is required. |
|
|
|
maxDate = newDate; |
|
|
} |
|
|
|
4 |
Assign the MonthCalendar |
if (_album.Count > 0) |
|
properties based on the calculated |
{ |
|
date values. |
monthCalDates.BoldedDates = dates; |
|
monthCalDates.MinDate = minDate; |
|
|
|
|
|
Note: The SelectionStart prop- |
monthCalDates.MaxDate = maxDate; |
|
erty ensures that the initial date |
monthCalDates.SelectionStart = minDate; |
|
} |
|
|
for the album is displayed by the |
|
|
} |
|
|
calendar. |
|
|
|
|
5 |
Add a new UpdatePhotographs |
private void UpdatePhotographs() |
|
method to update the appropriate |
{ |
|
tab page. |
if (tcPhotos.SelectedTab == tabPhotos) |
|
UpdateList(); |
|
|
|
|
|
How-to |
else if (tcPhotos.SelectedTab == tabDates) |
|
Use the SelectedTab property of |
UpdateCalendar(); |
|
} |
|
|
the tcPhotos control. |
|
|
|
|
|
|
|
6 |
Modify the OpenAlbum method to |
private void OpenAlbum(string fileName) |
|
update the appropriate tab page. |
{ |
|
|
CloseAlbum(); |
|
|
_album.Open(fileName); |
|
|
this.Text = _album.FileName; |
|
|
UpdatePhotographs(); |
|
|
} |
|
|
|
7 |
In the MainForm.cs [Design] |
private void tcPhotos_SelectedIndexChanged |
|
window, handle the |
(object sender, System.EventArgs e) |
|
SelectedIndexChanged event for |
{ |
|
UpdatePhotographs(); |
|
|
our tab control. |
|
|
} |
|
|
Note: This is the default event for |
|
|
tab controls, and occurs when- |
|
|
ever a new tab is selected by the |
|
|
user. |
|
|
|
|
CALENDARS |
375 |
INITIALIZE THE MONTH CALENDAR CONTROL (continued)
|
Action |
Result |
|
|
|
8 |
Update the |
private void cmbxAlbums_SelectedIndexChanged |
|
SelectedIndexChanged event |
(object sender, System.EventArgs e) |
|
handler for our combo box control |
{ |
|
. . . |
|
|
to enable or disable the controls as |
|
|
try |
|
|
required. |
{ |
|
Note: We take a slightly different |
CloseAlbum(); |
|
OpenAlbum(albumPath); |
|
|
approach here than we used ear- |
tcPhotos.Enabled = true; |
|
lier in the chapter. The final effect |
btnAlbumProp.Enabled = true; |
|
is the same. |
} |
|
catch (Exception) |
|
|
|
|
|
|
{ |
|
|
// Unable to open album |
|
|
this.Text = "Unable to . . . album"; |
|
|
tcPhotos.Enabled = false; |
|
|
lstPhotos.Items.Clear(); |
|
|
monthCalDates.RemoveAllBoldedDates(); |
|
|
btnAlbumProp.Enabled = false; |
|
|
} |
|
|
} |
|
|
|
Our calendar, as well as our list box, is updated whenever an album is opened and whenever the user displays an alternate tab page. Compile and run the application if you would like to see this in action. The next section processes the user’s mouse clicks in the control to provide access to the PhotoEditDlg form associated with a selected date.
11.4.3HANDLING MOUSE CLICKS IN A CALENDAR CONTROL
Our MonthCalendar control is on the form and displays the dates assigned to an album’s photographs in bold. The next step is to handle clicks by the user and link them up with associated photographs.
We will handle the MouseDown event for this purpose, and create a ContextMenu object on the fly to display any photos associated with the selection point. The MonthCalendar class also provides the DateChanged event that occurs whenever
a valid date is clicked. We could use this event instead, although the current mouse position would still be required to display the context menu. Since the MouseDown event provides the mouse location directly, this event seems a more logical choice.
We will discuss mouse events in more detail in the next chapter. Like the
Move event used in chapter 8, a MouseDown event handler receives a MouseEventArgs that includes the current position of the mouse. We will use this position both to determine which aspect of the calendar the user clicked on and to display the context menu at the appropriate location.
Before we see how to add this handler, one other item is needed. When we create MenuItem objects for the context menu, we will need a way to retrieve the associated Photograph object if the user later selects the menu. While the Control class provides a Tag property that associates an object instance with a control, the MenuItem
376 |
CHAPTER 11 MORE CONTROLS |
class has no such property. As a result, we have to deal with this unfortunate omission ourselves. In chapter 3, we created an array indexed by the menu location that linked a display mode to the menu. Now that we are more experienced, we will simply create a new class derived from MenuItem for a similar purpose.
CREATE A CUSTOM MENUITEM CLASS TO HOLD THE ALBUM INDEX
|
Action |
Result |
|
|
|
1 |
Within the MainForm class definition, |
private class PhotoMenuItem : MenuItem |
|
define a new PhotoMenuItem class |
{ |
|
based on the MenuItem class within |
|
|
the MainForm class. |
|
|
|
|
2 |
Add a public field in this class to hold |
public int tag; |
|
the integer album index associated |
} |
|
with the menu. |
|
|
|
|
As you will see, this very simple class will make our click handling much more efficient. Let’s take a look at this code.
HANDLE A MOUSE CLICK IN THE CALENDAR CONTROL
|
Action |
Result |
|
|
|
3 |
Add an event handler for the |
private void monthCalDates_MouseDown |
|
MouseDown event in the |
(object sender, |
|
MonthCalendar control. |
System.Windows.Forms.MouseEventArgs e) |
|
{ |
|
|
|
|
|
|
|
4 |
Determine if the user clicked on a |
MonthCalendar.HitTestInfo info |
|
date. |
= monthCalDates.HitTest(e.X, e.Y); |
|
How-to |
if (info.HitArea |
|
== MonthCalendar.HitArea.Date) |
|
|
Use the HitTest method. |
{ |
|
|
|
5 |
If so, create a new context menu |
ContextMenu ctxtPhotoCal |
|
to hold any photographs |
= new ContextMenu(); |
|
associated with this date. |
|
|
|
|
6 |
Iterate through the photos in the |
for (int i = 0; i < _album.Count; i++) |
|
album. |
{ |
|
|
|
7 |
Look for any photographs taken |
if (_album[i].DateTaken.Date |
|
on the same date as the date |
== info.Time.Date) |
|
clicked by the user. |
{ |
|
|
|
|
How to |
|
|
Use the Date property to obtain |
|
|
only the date portion of the |
|
|
DateTime objects. |
|
|
|
|
CALENDARS |
377 |
HANDLE A MOUSE CLICK IN THE CALENDAR CONTROL (continued)
|
Action |
Result |
|
|
|
8 |
If a matching photo is found, |
PhotoMenuItem newItem |
|
create a new PhotoMenuItem |
= new PhotoMenuItem(); |
|
object for this photo. |
newItem.tag = i; |
|
|
|
|
How-to |
newItem.Text = _album[i].FileName; |
|
newItem.Click += new |
|
|
a. Initialize the tag field to the |
|
|
EventHandler( |
|
|
photograph’s index. |
ctxtPhotoCal_MenuClick); |
|
b. Initialize the MenuItem.Text |
|
|
property to the image file |
|
|
name. |
|
|
c. Set a Click handler for this |
|
|
menu item. |
|
|
|
|
9 |
Add this new item to the context |
ctxtPhotoCal.MenuItems.Add(newItem); |
|
menu. |
} |
|
|
} |
|
|
|
10 |
If one or more matching |
if (ctxtPhotoCal.MenuItems.Count >= 1) |
|
photographs were found, display |
{ |
|
the context menu. |
ctxtPhotoCal.Show(monthCalDates, |
|
new Point(e.X, e.Y)); |
|
|
|
|
|
How-to |
} |
|
Use the Show method at the |
} |
|
} |
|
|
current mouse location. |
|
|
|
|
|
|
|
11 |
Create a private DisplayPhoto- |
private bool DisplayPhotoEditDlg(int index) |
|
EditDlg method to accept an |
{ |
|
album index and display the |
_album.CurrentPosition = index; |
|
|
|
|
associated dialog. |
using (PhotoEditDlg dlg |
|
Note: This method returns a |
= new PhotoEditDlg(_album)) |
|
{ |
|
|
boolean value indicating whether |
|
|
if (dlg.ShowDialog() == DialogResult.OK) |
|
|
the user modified any settings. |
{ |
|
|
_bAlbumChanged = true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
12 |
Implement a ctxtPhoto- |
private void ctxtPhotoCal_MenuClick |
|
Cal_MenuClick method to |
(object sender, System.EventArgs e) |
|
handle any context menu |
{ |
|
PhotoMenuItem mi = sender as PhotoMenuItem; |
|
|
selection and display the |
|
|
|
|
|
associated Photo Properties |
if ((mi != null) |
|
dialog. |
&& (DisplayPhotoEditDlg(mi.tag))) |
|
|
{ |
|
|
UpdateCalendar(); |
|
|
} |
|
|
} |
|
|
|
378 |
CHAPTER 11 MORE CONTROLS |
HANDLE A MOUSE CLICK IN THE CALENDAR CONTROL (continued)
|
Action |
Result |
|
|
|
13 |
Update the Click handler for the |
private void btnPhotoProp_Click |
|
photo’s Properties button on the |
(object sender, System.EventArgs e) |
|
Photos tab page to use the new |
{ |
|
if (_album.Count == 0) |
|
|
DisplayPhotoEditDlg method. |
|
|
return; |
|
|
|
if (lstPhotos.SelectedIndex >= 0) |
|
|
{ |
|
|
if (DisplayPhotoEditDlg( |
|
|
lstPhotos.SelectedIndex)) |
|
|
{ |
|
|
UpdateList(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
When the user clicks on the MonthCalendar control, this code will find and display any photographs associated with a selected date. Note how the HitTest method is used to retrieve information about the selected point. This method returns a TestInfo object. The HitTestInfo class is defined within the MonthCalendar class, and provides a HitArea property containing the type of area clicked by the user, and a Time property containing the DateTime value corresponding to the selected item. The possible values for the HitArea property are defined by the MonthCalendar.HitArea enumeration, as described in .NET Table 11.6.
private void monthCalDates_MouseDown
(object sender, System.Windows.Forms.MouseEventArgs e)
{
MonthCalendar.HitTestInfo info = monthCalDates.HitTest(e.X, e.Y); if (info.HitArea == MonthCalendar.HitArea.Date)
{
Another important part of this code is the definition and use of the PhotoMenuItem class. Without this class, we would be forced to search for a selected photograph based on the file name stored in the Text property of the menu. This rather simple extension to MenuItem provides an efficient method of communicating a photograph’s index from the context menu to a menu item’s Click handler.
private class PhotoMenuItem : MenuItem
{
// An integer field to store a photograph’s index public int tag;
}
Because this class is still a MenuItem instance, we can use it just like any other menu item object. We can set the Text property, establish a Click event handler, and add the menu to our context menu.
PhotoMenuItem newItem = new PhotoMenuItem();
newItem.tag = i;
CALENDARS |
379 |
newItem.Text = _album[i].FileName;
newItem.Click += new EventHandler(ctxtPhotoCal_MenuClick);
// Add this item to the context menu ctxtPhotoCal.MenuItems.Add(newItem);
After the context menu has been displayed, the Click handler receives the menu item object selected by the user. We downcast the given object into a PhotoMenuItem instance in order to retrieve the index in the photo album and display the appropriate Photo Properties dialog.
private void ctxtPhotoCal_MenuClick(object sender, System.EventArgs e)
{
PhotoMenuItem mi = sender as PhotoMenuItem;
if ((mi != null) && (DisplayPhotoEditDlg(mi.tag)))
{
UpdateCalendar();
}
}
Compile and run the application to see how all this code works. Click on a date where one or more photographs were taken and be amazed as a context menu pops up with the corresponding photos. Also try clicking on other aspects of the control to see what happens. In particular, see what happens when you click on the month and year in the title of the control. Note that your ability to alter the month and year displayed is restricted by the range of dates represented in the photo album.
More .NET The PhotoMenuItem class developed in this section extends the MenuItem object provided by the Windows Forms namespace. This technique is useful when you need a class that is similar to an existing control, and the ability to downcast objects in C# ensures that you can access the additional members of your derived class in a type-safe manner.
You can also build custom controls by extending the Control class directly. Windows Forms also provides a UserControl class that is specifically intended for building customized container controls. The Project menu in Visual Studio .NET includes an Add User Control item for creating such a control. There is a walkthrough in the .NET documentation entitled “Authoring a User Control with Visual C#” that introduces this concept.
Custom controls can also be tightly integrated into the Toolbox and other parts of Visual Studio .NET. The System.Windows.Forms.Design namespace contains the classes and other types to support such integration. If you are interested in this topic, search for more information at any of the .NET web sites listed in appendix D. In particular, as of this writing there is an article by Shawn Burke entitled “Writing Custom Designers for .NET Components” at the Microsoft Developer Network at msdn.microsoft.com.
380 |
CHAPTER 11 MORE CONTROLS |