Developing Desktop Applications 02

MDI Applications
Multiple document interface (MDI) applications permit more than one document to be open at a time. This is in contrast to single document interface (SDI) applications, which can manipulate only one document at a time. 



Visual Studio .NET is an example of an MDI application—many source files and design views can be open at once. In contrast, Notepad is an example of an SDI application—opening a document closes any previously opened document.
There is more to MDI applications than their ability to have multiple files open at once. The Microsoft Windows platform SDK specifies several UI behaviors that MDI applications should implement. The Windows operating system provides support for these behaviors, and this support is exposed through Windows Forms as well.

Parent and Child Forms
MDI applications consist of a main form, which does not itself display any data, and one or more child forms, which appear only within the main form and are used for displaying documents. The main form is called the MDI parent, and the child forms are called the MDI children. The Form class has two properties that control whether a given form is an MDI parent, MDI child, or neither. The Boolean IsMdiContainer property determines whether a form behaves as an MDI parent. The MdiParent property (which is of type Form) controls whether a form behaves as an MDI child. Setting the MdiParent property of a form to reference the application's MDI parent form makes the    form an MDI child form. Example 4-6 shows the minimum amount of code required to display an MDI parent form containing a single MDI child form.

Example 4-6. A minimal MDI application
CODES:
Assuming that the code in Example 4-6 is saved in a file named MyApp.vb, it can be compiled from the command line with this command:
CODES:
Running the resulting executable produces the display shown in Figure 8.

Figure 8.
The Form class has two read-only properties related to MDI behavior. The IsMdiChild property returns a Boolean value that indicates whether the form is an MDI child. The MdiChildren property of a parent form contains a collection of references to the form's child forms. The IsMdiChild and MdiChildren properties are both automatically maintained in response to setting the child forms' MdiParent properties.

Creating a Window Menu
MDI applications usually have a main-menu item called Window. On this menu appear standard items for cascading, tiling, and activating child windows and arranging the icons of minimized child windows. Figure 9 shows a typical example.

Figure 9
Example 4-7. An MDI application with a Window menu
CODES:
To add a Window menu to the parent form of an MDI application, perform the following steps. First, add a menu item to the MDI parent form's main menu, setting its Text property to anything desired (usually Window) and its MdiList property to True. It is the MdiList property that makes the Window menu a Window menu. Setting the MdiList property to True causes the Windows Forms framework to add and delete menu items to and from this menu item as necessary. This in turn will always display the current list of MDI child windows in the menu.
Next, add menu items for Cascade, Tile Horizontally, Tile Vertically, and Arrange Icons. In the Click vent handler for each of these menu items, call the Form class's LayoutMdi method, passing the appropriate parameter value for the desired action. The syntax of the LayoutMdi method is:
CODES:
The method's single argument must be a value from the MdiLayout enumeration (defined in the System.Windows.Forms namespace). The values in this enumeration are:

ArrangeIcons
Indicates that the icons for the minimized MDI child windows should be neatly arranged.

Cascade
Indicates that the MDI child windows should be cascaded (displayed overlapping each other).

TileHorizontal
Indicates that the MDI child windows should be tiled (displayed without overlapping), with each child window filling the width of the MDI parent.

TileVertical
Indicates that the MDI child windows should be tiled, with each child window filling the height of the MDI parent.

Merging Menus
Often, the items that should appear on an MDI application's main menu are dependent on the type of document being displayed or on whether any document is displayed at all. Of course, this effect could be achieved in code by dynamically adding and removing menu items each time a child window is activated. However, the Windows Forms framework provides an easier way. 

If an MDI child form has a main menu of its own, it and the MDI parent form's main menu are merged to produce the menu that is shown to the user when the child form is displayed. Two properties of the MenuItem class affect how the menu items are merged. First, the MergeOrder property determines the order in which the menu items are displayed. This property can be set to any Integer value, and the values don't have to be contiguous. The menu items from the two menus are sorted on this value to determine the order in which the menu items are displayed on screen.

For example, consider an MDI parent form that has a main menu with three menu items representing File, Window, and Help menus. Further, say that the MergeOrder properties of these menu items are 10, 20, and 30, respectively. Now, if an MDI child form is displayed and its main menu has, for example, an Edit item with a MergeOrder property value of 15, the menu displayed to the user will have four items: File, Edit, Window, and Help, in that order. Example 4-8 shows a revised version of Example 4-6 that contains the code necessary to create such a menu; lines shown in boldface have been added to define the main menu and its menu items.

