Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
551,33 KB
Nội dung
116 CHAPTER 4 STATUS BARS 4.3.2 ASSIGNING PANEL TEXT With our panels defined, we simply set the Text property value for each panel to have the text displayed by the application. This only works for panels with their Style property set to Text, of course. We will look at our owner-drawn panel in section 4.4. Since our panels only have meaning after an image is loaded, we assign their values as part of the Click event handler for the Load button, as indicated by the following steps. .NET Table 4.3 StatusBarPanel class The StatusBarPanel class is a component that appears as a panel within a StatusBar con- trol. This class is part of the System.Windows.Forms namespace, and inherits from the Sys- tem.ComponentModel.Component class. A panel must be associated with a StatusBar instance with its ShowPanels property set to true in order to appear on a form. Public Properties Alignment Gets or sets the HorizontalAlignment for the panel’s text. AutoSize Gets or sets how the panel is sized within the status bar. BorderStyle Gets or sets the type of border to display for the panel, if any. MinWidth Gets or sets the minimum width for the panel. Parent Gets the StatusBar object that contains this panel. Style Gets or sets the style used to draw the panel. Text Gets or sets the text for the panel. ToolTipText Gets or sets the tool tip for the panel. Width Gets the current width or sets the default width for the panel. Public Methods BeginInit Begins initialization of the panel when used within a form or other component. EndInit Ends initialization of the panel when used within a form or other component. SET THE TEXT TO APPEAR IN THE PANELS Action Result 1 In the menuLoad_Click method, set the ShowPanels property to false while the image is loading. private void menuLoad_Click (object sender, System.EventArgs e) { . . . try { statusBar1.ShowPanels = false; STATUS BAR PANELS 117 Look again at the new try block. try { statusBar1.ShowPanels = false; statusBar1.Text = "Loading " + dlg.FileName; pbxPhoto.Image = new Bitmap(dlg.OpenFile()); statusBar1.Text = "Loaded " + dlg.FileName; this.sbpnlFileName.Text = dlg.FileName; this.sbpnlImageSize.Text = String.Format("{0:#} x {1:#}", pbxPhoto.Image.Width, pbxPhoto.Image.Height); statusBar1.ShowPanels = true; } Two items are worth noting in this code: b The ShowPanels property is set to false while an image is loading so that the Sta- tusBar.Text property setting will appear, and set to true after the image is loaded and the panels are set. c The Format method used here is a static method provided by the String class for constructing a string. We could spend a chapter covering this and other features avail- able in C# strings generally and the .NET System.String class specifically, but instead will assume you can look this one up in the documentation. In the code shown here, the "{0:#} x {1:#}" string indicates that two parameters are required, both of them integers. Build and run the application to see these panels in action. Resize the window to see how the panels react. You will notice that the first panel resizes automatically along with the window, while the second two panels maintain their initial size. This is con- sistent with the AutoSize settings we used for these objects. 2 Initialize the sbpnlFileName and sbpnlImageSize panels after the image is success- fully loaded. statusBar1.Text = "Loading " + dlg.FileName; pbxPhoto.Image = new Bitmap(dlg.OpenFile()); statusBar1.Text = "Loaded " + dlg.FileName; this.sbpnlFileName.Text = dlg.FileName; this.sbpnlImageSize.Text = String.Format("{0:#} x {1:#}", pbxPhoto.Image.Width, pbxPhoto.Image.Height); 3 Set the ShowPanels property to true so the panel text will appear. statusBar1.ShowPanels = true; } . . . } SET THE TEXT TO APPEAR IN THE PANELS (continued) Action Result b Disable the panels c Create image size string 118 CHAPTER 4 STATUS BARS 4.4 OWNER-DRAWN PANELS So what about this owner-drawn panel? Text panels do not need to worry about drawing their text onto the panel, as the .NET Framework handles this internally. There are some cases where text just will not do, and these situations requiring man- ual drawing of the panel. Drawing of panels and other objects in .NET are handled through use of the Sys- tem.Drawing namespace, sometimes referred to as GDI+ since it is based on an update to the graphical drawing interface provided by Microsoft. Components such as menus, status bars, and tabs that contain drawable components support a DrawItem event that occurs when an item in the component should be drawn. Controls derived from the Control class provide a Paint event for this purpose. Both types of drawing make use of the Graphics class discussed in this section in order to draw the item. This section will examine how owner-drawn status bar panels are supported, and draw the sbpnlImagePercent panel for our application. A similar discussion would apply to owner-drawn menu items or other objects supporting the DrawItem event. The result of our changes is shown in figure 4.5. As you can see in the figure, when the image is displayed in Actual Size mode, the third panel will show a numeric and visual representation of how much of the image is displayed. Before we draw this panel, let’s take a closer look at the DrawItem event. 4.4.1 T HE DRAWITEM EVENT The DrawItem event is used by a number of classes to draw an item contained within some sort of larger collection. For instance, the MenuItem, ListBox, and ComboBox classes all include a DrawItem event for custom drawing of their con- tents. These classes use the DrawItemEventArgs class to provide the data associ- ated with the event. The StatusBar class uses a derived version of this class, but the bulk of the drawing information is in the base class. An overview of this base class is provided in .NET Table 4.4. Figure 4.5 The third status bar panel here indicates that 30 percent of the image is visible in the window. OWNER-DRAWN PANELS 119 For the StatusBar class, the StatusBarDrawItemEventArgs class derives from the DrawItemEventArgs class and is received by StatusBar.DrawItem event handlers. The Panel property provided by this class is useful both for identifying the panel and when the text assigned to the panel is needed. When a DrawItem event handler is invoked, the default property values are what you might expect. The Bounds property is set to the display rectangle of the panel to draw. This rectangle is with respect to the rectangle for the containing status bar, so the upper left corner of a panel’s bounds is not (0,0). The Font and ForeColor prop- erties are set to the font information for the StatusBar object; the Graphics prop- erty to an appropriate drawing object, the Index to the zero-based index number of the panel, and State is typically set to DrawItemState.None. The DrawItem event is called once for each panel drawn. .NET Table 4.4 DrawItemEventArgs class The DrawItemEventArgs class is an event object used when handling DrawItem events in a number of classes. This class is part of the System.Windows.Forms namespace, and inher- its from the System.EventArgs class. Practically, this class is used to manually draw list box items, menu items, status bar panels and other objects. The StatusBarDrawItemEventArgs class extends this class for use with StatusBar objects. This class includes a public Panel property to indicate which panel requires drawing. Public Properties Bounds Gets the Rectangle of the area to be drawn with respect to the entire graphical area for the object. Font Gets a suggested Font to use for any text. Typically, this is the parent’s Font property. ForeColor Gets a suggested Color to use for foreground elements, such as text. Typically, this is SystemColors.WindowText, or SystemColors.HighlightText if the object is selected. Graphics Gets the Graphics object to use for painting the item. Index Gets the index of the item to be painted. The exact meaning of this property depends on the object. State Gets additional state information on the object, using the DrawItemState enumeration. Examples include whether the item is selected, enabled, has the focus, or is checked (for menus). Public Methods DrawBackground Draws the Bounds rectangle with the default background color. DrawFocusRectangle Draws a focus rectangle in the Bounds area. 120 CHAPTER 4 STATUS BARS .NET Table 4.5 System.Drawing namespace The System.Drawing namespace provides access to basic graphics functionality provided by the graphical device interface (GDI+). The classes in this namespace are used when drawing to any display device such as a screen or printer, and to represent drawing primitives such as rectangles and points. Classes Brush An abstract class representing an object used to fill the interior of a graphical shape. For example, the Graphics.FillRectangle method uses a brush to fill a rectangular area on a drawing surface. Classes derived from this class include the SolidBrush and TextureBrush classes. Brushes A sealed class that provides Brush objects for all standard colors. For example, the Brushes.Red property can be used to fill shapes with a solid red color. Font Represents a font that defines how text is drawn. This includes the font style and size as well as the font face. Graphics Represents a GDI+ drawing surface. Members are provided to draw shapes, lines, images, and other objects onto the drawing surface. Image An abstract class for image objects such as Bitmap. Pen Represents an object used to draw lines and curves. A pen can draw a line in any color and specify various styles such as line widths, dash styles, and ending shapes (such as arrows). For example, the Graphics.DrawRectangle method uses a pen to draw the outline of a rectangular area on a drawing surface. Region Represents the interior of a graphics shape composed of rectangles and paths. SystemColors A sealed class that provides Color objects for the colors configured in the local Windows operating system. For example, the SystemColors.Control property returns the color configured for filling the surface of controls. Similar classes also exist for Brush, Pen, and Icon objects based on the local system configuration. Color Stores a color value. A number of static colors are defined, such as Color.Red, or a custom color can be created from an alpha component value and a set of RGB values. Structures Point A two-dimensional point as an integral x and y coordinate. PointF A two-dimensional point as a floating point x and y coordinate. Rectangle Stores the location and size of a rectangular region within a two-dimensional area. All coordinates are integral values. Size Represents the size of a rectangular region as an integral width and height. SizeF Represents the size of a rectangular region as a floating point width and height. OWNER-DRAWN PANELS 121 A number of classes are available in the System.Drawing namespace for drawing status bar panels, menu items, and other objects. An overview of this namespace is provided in .NET Table 4.5. Rather than provide detailed coverage of this namespace in any one chapter of the book, we will visit members of this namespace as required by our application. In particular, we will use this namespace again in chapter 7 when drawing on Form and Panel controls, and also in chapter 10 when discussing owner-drawn list boxes. 4.4.2 D RAWING A PANEL So let’s draw the panel in our application. If you recall, we want this panel to show what percentage of the image is shown in the PictureBox control. To do this, we need to handle the DrawItem event. We will build this code step by step. The com- plete code for the handler is shown following the table. Set the version number of the application to 4.4. ADD DRAWITEM HANDLER Action Result 1 Handle the DrawItem event for the StatusBar control in the MainForm.cs [Design] window. An event handler for the DrawItem event is added to the control. protected void statusBar1_DrawItem (object sender, StatusBarDrawItemEventArgs sbdevent) { 2 In this handler, check that the panel to draw is the sbpnlImagePercent panel. Note: This if statement is not strictly necessary. Still, since the event relates to the entire status bar and not just this panel, this provides some robustness against future changes. if (sbdevent.Panel == sbpnlImagePercent) { // Calculate the percent of the image shown // Calculate the rectangle to fill // Draw the rectangle in the panel // Draw the text on top of the rectangle } } Note: The four comments here are the four steps that must be performed to draw the panel. Each step is performed in the subsequent four steps of this table. How-to In the Properties window for the status bar, double-click the DrawItem entry. 122 CHAPTER 4 STATUS BARS The complete code for this handler is shown as follows: protected void statusBar1_DrawItem (object sender, StatusBarDrawItemEventArgs sbdevent) { if (sbdevent.Panel == sbpnlImagePercent) 3 Calculate what percentage of the image appears in the window. // Calculate the percent of the image shown int percent = 100; if (pbxPhoto.SizeMode != PictureBoxSizeMode.StretchImage) { Rectangle dr = pbxPhoto.ClientRectangle; int imgWidth = pbxPhoto.Image.Width; int imgHeight = pbxPhoto.Image.Height; percent = 100 * Math.Min(dr.Width, imgWidth) * Math.Min(dr.Height, imgHeight) / (imgWidth * imgHeight); } 4 Calculate the rectangular region to fill. // Calculate the rectangle to fill Rectangle fillRect = sbdevent.Bounds; fillRect.Width = sbdevent.Bounds.Width * percent / 100; 5 Draw this rectangle in the panel. // Draw the rectangle in the panel sbdevent.Graphics.FillRectangle( Brushes.SlateGray, fillRect); Note: We could also have used the ForeColor prop- erty of the event as the color here. This code illus- trates using the Brushes class, which provides access to a Brush object for all standard colors available in the framework. 6 Draw the percentage value in the panel. // Draw the text on top of the rectangle sbdevent.Graphics.DrawString( percent.ToString() + "%", sbdevent.Font, Brushes.White, sbdevent.Bounds); Note: White is a good color choice if used with the default desktop colors. It may not be a good choice if custom desktop colors are used. ADD DRAWITEM HANDLER (continued) Action Result How-to a. If the SizeMode setting for the image is StretchIm- age, use 100% of the panel. b. Otherwise, divide the smaller of the display area and the image size by the total image area. c. For simplicity, use integer percent values. How-to Use the event’s Bounds property and adjust its Width based on the calculated percent. How-to a. Use the Graphics object for the event. b. Paint the rectangle with the FillRectangle method, using a SlateGray brush. How-to Use the DrawString method for the Graphics object. OWNER-DRAWN PANELS 123 { // Calculate the percent of the image shown int percent = 100; if (pbxPhoto.SizeMode != PictureBoxSizeMode.StretchImage) { Rectangle dr = pbxPhoto.ClientRectangle; int imgWidth = pbxPhoto.Image.Width; int imgHeight = pbxPhoto.Image.Height; percent = 100 * Math.Min(dr.Width, imgWidth) * Math.Min(dr.Height, imgHeight) / (imgWidth * imgHeight); } // Calculate the rectangle to fill Rectangle percentRect = sbdevent.Bounds; percentRect.Width = sbdevent.Bounds.Width * percent / 100; // Draw the rectangle in the panel sbdevent.Graphics.FillRectangle(Brushes.SlateGray, percentRect); // Draw the text on top of the rectangle sbdevent.Graphics.DrawString(percent.ToString() + "%", sbdevent.Font, Brushes.White, sbdevent.Bounds); } } The Graphics class used in this handler provides a rich set of drawing capabilities, from circles, ellipses, and rectangles to polygons, pie shapes, and bezier curves. Here we use the FillRectangle method, which requires a Brush object to use when “painting” the rectangle. In chapter 7, we will make additional use of this class. See .NET Table 4.6 for an overview of some of the more interesting members of this class. It should be noted that the statusBar1_DrawItem handler is invoked each time a panel must be redrawn. As a result, care should be taken in handlers such as this to avoid expensive calculations or other operations that might adversely affect the performance of the application. For example, if we had generated a custom Brush object while filling the rectangle here, such an operation would be performed each time the handler is invoked, potentially using an excessive amount of memory over the life of the application. Of course, our choice of the SlateGray color might not be the best choice either, as it might interfere with colors the user has selected for their desktop. A better option here might be to determine a color programmatically based on the user’s desktop settings, and generate a single Brush object the first time the event handler is invoked that is reused for the life of the application. You can compile and run this code so far if you like, but we do need to make one more change. When the PictureBox.SizeMode property is StretchImage, the complete image (100%) is always shown. When SizeMode is set to Normal, the amount of image shown varies as the size of the client area changes. As a result, when the user changes this setting, we need to make sure that our panel is redrawn by inval- idating the contents of the status bar. 124 CHAPTER 4 STATUS BARS .NET Table 4.6 Graphics class The Graphics class is a drawing object that encapsulates a drawing surface , or more specif- ically a graphical device interface (GDI+) drawing surface. This class is part of the Sys- tem.Drawing namespace, and inherits from the System.MarshalByRefObject class. Drawing the outline of a shape typically requires a Pen object, while drawing a filled-in shape typically requires a Brush object. This class contains a large number of members, but the list here should provide some idea of the supported functionality. Public Static Properties FromHdc Returns a Graphics instance from a given handle to a device context. FromHwnd Returns a Graphics instance from a given window handle. Public Properties Clip Gets or sets as a Region object the portion of the graphics area available for visible drawing. DpiX Gets the horizontal resolution supported by the object. DpiY Gets the vertical resolution supported by the object. PageUnit Gets or sets the GraphicsUnit value specifying the unit of measure for page coordinates. SmoothingMode Gets or sets the SmoothingMode value indicating how shapes are rendered with this object. TextRenderingHint Gets or sets the TextRenderingHint value indicating how text is rendered with this object. Public Methods Clear Fills the entire drawing surface with a specified color. DrawCurve Draws a curve specified as an array of points using a given Pen. DrawEllipse Draws the outline of an ellipse (which might be a circle) bounded by a given rectangle using a given Pen. DrawLine Draws a line using a given Pen. DrawRectangle Draws the outline of a rectangle using a given Pen. FillClosedCurve Fills the interior of a closed curve specified as an array of points using a given Brush. FillEllipse Fills the interior of an ellipse (which might be a circle) bounded by a given rectangle using a given Brush. FillRectangle Fills the interior of a rectangle using a given Brush. MeasureString Returns the size a given string would occupy using a given Font. RECAP 125 If you recall, our menus invoke the menuImage_ChildClick method to alter the display mode by assigning a new SizeMode value. Now the status bar will be redrawn whenever the SizeMode property is altered. Note that this change highlights another advantage of our decision in chapter 3 to handle the Click of an Image submenu item with a shared handler. If we decided to add additional display modes in the future, this code will ensure that the status bar is redrawn correctly each time it changes. Compile and run your application to verify that the code works as expected. Dis- play an image in both Stretch to Fit and Actual Size mode to see how the owner-drawn status bar panel behaves when the application is resized. 4.5 RECAP This chapter introduced the StatusBar class and showed how both text and panel information are displayed in this control. We looked at how to switch between the display of text and panels in a status bar, and discussed how various properties can be used to alter the appearance and behavior of status bar panels. We also presented the base class of all Windows Forms controls by looking at the Control class in some detail. A discussion of owner-drawn panels and the use of the DrawItem and Paint events led to a discussion of the System.Drawing namespace in general, and the Graphics class in particular. The next chapter takes us out of the Windows Forms namespace briefly in order to discuss reusable libraries. INVALIDATE STATUS BAR Action Result 7 Modify the menuImage_Child- Click method to force a redraw of the status bar. protected void menuImage_ChildClick(object sender, System.EventArgs e) { if (sender is MenuItem) { MenuItem mi = (MenuItem)sender; nSelectedImageMode = mi.Index; pbxPhoto.SizeMode = this.modeMenuArray[mi.Index]; pbxPhoto.Invalidate(); statusBar1.Invalidate(); } } [...]... This chapter is our chance to lean back in our respective chairs, take stock of where we’ve been, and plan for the future Before we jump back into the Windows Forms classes in chapter 6, we will build some infrastructure and introduce some important programming concepts Some of you may be familiar or comfortable with these concepts; others may not The discussion will attempt to provide enough material... class represents a single /// photo and its properties /// public class Photograph Once again, let’s modify the namespace to be Manning. MyPhotoAlbum MODIFY THE NAMESPACE Action 4 Result Modify the namespace to be Manning. MyPhotoAlbum namespace Manning { namespace MyPhotoAlbum { } } We now have a fully functional class as part of our library Of course, it doesn’t do anything yet Let’s... the startup project in Visual Studio NET terms That’s all it takes The solution MyPhotos now contains two projects: a MyPhotoAlbum project to create a DLL library, and a MyPhotos project to create a Windows Forms application You will note that the new project has its own AssemblyInfo.cs file to support an independent version number for the library CLASS LIBRARIES 135 We do not want a class called Class1,... outside of this book (hey, you never know!), we should follow this convention as well We will use the publisher’s name Manning as our top-level namespace 136 CHA PTE R 5 REUSABLE LIBRARIES MODIFY THE CLASS NAMESPACE Action 7 Modify the entire MyPhotoAlbum namespace to exist within the Manning namespace How-to Enter the bolded text into the PhotoAlbum.cs file When you type the final brace, Visual Studio... command-line tools discussed in chapter 1 are used for this purpose The next section provides a short discussion on this topic 5.2.2 138 USING THE COMMAND-LINE TOOLS As we saw in chapter 1, you can build Windows Forms applications without using Visual Studio NET The interactive environment makes a number of tasks easier, but also uses memory and other system resources On a computer with limited resources,... by the NET runtime until it is incorporated in an assembly manifest using the /addmodule switch This permits collections of files to become a single assembly /target:winexe Creates a Windows application (.exe) When a Windows application is run in a console window, the console does not wait for the application to exit This is different than a console application, where the console does in fact wait... derived classes to modify the collection OnClear Performs additional custom processing before clearing the contents of the collection This can be used by derived classes to perform any required actions before the collection is cleared OnInsert Protected Properties Gets or sets the actual number of objects in the array Clear Public Methods Count Performs additional custom processing before inserting... example, a genealogy program for creating family trees might want to link to a photo album of a specific person or family So we will place our new classes in a library that other programs can reuse In Windows parlance, such a library is called a Dynamic Link Library, or DLL 5.1.1 INTERFACES As you might expect, the NET Framework provides a number of classes that can help us here These classes implement... line later in the chapter In this section we will create a new project as part of our MyPhotos solution This project will build the new MyPhotoAlbum library We will create a top-level namespace called Manning for this project, and reference the new library from our MyPhotos project Set the version number of the application to 5.2 CREATE A REUSABLE LIBRARY IN VISUAL STUDIO NET Action 1 Result Add a new... the MyPhotos application since in this project the namespace is not likely to be used outside of the application itself Result The PhotoAlbum.cs file should now look as follows: using System; namespace Manning { namespace MyPhotoAlbum { /// /// Summary description for PhotoAlbum /// public class PhotoAlbum { public PhotoAlbum() { // // TODO: Add Constructor Logic here // } } } } . plan for the future. Before we jump back into the Windows Forms classes in chapter 6, we will build some infrastructure and introduce some important programming concepts. Some of you may be familiar. used when handling DrawItem events in a number of classes. This class is part of the System .Windows. Forms namespace, and inher- its from the System.EventArgs class. Practically, this class is. alter the appearance and behavior of status bar panels. We also presented the base class of all Windows Forms controls by looking at the Control class in some detail. A discussion of owner-drawn panels