- •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
UPDATE THE MENUIMAGE_CHILDCLICK METHOD TO USE THE NEW PANEL
|
Action |
Result |
|
|
|
3 |
Modify the code for the |
case DisplayMode.ActualSize: |
|
ActualSize display mode in a |
SetStyle(ControlStyles.ResizeRedraw, |
|
similar manner. |
false); |
|
pnlPhoto.AutoScroll = true; |
|
|
|
|
|
|
pnlPhoto.Invalidate(); |
|
|
break; |
|
|
. . . |
|
|
} |
|
|
|
That’s it for our menu handlers. The SetStyle method is a protected member and cannot be modified for our Panel class, so we just force the redraw to happen at the Form level as we did before. This will redraw the entire form and not just our panel, but it gets the job done. In this case, the drawing required outside of our panel is not overly complex, so this extra drawing should not be a problem.
On a more complex form, it would make sense to handle the Resize event for the pnlPhoto object instead of setting a form-level style as we do here. Handling the Resize event would allow us to only redraw the panel itself, and not the other parts of the Form.
The AutoScroll property is a public member of the ScrollableControl class, so we can set its value for the pnlPhoto object directly.
As you can see, because the Panel and Form classes are based on a similar class hierarchy, design changes like this are very easy to make in .NET. Let’s move on to our owner-drawn status bar.
7.4.3DRAWING THE STATUS BAR PANEL
Our status bar is drawn in the statusBar1_DrawItem method. This method must calculate the percentage of the image shown in the window. Since the image will now be displayed inside the Panel object, we must modify this routine to use the Panel client area rather than the MainForm one.
UPDATE THE STATUSBAR1_DRAWITEM METHOD TO USE THE PANEL
|
Action |
Result |
|
|
|
1 |
Locate the |
protected void statusBar1_DrawItem |
|
statusBar1_DrawItem |
(object sender, |
|
StatusBarDrawItemEventArgs sbdevent) |
|
|
method in the |
|
|
{ |
|
|
MainForm.cs file. |
|
|
. . . |
|
|
|
|
PANELS |
219 |
UPDATE THE STATUSBAR1_DRAWITEM METHOD TO USE THE PANEL (continued)
|
Action |
Result |
|
|
|
2 |
Modify the calculation of |
// Calculate percent of image shown |
|
the percent variable to |
int percent = 100; |
|
use the panel rather than |
if (_selectedMode == DisplayMode.ActualSize) |
|
{ |
|
|
the form. |
|
|
Photograph photo = _album.CurrentPhoto; |
|
|
|
Rectangle dr = pnlPhoto.ClientRectangle; |
|
|
int imgWidth = photo.Image.Width; |
|
|
int imgHeight = photo.Image.Height; |
|
|
percent = 100 |
|
|
* Math.Min(dr.Width, imgWidth) |
|
|
* Math.Min(dr.Height, imgHeight) |
|
|
/ (imgWidth * imgHeight); |
|
|
} |
|
|
. . . |
|
|
} |
|
|
|
Once again this change simply uses our private Panel field rather than the this keyword. Our last change is to draw the image inside the panel rather than on the form itself.
7.4.4DRAWING THE IMAGE
When drawing the image on the form, we were able to override the protected
OnPaint method that raises the Paint event. For the Panel object, we do not have access to protected members, so we must use the public Paint event to update the panel. Internally in the Windows Forms library, of course, the Panel control will use its own version of the OnPaint method to invoke our event handler.
ADD A PAINT HANDLER FOR THE PNLPHOTO OBJECT
|
Action |
Result |
|
|
|
1 |
Add a Paint event handler for the |
Visual Studio generates the appropriate code in the |
|
panel. |
source file. |
|
How-to |
protected void pnlPhoto_Paint |
|
Double-click the Panel control. |
(object sender, |
|
Note: The Paint event is the default |
System.Windows.Forms.PaintEventArgs e) |
|
{ |
|
|
event for the panel control in Visual |
} |
|
Studio. Other events can be added |
|
|
via the Properties window. |
|
|
|
|
Note that the Paint event handler receives a PaintEventArgs instance containing the event data. As we saw earlier in the chapter, this class contains the Graphics object for drawing inside the panel. Our code uses this object in the same way as when the image was drawn in the form. Continuing our previous steps:
220 |
CHAPTER 7 DRAWING AND SCROLLING |
TRANSFER THE DRAWING CODE INTO THE NEW PAINT HANDLER
|
Action |
Result |
|
|
|
2 |
In the pnlPhoto_Paint method, |
protected void pnlPhoto_Paint |
|
use the given Graphics to draw |
(object sender, |
|
the image when the album is not |
System.Windows.Forms.PaintEventArgs e) |
|
{ |
|
|
empty. |
|
|
if (_album.Count > 0) |
|
|
|
{ |
|
|
// Paint the current photo |
|
|
Photograph photo = _album.CurrentPhoto; |
|
|
Graphics g = e.Graphics; |
|
|
|
3 |
Copy the switch statement for |
switch (_selectedMode) |
|
drawing the image from the |
{ |
|
existing OnPaint method. |
. . . |
|
} |
|
|
|
|
|
|
} |
|
|
else |
|
|
{ |
|
|
// No image to paint |
|
|
} |
|
|
} |
|
|
|
4 |
Update this switch block to use |
switch (_selectedMode) |
|
the pnlPhoto object as |
{ |
|
appropriate. |
default: |
|
case DisplayMode.ScaleToFit: |
|
|
|
|
|
|
// Preserve aspect ratio of image |
|
|
g.DrawImage(photo.Image, |
|
|
photo.ScaleToFit( |
|
|
pnlPhoto.DisplayRectangle)); |
|
|
break; |
|
|
case DisplayMode.StretchToFit: |
|
|
// Fill entire panel with image |
|
|
g.DrawImage(photo.Image, |
|
|
pnlPhoto.DisplayRectangle); |
|
|
break; |
|
|
case DisplayMode.ActualSize: |
|
|
// Draw portion of image |
|
|
g.DrawImage(photo.Image, |
|
|
pnlPhoto.AutoScrollPosition.X, |
|
|
pnlPhoto.AutoScrollPosition.Y, |
|
|
photo.Image.Width, |
|
|
photo.Image.Height); |
|
|
pnlPhoto.AutoScrollMinSize |
|
|
= photo.Image.Size; |
|
|
break; |
|
|
} |
|
|
|
5 |
If the album is empty, draw the |
else |
|
standard system control color |
{ |
|
onto the panel. |
// No image to paint |
|
e.Graphics.Clear(SystemColors.Control); |
|
|
|
|
|
|
} |
|
|
} |
|
|
|
PANELS |
221 |
TRANSFER THE DRAWING CODE INTO THE NEW PAINT HANDLER (continued)
|
Action |
Result |
|
|
|
6 |
Remove the corresponding |
The OnPaint method now looks as follows: |
|
drawing code from the existing |
protected override void OnPaint |
|
OnPaint method. |
|
|
(PaintEventArgs e) |
|
|
|
{ |
|
|
if (_album.Count > 0) |
|
|
{ |
|
|
// Paint the current image |
|
|
Photograph photo = _album.CurrentPhoto; |
|
|
// Update the status bar. |
|
|
pnlFileName.Text = photo.Caption; |
|
|
pnlFileIndex.Text |
|
|
= String.Format("{0:#}/{1:#}", |
|
|
_album.CurrentIndex+1, |
|
|
_album.Count); |
|
|
pnlImageSize.Text |
|
|
= String.Format("{0:#} x {1:#}", |
|
|
photo.Image.Width, |
|
|
photo.Image.Height); |
|
|
statusBar1.ShowPanels = true; |
|
|
} |
|
|
else |
|
|
{ |
|
|
// Indicate the album is empty |
|
|
statusBar1.Text = "No Photos in Album"; |
|
|
statusBar1.ShowPanels = false; |
|
|
} |
|
|
|
7 |
At the end of this method, |
// Ensure contained controls are redrawn |
|
invalidate the panel to ensure it is |
pnlPhoto.Invalidate(); |
|
redrawn. |
statusBar1.Invalidate(); |
|
base.OnPaint(e); |
|
|
|
|
|
|
} |
|
|
|
It may look like a lot of code, but the number of changes is actually quite small, as indicated by the few number of bolded lines. The program is all set now. Verify that your code compiles and runs properly. Change display modes, use different-sized images, and resize the form to observe the effect.
TRY IT! If you are feeling brave, try adding a Fit to Width menu item to the Image submenu. This should preserve the aspect ratio of the image by scaling the image to match the width of the panel window. You will need to add a FitToWidth enumeration value to the DisplayMode enumeration. Calculate the height using code similar to the Photograph.ScaleToFit method where the width is preserved. The tricky part is setting the pnlPhoto.AutoScrollMinSize property appropriately and drawing the image into this same rectangle.
7.5RECAP
This chapter has looked at some drawing and scrolling aspects of the Form class. In particular, we removed the PictureBox control from our application and learned
222 |
CHAPTER 7 DRAWING AND SCROLLING |
how to draw our image directly onto the form. We used the protected OnPaint method and made use of the automated scroll bars inherited by the Form class to scroll our image. This did not work exactly as we wanted, so we modified our code to use the Panel class instead as a way to draw the image independent of the rest of the form.
The next chapter will continue our investigation of the Form class by looking at dialog boxes.
RECAP |
223 |