Example 4-8. An MDI application with merged menus
CODES:
If a menu item in the MDI child form menu has the same MergeOrder value as a menu item in the MDI parent form menu, a second property comes into play. The MergeType property of both MenuItem objects is examined, and the behavior is determined by the combination of their values. The MergeType property is of type MenuMerge (an enumeration defined in the System.Windows.Forms namespace) and can have one of the following values:

Add
The menu item appears as a separate item in the target menu, regardless of the setting of the other menu item's MergeType property.

MergeItems
If the other menu item's MergeType property is also set to MergeItems, the two menu items are merged into a single item in the target menu. Merging is then recursively applied to the subitems of the source menus, using their MergeOrder and MergeType properties.

If the other menu item's MergeType property is set to Add, both menu items appear in the target menu (just as though both had specified Add).
If the other menu item's MergeType property is set to Remove, only this menu item appears in the target menu (again, the same as specifying Add for this menu item).
If the other menu item's MergeType property is set to Replace, only the child form's menu item is displayed, regardless of which one is set to MergeItems and which one is set to Replace. (This seems like inconsistent behavior and may be a bug.)

Remove
The menu item isn't shown in the target menu, regardless of the setting of the other menu item's MergeType property.

Replace
If the other menu item's MergeType property is set to Add, both menu items appear in the target menu (just as though both had specified Add). If the other menu item's MergeType property is set to MergeItems or Replace, only the child form's menu item is shown. (This seems like inconsistent behavior and may be a bug.)
If the other menu item's MergeType property is also set to Replace, only the child form's menu item is shown.

Detecting MDI Child Window Activation
Code in the MDI parent form class can be notified when an MDI child form becomes active inside an MDI parent form. ("Active" means that the child form receives the input focus after another MDI child form or the MDI parent form had the input focus.) To receive such notification, the MDI parent form must override the OnMdiChildActivate method (defined in the Form class). For example:
CODES:
It is important to call the base-class implementation of OnMdiChildActivate within the overriding function, so that any necessary base-class processing (including raising of the MdiChildActivate event) can occur. The e parameter carries no information. To find out which MDI child form became active, read the ActiveMdiChild property of the MDI parent form. This property is of type Form. Convert it to the MDI child form's type to gain access to any public members that are specific to that type. For example:
CODES:
To have code outside of the MDI parent form class notified when an MDI child form becomes active, write a handler for the MDI parent form's MdiChildActivate event. This event is defined in the Form class as:
CODES:
The sender parameter is the MDI parent form, not the MDI child form that has been activated. The parameter does not contain any additional information about the event. As when overriding the OnMdiChildActivate method, read the MDI parent form's ActiveMdiChild property to discover which MDI child form has been activated.

Component Attributes
The System.ComponentModel namespace defines several attributes for use in component, control, and form declarations. These attributes don't affect component behavior. Rather, they provide information that is used or displayed by the Visual Studio .NET IDE. The following is a description of each attribute:

AmbientValueAttribute
For ambient properties, specifies the property value that will mean "get this property's actual value from wherever ambient values come from for this property." Ambient properties are properties able to get their values from another source. For example, the BackColor property of a Label control can be set either to a specific Color value or to the special value Color.Empty, which causes the Label's background color to be the same as the background color of the form on which it is placed.

Putting this attribute on a property definition isn't what causes the property to behave as an ambient property: the control itself must be written such that when the special value is written to the property, the control gets the actual value from the appropriate location. This attribute simply provides the Windows Forms Designer with a way to discover what the special value is. When specifying this attribute, pass the special value to the attribute's constructor. For     example, <AmbientValue(0)>.

BindableAttribute
Indicates whether a property is typically usable for data binding. Specify <Bindable(True)> to indicate that the property can be used for data binding or <Bindable(False)> to indicate that the property typically is not used for data binding. This attribute affects how a property is displayed in the IDE, but does not affect whether a property can be bound at runtime. By default, properties are considered not bindable.

BrowsableAttribute
Indicates whether a property should be viewable in the IDE's Properties window. Specify <Browsable(True)> to indicate that the property should appear in the Properties window or <Browsable(False)> to indicate that it should not. By default, properties are considered browsable.

CategoryAttribute
Indicates the category to which a property or event belongs ("Appearance," "Behavior," etc.). The IDE uses this attribute to sort the properties and events in the Properties window. Specify the category name as a string argument to the attribute. For example, <Category("Appearance")>. The argument can be any string. If it is not one of the standard strings, the Properties window will add a new group for it. The standard strings are:

          Action
Used for events that indicate a user action, such as the Click event.
        Appearance
Used for properties that affect the appearance of a component, such as the BackColor property.
         Behavior
Used for properties that affect the behavior of a component, such as the AllowDrop property.
         Data
