- •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
8.4.4UPDATING THE PIXELDLG FORM
So far we have created and displayed our form as a modeless dialog. In this section we will implement the code to update this dialog based on the current location of the mouse pointer in the pnlPhoto control. We will account for the fact that a photo might not be displayed, and that the mouse pointer may be located outside of the panel.
This code for UpdatePixelData is a bit long, so let’s get to it.
IMPLEMENT UPDATEPIXELDATA METHOD
|
Action |
Result |
|
|
|
1 |
In the MainForm.cs window, add |
protected void UpdatePixelData |
|
an UpdatePixelData method to |
(int xPos, int yPos) |
|
the end of the file. |
{ |
|
|
|
|
|
|
2 |
Return immediately if the |
if (_dlgPixel == null || !_dlgPixel.Visible) |
|
PixelDlg does not exist or is not |
return; |
|
visible. |
|
|
|
|
3 |
Get the currently display photo. |
Photograph photo = _album.CurrentPhoto; |
|
|
|
4 |
Display all zeros if a Photograph |
Rectangle r = pnlPhoto.ClientRectangle; |
|
is not displayed or the given |
if (photo == null |
|
coordinates are outside the |
|| !(r.Contains(xPos,yPos))) |
|
{ |
|
|
display area. |
|
|
_dlgPixel.Text = ((photo == null) |
|
|
Note: The question mark ‘?’ syn- |
? " " : photo.Caption); |
|
_dlgPixel.XVal = 0; |
|
|
tax here works the same as in |
|
|
_dlgPixel.YVal = 0; |
|
|
C++. |
_dlgPixel.RedVal = 0; |
|
|
_dlgPixel.GreenVal = 0; |
|
|
_dlgPixel.BlueVal = 0; |
|
|
_dlgPixel.Update(); |
|
|
return; |
|
|
} |
|
|
|
5 |
Display the caption for the current |
_dlgPixel.Text = photo.Caption; |
|
image in the title bar of our dialog. |
|
|
|
|
6 |
Use a switch statement to |
// Calc x and y position in the photo |
|
determine the current display |
int x = 0, y = 0; |
|
mode. |
Bitmap bmp = photo.Image; |
|
switch (this._selectedMode) |
|
|
|
|
|
Note: The calculation here |
{ |
|
depends on how the image is |
|
|
displayed, so a switch state- |
|
|
ment is required. |
|
|
|
|
7 |
Implement the Actual Size display |
case DisplayMode.ActualSize: |
|
mode logic. |
// Panel coords equal image coords |
|
Note: In this mode, the display |
x = xPos; |
|
y = yPos; |
|
|
area and image area are equiva- |
break; |
|
lent. |
|
|
|
|
MODELESS DIALOGS |
259 |
|
IMPLEMENT UPDATEPIXELDATA METHOD |
(continued) |
|
|
|
|
|
|
Action |
|
Result |
|
|
|
|
8 |
Implement the Stretch to Fit |
case DisplayMode.StretchToFit: |
|
|
display mode logic. |
// Translate panel coords to image |
|
|
Note: In this mode, the image |
x = xPos * bmp.Width / r.Width; |
|
|
y = yPos * bmp.Height / r.Height; |
||
|
fills the entire display area, so we |
break; |
|
|
convert from display position to |
|
|
|
image location. |
|
|
|
|
|
|
9 |
Implement the Scale to Fit display |
case DisplayMode.ScaleToFit: |
|
|
mode logic. |
// Determine image rectangle. |
|
|
|
Rectangle r2 = photo.ScaleToFit(r); |
|
|
How-to |
if (!r2.Contains(xPos, yPos)) |
|
|
a. Calculate the rectangle contain- |
||
|
return; |
// Mouse outside image |
|
|
ing the image using the |
||
|
|
|
|
|
ScaleToFit method in the |
// Translate r2 coords to image |
|
|
Photograph class. |
x = (xPos - r2.Left) |
|
|
b. If the mouse pointer is outside |
* bmp.Width / r2.Width; |
|
|
y = (yPos - r2.Top) |
||
|
this rectangle, it is not in the |
* bmp.Height / r2.Height; |
|
|
image. |
break; |
|
|
c. Otherwise, convert this rectan- |
} |
|
|
|
|
|
|
gle into image coordinates. |
|
|
|
|
|
|
10 |
Retrieve the color of the pixel at |
// Extract color at calculated location |
|
|
the calculated image location. |
Color c = bmp.GetPixel(x, y); |
|
|
How-to |
|
|
|
Use the Bitmap.GetPixel |
|
|
|
method. |
|
|
|
|
|
|
11 |
Finally, update the pixel dialog |
// Update PixelDlg with new values |
|
|
with the appropriate values. |
_dlgPixel.XVal = x; |
|
|
How-to |
_dlgPixel.YVal = y; |
|
|
_dlgPixel.RedVal = c.R; |
||
|
For the RGB color values, use the |
_dlgPixel.GreenVal = c.G; |
|
|
R, G, and B properties in the |
_dlgPixel.BlueVal = c.B; |
|
|
_dlgPixel.Update(); |
||
|
Color structure. |
||
|
} |
|
|
|
|
|
|
And there you have it. This method updates the PixelDlg form each time it is called. Since the explanation of each step is embedded in the table, we will not discuss this code further.
Our final task is to make sure this method is called each time the mouse pointer moves or the displayed photograph changes.
8.4.5UPDATING PIXELDLG AS THE MOUSE MOVES
In the previous section we waded through the logic necessary to convert the current mouse pointer location in panel coordinates to the corresponding image coordinates to update the PixelDlg form correctly. Next, we need to ensure that UpdatePixelData is called whenever appropriate.
260 |
CHAPTER 8 DIALOG BOXES |
The most obvious time is whenever the location of the mouse pointer changes. There is a MouseMove event inherited from the Control class for this purpose. The protected OnMouseMove method raises this event, so we could override OnMouseMove in our Form class. In this case, we would have to convert from Form coordinates to Panel coordinates, so handling the event for the Panel class is probably a better choice. More importantly, by handling mouse pointer movements in the Panel object directly, our code is only called when the movement occurs inside the panel.
CALL THE UPDATEPIXELDATA METHOD WHEN THE MOUSE MOVES
|
ACTION |
RESULT |
|
|
|
1 |
In the MainForm.cs |
protected void pnlPhoto_MouseMove |
|
[Design] window, add a |
(object sender, |
|
MouseMove event handler |
System.Windows.Forms.MouseEventArgs e) |
|
{ |
|
|
for the pnlPhoto object. |
|
|
|
|
|
|
|
2 |
Call the UpdatePixelData |
UpdatePixelData(e.X, e.Y); |
|
method with the current |
} |
|
mouse pointer coordinates. |
|
|
|
|
The MouseMove event handler receives a MouseEventArgs parameter containing, among other event data, an X and Y property defining the current coordinates of the mouse pointer in the control’s coordinates. We will discuss this and other mouse events in chapter 12, so we will not go into more detail on this handler here.
The one other instance when the pixel values must be updated is when the displayed image changes. The easiest place to track this is when the Panel is painted in the pnlPhoto_Paint method. Continuing the previous steps:
CALL UPDATEPIXELDATA WHEN CURRENT PHOTO CHANGES
|
Action |
Result |
|
|
|
3 |
Locate the pnlPhoto_Paint |
protected void pnlPhoto_Paint(. . .) |
|
method in the MainForm.cs |
{ |
|
source file. |
|
|
|
|
4 |
Call UpdatePixelData if a new |
// Update PixelDlg if photo has changed |
|
photo is displayed. |
if ((_dlgPixel != null) && (_nPixelDlgIndex |
|
|
!= _album.CurrentPosition)) |
|
|
{ |
|
|
_nPixelDlgIndex = _album.CurrentPosition; |
|
|
Point p = pnlPhoto.PointToClient( |
|
|
Form.MousePosition); |
|
|
UpdatePixelData(p.X, p.Y); |
|
|
} |
|
|
// Paint the current photo, if any |
|
|
if (_album.Count > 0) |
|
|
{ |
|
|
. . . |
|
|
} |
|
|
} |
|
|
|
MODELESS DIALOGS |
261 |
This code uses the same Form.MousePosition method and nonstatic Panel.PointToClient we saw earlier in this section.
Our modeless dialog is finished. Compile your code, show your friends, and otherwise verify that the dialog works properly. Note how both the form and the dialog can be manipulated at the same time, and how the dialog behaves when you display the next or previous image in an album, with the mouse cursor both inside and outside the panel control.
TRY IT! One nice change you could make here is to modify the cursor used for the Panel control to use a small cross-hair rather than the normal arrow. Do this by changing the Cursor property for the Panel class to use the Cross cursor.
Another interesting change is to allow the user to hide the PixelDlg window using the main application’s menu bars. One way to do this is to modify the Text displayed for the menuPixelData menu to be “Hide Pi&xel Data” whenever the dialog is displayed and back to “Pi&xel Data” whenever the dialog is hidden or closed. Set the appropriate menu text in the menuView_Popup handler, and use the Hide method or the Visible property to hide the dialog.
Before we move on to the next topic, let’s give a quick summary of what we covered in this chapter.
8.5RECAP
In this chapter we looked at dialog boxes. We began with simple dialogs using the MessageBox class, and then created a custom modal dialog based on the Form class, followed by a custom modeless dialog. Along the way we discussed the difference between modal and modeless dialog boxes, caught potential exceptions when opening and saving our album files, examined the relationship between the Close and Dispose methods, saw how to intercept a closing window using the OnClosing method, and learned how to track the mouse pointer within a panel control.
We are not done with dialog boxes. Since our main form is getting rather full, future topics will require dialogs in order to continue this book and expand the capabilities of our program. In particular, the next chapter will create a dialog for both our current album and the individual photos in the album as a way to introduce specific Windows Forms controls in more detail.
262 |
CHAPTER 8 DIALOG BOXES |