Used for properties that relate to data, such as the DecimalPlaces property of the NumericUpDown control.
         Design
Used for properties that relate to the design-time appearance or behavior of a component.
         DragDrop
Used for properties and events that relate to drag and drop. No Windows Forms components have any properties or events marked with this category.
         Focus
Used for properties and events that relate to input focus, such as the CanFocus property and the GotFocus event.
          Format
Used for properties and events that relate to formats. No Windows Forms components have any properties or events marked with this category.
         Key
Used for events that relate to keyboard input, such as the KeyPress event.
         Layout
Used for properties and events that relate to the visual layout of a component, such as the Height property and the Resize event.
          Mouse
Used for events that relate to mouse input, such as the MouseMove event.
          WindowStyle
Used for properties and events that relate to the window style of top-level forms. No Windows
Forms components have any properties or events marked with this category.
If no CategoryAttribute is specified, the property is considered to have a category of
Misc.

DefaultEventAttribute
Indicates the name of the event that is to be considered the default event of a component. For example, <DefaultEvent("Click")>. When a component is double-clicked in the Windows Forms Designer, the designer switches to code view and displays the event handler for the default event. This attribute can be used only on class declarations.

DefaultPropertyAttribute
Indicates the name of the property that is to be considered the default property of a component. For example, <DefaultProperty("Text")>. This attribute can be used only on class declarations. The default property is the property that the Windows Forms Designer highlights in the Properties window when a component is clicked in design view.

DefaultValueAttribute
Indicates the default value of a property. For example, <DefaultValue(0)>. If the IDE is used to set a property value to something other than the default value, the code generator will generate the appropriate assignment statement in code. However, if the IDE is used to set a property to the default value, no assignment statement is generated.

DescriptionAttribute
Provides a description for the code element. For example, <Description("The text
contained in the control.")>. The IDE uses this description in tool tips and IntelliSense.

DesignerAttribute
Identifies the class that acts as the designer for a component. For example, <Designer("MyNamespace.MyClass")>. The designer class must implement the IDesigner interface. This attribute can be used only on class declarations and is needed only if the built-in designer isn't sufficient. Creating custom designers is not discussed in this site

DesignerCategoryAttribute
Used with custom designers to specify the category to which the class designer belongs.

DesignerSerializationVisibilityAttribute
Used with custom designers to specify how a property on a component is saved by the designer.

DesignOnlyAttribute
Indicates when a property can be set. Specify <DesignOnly(True)> to indicate that the property can be set at design time only or <DesignOnly(False)> to indicate that the property can be set at both design time and runtime (the default).

EditorAttribute
Identifies the "editor" to use in the IDE to allow the user to set the values of properties that have the type on which the EditorAttribute attribute appears. In this way, a component can declare new types and can declare properties having those types, yet still allow the user to set the values of the properties at design time. Creating custom type editors is not discussed in this site.

EditorBrowsableAttribute
Indicates whether a property is viewable in an editor. The argument to the attribute's constructor must be one of the values defined by the EditorBrowsableState enumeration (defined in the System.ComponentModel namespace). The values of this enumeration are:

            Advanced
Only advanced users should see the property. It is up to the editor to determine when it's appropriate to display advanced properties.
           Always
The property should always be visible within the editor.
          Never
The property should never be shown within the editor.

ImmutableObjectAttribute
Indicates whether a type declaration defines a state that can change after an object of that type is constructed. Specify <ImmutableObject(True)> to indicate that an object of the given type is immutable. Specify <ImmutableObject(False)> to indicate that an object of the given type is not immutable. The IDE uses this information to determine whether to render a property as read-only.

InheritanceAttribute
Used to document an inheritance hierarchy that can be read using the IInheritanceService interface. This facility is not discussed in this site.

InstallerTypeAttribute
Used on type declarations to specify the installer for the type. Installers are not discussed in this  site.

LicenseProviderAttribute
Indicates the license provider for a component.

ListBindableAttribute
Indicates whether a property can be used as a data source. (A data source is any object that exposes the IList interface.) Specify <ListBindable(True)> to indicate that the property can be used as a data source. Specify <ListBindable(False)> to indicate that the property can't be used as a data source.

LocalizableAttribute
Indicates whether a property's value should be localized when the application is localized. Specify <Localizable(True)> to indicate that the property's value should be localized. Specify <Localizable(False)> or omit the LocalizableAttribute attribute to indicate that the property's value should not be localized. The values of properties declared with <Localizable(True)> are stored in a resource file, which can be localized.

MergablePropertyAttribute
Indicates where property attributes can be merged. By default, when two or more components are selected in the Windows Forms Designer, the Properties window typically shows the properties that are common to all of the selected components. If the user changes a value in the Properties window, the value is changed for that property in all of the selected components. Placing <MergableProperty(False)> on a property declaration changes this behavior. Any property declared in this way is omitted from the Properties window when two or more components are selected. Specifying <MergableProperty(True)> is the same as omitting
the attribute altogether.

NotifyParentPropertyAttribute
Indicates whether the display of a property's parent property should be refreshed when the given property changes its value.

ParenthesizePropertyNameAttribute
Indicates whether the property name should be parenthesized in the Properties window. Specify <ParenthesizePropertyName(True)> to indicate that the property name should appear within parentheses. Specify <ParenthesizePropertyName(False)> to indicate that the property name should not appear within parentheses. Omitting the attribute is the same as specifying <ParenthesizePropertyName(False)>. The only benefit to parenthesizing property names in the property window is that they are sorted to the top of the list. Microsoft has no specific recommendations for when to parenthesize a property name.

PropertyTabAttribute
Specifies a type that implements a custom property tab (or tabs) for a component. This facility is not discussed in this  site.

ProvidePropertyAttribute
Used with extender providers—i.e., classes that provide properties for other objects. Extender providers are not discussed in this  site.

ReadOnlyAttribute
Indicates whether a property is read-only at design time. Specify <ReadOnly(True)> to
indicate that the property is read-only at design time. Specify <ReadOnly(False)> to
indicate that the property's ability to be modified at design time is determined by whether a Set
method is defined for the property. Omitting the attribute is the same as specifying
<ReadOnly(False)>.

RecommendedAsConfigurableAttribute
Indicates whether a property is configurable. Configurable properties aren't discussed in this site.

RefreshPropertiesAttribute
Determines how the Properties window is refreshed when the value of the given property changes.

RunInstallerAttribute
Indicates whether an installer should be invoked during installation of the assembly that contains the associated class. This attribute can be used only on class declarations, and the associated class must inherit from the Installer class (defined in the System.Configuration.Install namespace). Installers are not discussed in this site.

ToolboxItemAttribute
Specifies a type that implements a toolbox item related to the declaration on which the attribute is placed. This facility is not discussed in this site.

ToolboxItemFilterAttribute
Specifies a filter string for a toolbox-item filter. This facility is not discussed in this site.

TypeConverterAttribute
Indicates the type converter to be used with the associated item. Type converters are not discussed in this site.

2-D Graphics Programming with GDI+
The Windows operating system has always included support for drawing two-dimensional graphics. This support is known as the Graphics Device Interface (GDI) library. The GDI library is now easier to use and provides additional features. The new capabilities are known collectively as GDI+. GDI+ features are exposed in the .NET Framework through classes in the System.Drawing, System.Drawing.Drawing2D, System.Drawing.Imaging, and System.Drawing.Text namespaces. This section discusses some of those capabilities.

The Graphics Class
Objects of type Graphics (defined in the System.Drawing namespace) represent two-dimensional surfaces on which to draw. A Graphics object must be obtained before any drawing can be done. A common way to obtain a Graphics object is to override the OnPaint method of a form or user control, as shown in the following code fragment:
CODES:
The single argument passed to the OnPaint method, e, is of type PaintEventArgs. This class has a property called Graphics, which holds a reference to the Graphics object to be used for drawing on the user control or form. The PaintEventArgs class is defined in the System.Windows.Forms namespace. It has two properties:

ClipRectangle
Defines the area that needs to be drawn. Drawing done outside the limits of the clip rectangle will not be displayed. The coordinates of the rectangle are relative to the client rectangle of the user control or form.
The syntax of the ClipRectangle property is:
CODES:
Graphics
Defines the graphics surface on which to draw. The syntax of the Graphics property is:
CODES:
The following list shows some of the Graphics class's many methods that are available for drawing various lines and shapes, and Example 5-7 in Chapter 5 gives an example of drawing a filled ellipse. This list is just to get you started; it is beyond the scope of this book to document the syntax of each of these methods.

DrawArc
Draws an arc (that is, a portion of an ellipse).

DrawBezier
Draws a Bezier curve.

DrawBeziers
Draws a series of Bezier curves.

DrawClosedCurve
Is the same as the DrawCurve method (see the next item in this list), except that the last point in the curve is connected back to the first point.

DrawCurve
Draws a smooth, curved figure that passes through a given array of points.

DrawEllipse
Draws an ellipse.

DrawIcon
Draws an icon. Icons are represented by objects of type Icon (defined in the System.Drawing namespace). The Icon class defines various methods for loading icons.

DrawIconUnstretched
Is the same as the DrawIcon method, but does not stretch the icon to fit the clipping rectangle.

DrawImage
Draws an image. Images are represented by objects of type Image (defined in the
System.Drawing namespace). The Image class defines various methods for loading images in
standard formats, such as bitmaps and JPEGs.
DrawImageUnscaled
Is the same as DrawImage, except that the DrawImageUnscaled method ignores any width
and height parameters passed to it.

DrawLine
Draws a line.

DrawLines
Draws a series of lines.

DrawPath
Draws a series of lines and curves that are defined by a GraphicsPath object. The GraphicsPath class is beyond the scope of this book.

DrawPie
Draws a pie section.

DrawPolygon
Draws lines to connect a series of points.

DrawRectangle
Draws a rectangle.

DrawRectangles
Draws a series of rectangles.

DrawString
Draws text.
FillClosedCurve
Draws a filled, closed curve.

FillEllipse
Draws a filled ellipse.

FillPath
Draws a filled figure whose shape is given by a GraphicsPath object. The GraphicsPath class is beyond the scope of this book.

FillPie
Draws a filled pie section.

FillPolygon
Draws a filled polygon (see the DrawPolygon method earlier in this list).

FillRectangle
Draws a filled rectangle.

FillRectangles
Draws a series of filled rectangles.

FillRegion
Draws a filled figure whose shape is given by a Region object.

The Pen Class
Pen objects hold the settings used when drawing lines. All of the Graphics class's Draw...methods (DrawArc, DrawBezier, etc.) require that the caller supply a Pen object. The supplied Pen object determines the properties of the line used for drawing (for example, its color, width, etc.). 

Example 4-9. Drawing an ellipse on a form
CODES:
Figure 10. The ellipse drawn by the code in Example 4-9

Figure 10
CODES:
By convention, objects that allocate scarce resources expose a Dispose method to allow the object client to tell the object to release its resources. When using any object that exposes a Dispose method (as the Pen object does), the Dispose method must be called when the client code is finished using the object. If the Dispose method isn't called (or if it isn't implemented), resources will be held longer than necessary, which may in turn result in resources being unavailable for other code that needs them.

The .NET Framework provides a number of predefined pens through the properties of the Pens and SystemPens classes (defined in the System.Drawing namespace). For example, the Blue property of the Pens class returns a Pen object whose color is set to Color.Blue. Thus, the following line of code draws a blue ellipse:
CODES:
Similarly, the SystemPens class's WindowText property returns a Pen object whose color is set to the system's window text color. Using the standard pens provided by the Pens and SystemPens classes can be more efficient than instantiating new Pen objects. However, their properties (such as line width) cannot be altered.

Example 4-10. Setting Pen properties
CODES:

Figure 11
The Width property is a value of type Single that determines the width of lines drawn with this pen. The default is 1. The unit of measurement is determined by the PageUnit property of the Graphics object in which the lines are drawn. The PageUnit property is of the enumeration type GraphicsUnit (defined in the System.Drawing namespace). The values of GraphicsUnit that are appropriate for assignment to the PageUnit property are:

Display
Units are specified in 1/75 of an inch.
Document
Units are specified in 1/300 of an inch.
Inch
Units are specified in inches.
Millimeter
Units are specified in millimeters.
Pixel
Units are specified in pixels.
Point
Units are specified in points (1/72 of an inch).
The DashStyle property of the Pen object determines the whether the line is solid or dashed, as well as the style of the dash. The DashStyle property is of the enumeration type DashStyle (defined in the System.Drawing.Drawing2D namespace), which defines the following values:
Custom
Specifies a programmer-defined dash style. If this value is used, other properties of the Pen object control the exact appearance of the dashes in the line. Creating custom dash styles is not discussed in this book.
Dash
Specifies a dashed line.
DashDot
Specifies a line consisting of alternating dashes and dots.
DashDotDot
Specifies a line consisting of alternating dashes and two dots.
Dot
Specifies a dotted line.
Solid
Specifies a solid line.
The standard dash styles are shown in Figure 12

Figure 12
The Brush Class
Brush objects hold the settings used when filling graphics areas. All of the Graphics class's Fill...methods (FillClosedCurve, FillEllipse, etc.) require that the caller supply a Brush object. The supplied Brush object determines how the interior of the figure will be painted. Example 4-11 shows an OnPaint method that can be used to draw an ellipse on a user control or a form. It is similar to Example 4-9, but draws a filled ellipse rather than an outline. The lines that differ from Example 4-9 are shown in bold. The resulting display is shown in Figure 13.

Example 4-11. Drawing a filled ellipse on a form
CODES:
Figure 13



No comments:

Post a Comment