Sie sind auf Seite 1von 60

Multiple Document Interfaces (MDI) and Graphics (GDI+)

Visual Basic .NET Professional Skills Development 22-1


Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Multiple Document
Interfaces (MDI)
and Graphics
(GDI+)
Objectives
Create MDI Windows applications.
Manage menus in MDI forms.
Use Toolbar and StatusBar controls, and ImageList components.
Take control of the display of combo box lists and menus.
Draw shapes on Windows forms.
Create transparency effects and non-rectangular forms.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-2 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Creating MDI Applications
Since the earliest versions of Windows, Microsoft has supported a style of
application that is referred to as Multiple Document Interface, or MDI. In an
MDI application, there is one parent window that creates a shell
environment for the application, with other child windows contained within it.
The child windows in an MDI application always have certain characteristics:
They never move outside the borders of the parent window.
They are resizable.
When one child is maximized, all child windows are maximized.
When maximized, the title of the active child window is appended to
the title of the parent.
When minimized, child windows are displayed as icons within the
parent window.
Any menus in the child windows appear as menus of the parent, when
that child is active.
MDI Settings in .NET Windows Forms
Windows Forms provide very flexible and full-featured support for building
MDI applications. You can designate any form as an MDI parent, simply by
setting its IsMdiContainer property to True. To make a form into an MDI
child, you set its MdiParent property to point to an MDI container form.
These properties can even be modified at runtime, and you can designate more
than one form in an application as an MDI container.
Try It Out!
Follow these steps to see how you can turn .NET Windows forms into an MDI
parent and child forms by setting their IsMdiContainer and MdiParent
properties.
1. Open the sample application in Visual Studio by double-clicking
MDI-GDI+.sln in the sample folder named MDI-GDI+. Press F5 to
run the application and bring up the switchboard form named
frmMain.
Creating MDI Applications
Visual Basic .NET Professional Skills Development 22-3
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

2. Click the MDI Settings button on the switchboard form. This opens
three sample forms named Form1, Form2 and Form3. Each of these
forms has a check box that enables you to set the forms
IsMdiContainer property and a combo box to set the MdiParent
property. The combo box is enabled only when the check box is
checked.
3. Click the IsMdiContainer check box in Form1 and Form3. Youll
see the appearance of these forms change. Notice that these MDI
parent forms still contain their controls, but the backcolor of the form
has changed to the Windows system color named AppWorkspace,
which is a dark gray color in the standard color scheme.
4. Use the combo box in Form2 to set its MdiParent property to Form1.
Form2 becomes an MDI child form, contained inside Form1, as shown
in Figure 1. (Youll need to resize Form1 to see Form2.)

Figure 1. MDI properties can be set dynamically at runtime.
5. Maximize Form2, and the title of Form1 changes to
Form1 [Form2].
6. Restore Form2 and set its MdiParent property to Form3. The form
moves to become a child of Form3.
7. Clear the IsMdiContainer check box on Form3 and on Form1. All
three forms return to being non-MDI forms. You can close the forms
individually or you can close the switchboard form and the others will
close automatically.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-4 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

WARNING! Although it is interesting and instructive to play with changing the
MDI properties of forms during runtime, it is not something you
normally would (or should) do in a production application. In fact,
you will find that if you experiment with the sample forms, it isnt
hard to find combinations of steps that produce errors, or that leave a
form in a state where it cant be closed without closing the startup
switchboard form. We recommend that you set the IsMdiContainer
property when you design your applications. If you find a need to
change this property at runtime, be sure to test the application
thoroughly.
Additional MDI Form Properties
In addition to the IsMdiContainer and MdiParent properties, there are two
other useful properties that relate to a forms status in an MDI application.
The IsMdiChild property is a read-only Boolean property that provides an
alternative to checking a forms MdiParent property. This is useful if you just
need to determine whether the form is an MDI child or not.
For example, the event handler for the CheckedChanged event of
chkIsMdiContainer in the sample forms determines whether the form is an
MDI child before attempting to make it into an MDI parent. If the form is an
MDI child, then it sets the MdiParent property to Nothing before moving on to
setting the IsMdiContainer property to True:
If chkIsMdiContainer.Checked Then
If Me.IsMdiChild Then
' You must make the form
' a top-level form,
' before making it a parent.
Me.MdiParent = Nothing
End If

So what is the difference between checking the value of IsMdiChild and
simply checking whether the forms MdiParent property is set to Nothing?
There is no difference. The read-only IsMdiChild property is added for your
convenience, and perhaps to provide some backwards compatibility with
Visual Basic 6 applications, where forms had a Boolean MDIChild property
and applications could only have one MDI parent form.
When the CheckBox is cleared, the code uses the MdiChildren property to
find all the forms contained by that MDI parent form. The code sets the
See
chkI sMdiContainer
_CheckedChanged
in Form1
Creating MDI Applications
Visual Basic .NET Professional Skills Development 22-5
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

MdiParent property of these child forms to Nothing before setting its own
IsMdiContainer property to False:

Else ' the check box was cleared
Dim frm As Form
For Each frm In Me.MdiChildren
frm.MdiParent = Nothing
Next
Me.IsMdiContainer = False
Me.lblMdiParent.Enabled = True
Me.cboMdiParent.Enabled = True
End If
End Sub

Creating Multiple Document Windows
The sample application includes a pair of forms named frmMDIParent and
frmMDIChild. These forms comprise a simple MDI application that allows
you to create and display multiple child windows. Each child window, based
on the frmMDIChild form, contains a RichTextBox control, and you can
individually format the text in each window.
To test this example, click the MDI Sample button on the switchboard to open
the parent form with one child window. Choose File|New to create a second
child window, as shown in Figure 2. Use the Format menu to control the font
in each window. Note that the name of the active document is displayed in the
status bar control that is docked to the bottom of the parent form.

Figure 2. Creating multiple child documents.
See frmMDI Parent
and frmMDI Child
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-6 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

The AddNewDocument method of frmMDIParent is called from the
mnuFileNew.Click event handler. This code shows how multiple form
instances can be created from one form class. A counter is incremented to give
each form a unique document number, which appears in the title bar and as the
initial text in the RichTextBox control. The MdiParent property is set for each
form before the form is displayed.

Private Sub AddNewDocument()
mintChildCount += 1
'Create the form
Dim frmNew As New frmMDIChild( _
"Document " & mintChildCount.ToString())
frmNew.MdiParent = Me
frmNew.Show()
End Sub

The frmMDIChild forms code contains an overloaded constructor that takes a
single string parameter and displays that string in the title bar and in the
RichTextBox control.

Public Sub New(ByVal name As String)
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
Me.Text = name
RichTextBox1.Text = name

The code in the overloaded constructor also initializes the font used in the
RichTextBox control to be the one currently selected in the forms Format
menu. That font is stored in a private variable named currentFontFamily and
its size is stored in a variable named sngFontSize.

RichTextBox1.Font = _
New Font(currentFontFamily, sngFontSize)

Finally, the constructor calls a subroutine that dynamically creates the Format
menu items for the form and hooks up their event handlers.
See
AddNewDocument
in frmMDI Parent
See frmMDI Child
Creating MDI Applications
Visual Basic .NET Professional Skills Development 22-7
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Call AddFormatMenus()
End Sub

Detecting Which Child is Active
In the sample application, the status bar shows the text assigned to the active
child form. To accomplish this, the application handles a special event that is
raised only in MDI parent forms, the MdiChildActivate event.
In the MdiChildActivate event handler (or in any code in the parent form) you
can detect which child form currently has the focus by checking the value of
the parent forms ActiveMdiChild property.

Private Sub frmMDIParent_MdiChildActivate( _
ByVal sender As Object, ByVal e As System.EventArgs) _
Handles MyBase.MdiChildActivate
If (Me.ActiveMdiChild Is Nothing) Then
StatusBar1.Text = ""
Else
StatusBar1.Text = Me.ActiveMdiChild.Text
End If
End Sub

Managing Menus in MDI Applications
One tricky aspect of developing an MDI application is planning and
controlling the behavior of menus. There will usually be some menu items that
are global to the application and that therefore belong in the parent container
form. Each child form, however, may have its own menus, which can replace
or merge with those of the parent container.
MergeOrder
You set the MergeOrder property of a menu item to determine its position in
relation to other menu items. For example, frmMDIParent has three top-level
menu items with the MergeOrder settings shown in Table 1.
See
frmMDI Parent_
MdiChildActivate
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-8 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

frmMDIParent Menus MergeOrder
File 0
Edit 1
Window 10
Table 1. MergeOrder settings for the top-level menu items in frmMDIParent.
The MergeOrder settings determine the order in which these menu items will
appear on the menu bar of the form. If you change the MergeOrder value of
Edit to 11, it will appear after the Window menu.
The frmMDIChild form also contains a menu. Its menu items have the
MergeOrder values shown in Table 2.
frmMDIChild Menus MergeOrder
File 0
Format 5
Table 2. MergeOrder settings for the top-level menu items in frmMDIChild.
When you display these forms, you only see menus in the parent form. MDI
child forms never display menus. However, the menus from frmMDIChild
arent simply eliminated. They appear in the parent form. The Format menu
appears between the Edit and Window menus, because its MergeOrder value
(5) is between the values assigned to Edit (1) and Window (10).
MergeType
In the example application, the File menus in the parent and child forms have
the same name and the same MergeOrder value. What determines the behavior
when these menus are merged at runtime? This behavior is determined by the
MergeType property, which provides several useful options.
Figure 3 shows the MergeType property values that are available and Table 3
describes the effect of each one.
Creating MDI Applications
Visual Basic .NET Professional Skills Development 22-9
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Figure 3. Setting the MergeType property.
MergeType Setting Effect
Add Add the child menu item to the parents menu, along with the
parent item that has the same MergeOrder value.
Replace Replace the parent menu item with the one in the child form.
MergeItems Blend the sub-items from the parent and child menus.
Remove Use the parent menu item and ignore the one defined in the
child.
Table 3. The MergeType property determines how menu conflicts between parent
and child MDI forms are handled.
Because the mnuFile MenuItem in frmMDIChild has its MergeType set to
MergeItems, the sub-items under this File menu are merged with the sub-items
of the File menu in frmMDIParent. The order in which the sub-items appear in
the merged File menu is determined by the MergeOrder values of the sub-
items.
The mnuFile menu item in frmMDIChild has one sub-item, mnuFileSave,
which has a MergeOrder of 105. The mnuFile menu item in frmMDIParent has
three sub-items, mnuFileNew (100), mnuFileSep1 (108), and mnuFileExit
(110). Because of these settings, the Save item (105) appears between the New
item (100) and the separator line (108) in the merged menu that is shown in
Figure 4.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-10 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Figure 4. The Save item, with a MergeOrder of 105 is displayed between New
(100) and the separator (108) in the merged File menu.
Listing and Arranging MDI Child Windows
To have a menu automatically list all the child windows in an MDI
application, all you need to do is set the MdiList property of that menu item to
True. The list is automatically maintained and the item corresponding to the
active window is automatically checked. Selecting an item from this menu list
makes the selected window become active. In the sample application, the
MdiList property of mnuWindow is set to True. A list of child windows is
shown in Figure 5.
Calling the LayoutMdi method of an MDI parent form enables you to
reposition the child windows into a neatly-arranged cascading or tiled pattern.
You pass the appropriate member of the MdiLayout enumeration to the
method call. For example, here is code that horizontally tiles the child
windows, as shown in Figure 5.

Private Sub mnuWindowHorizontal_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles mnuWindowHorizontal.Click
Me.LayoutMdi(MdiLayout.TileHorizontal)
End Sub

See frmMDI Parent
Creating MDI Applications
Visual Basic .NET Professional Skills Development 22-11
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Figure 5. List child windows by setting a menu items MdiList property, and
arrange the windows by calling the forms LayoutMdi method.
Editing Documents Using the Clipboard
The sample MDI application also shows you two different ways to
cut/copy/paste values to or from the Windows Clipboard. The .NET
Framework includes a Clipboard object with methods you can call, but the
RichTextBox and TextBox controls also have their own built-in methods for
working with the Clipboard. You can even use methods of the controls to
Undo or Redo successive user actions.
You can experiment with these capabilities by testing the menu commands
under the Edit menu in the sample form, as shown in Figure 6. Note that these
commands have also been assigned the standard Windows shortcuts.

Figure 6. Adding standard editing functionality.
The mnuEditCopy.Click event handler sets references to the active child form
and its RichTextBox control. The code passes the value of the SelectedText
property of the RichTextBox to the SetDataObject method of the Clipboard
See mnuEditCopy_
Click
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-12 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

object. A comment in the code shows how you can accomplish the same effect
by calling the Copy method of the control.

' Get the active child form.
Dim frmActive As Form = Me.ActiveMdiChild

' Get the active RichTextBox control
' on the active form, if there is one.
If (Not frmActive Is Nothing) Then
Try
Dim rtb As RichTextBox = _
CType(frmActive.ActiveControl, RichTextBox)
If (Not rtb Is Nothing) Then
Clipboard.SetDataObject(rtb.SelectedText)
' You can also use this:
'rtb.Copy()
End If
Catch
MessageBox.Show("No text is selected.")
End Try
End If

The code for pasting calls the Clipboards GetDataObject method, and it also
checks the data to ensure that it is in text format. This is a useful validation to
perform, because the Clipboard can hold many different types of data. A
comment in the code shows how this can also be done by calling methods of
the RichTextBox control.
See mnuEditPaste_
Click
Creating MDI Applications
Visual Basic .NET Professional Skills Development 22-13
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Private Sub mnuEditPaste_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles mnuEditPaste.Click
' Get the active child form.
Dim frmActive As Form = Me.ActiveMdiChild
' Get the active RichTextBox control
' on the active form, if there is one.
If (Not frmActive Is Nothing) Then
Try
Dim rtb As RichTextBox = _
CType(frmActive.ActiveControl, RichTextBox)
If (Not rtb Is Nothing) Then
' Get the clipboard data.
Dim ido As IDataObject = Clipboard.GetDataObject()
' If the data is text, then paste.
If (ido.GetDataPresent(DataFormats.Text)) Then
rtb.SelectedText = _
ido.GetData(DataFormats.Text).ToString()
End If

' This can also be done using methods
' of the RichTextBox control:
'If rtb.CanPaste( _
' DataFormats.GetFormat(DataFormats.Text)) Then
' rtb.Paste()
'End If
End If
Catch
MessageBox.Show("No insertion point is selected.")
End Try
End If
End Sub

Implementing Undo and Redo is very easy. You only need to call the
appropriate method of the control.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-14 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Dim rtb As RichTextBox = _
CType(frmActive.ActiveControl, RichTextBox)
If (Not rtb Is Nothing) Then
rtb.Undo()
End If


Dim rtb As RichTextBox = _
CType(frmActive.ActiveControl, RichTextBox)
If (Not rtb Is Nothing) Then
rtb.Redo()
End If

The Scribble Application
Visual Basic .NET Professional Skills Development 22-15
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

The Scribble Application
The Scribble application is an interesting MDI example provided by Microsoft.
You can find it in the following directory, which includes several other useful
samples:
\Program Files\Microsoft Visual Studio .NET\Vb7\VB Samples\
A copy of Scribble is included in the MDI-GDI+.sln sample solution for this
chapter. To test it, set Scribble as the startup project for the solution. When you
run the application, the mouse cursor acts as a pen, and you can scribble on
the child forms, as shown in Figure 7.

Figure 7. Running the Scribble application.
The ToolBar Control
The Scribble.vb MDI parent form includes a ToolBar control. Select the
ToolBar control on the form and bring up the Properties window. Click on the
ellipsis next to the Buttons property. This loads the ToolBarButton Collection
Editor, as shown in Figure 8.
See Scribble
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-16 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Figure 8. Adding Toolbar buttons and setting their properties, using the
ToolBarButton Collection Editor.
The ToolBarButton Collection Editor dialog box makes it very easy to add or
remove buttons, to change their order of appearance in the ToolBar, and to set
their properties.
The ImageIndex property of each button is used to select the image to be
displayed for each button. The images shown in the drop-down list for this
property are the images contained in the Images collection of the ImageList1
component of the form, shown in Figure 9.
The Scribble Application
Visual Basic .NET Professional Skills Development 22-17
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Figure 9. ImageList1 is a component containing a collection of images that can be
used by controls on the form. The ToolBar button images are contained in this
collection.
The ImageList Component
The ImageList component provides an efficient way of storing and working
with a collection of images to be used by controls on the form. In the Scribble
sample form, the images displayed on the ToolBar buttons are taken from the
collection of images stored in the ImageList1 component.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-18 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

To add, remove, or reorder images, bring up the Image Collection Editor
dialog box, shown in Figure 10, by clicking the ellipsis next to the Images
property of the component.

Figure 10. Adding, removing, or reordering images of the ImageList control with
the Image Collection Editor.
Responding to ToolBar ButtonClick Events
The ToolBar control has a single ButtonClick event that fires when any of the
buttons on the ToolBar are clicked. The ToolBarButtonClickEventArgs object
has a Button property that allows you to determine which button was clicked:
The Scribble Application
Visual Basic .NET Professional Skills Development 22-19
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Private Sub ToolBar1_ButtonClick( _
ByVal Sender As System.Object, ByVal e As _
System.Windows.Forms.ToolBarButtonClickEventArgs) _
Handles toolBar1.ButtonClick
If e.Button Is NewButton Then
NewDoc()
ElseIf e.Button Is OpenButton Then
Open()
ElseIf e.Button Is SaveButton Then
Save()
ElseIf e.Button Is PreviewButton Then
PrintPreview()
ElseIf e.Button Is PrintButton Then
Print()
ElseIf e.Button Is HelpButton1 Then
ShowHelpTopics()
End If
End Sub

Displaying Help
The Scribble.vb form also includes a HelpProvider component. This
component is similar to the ToolTip component in the way that it adds
properties to the controls on the form for providing context-sensitive Help
when the F1 key is pressed.
Figure 11 shows the HelpNamespace property of the HelpProvider1
component, which specifies the location of the scribble.chm Help file.

Figure 11. The HelpProvider properties window.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-20 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

If the user selects Help from the menu, then the following code runs:

Private Sub ShowHelpTopics()
Help.ShowHelp(Me, "..\help\scribble.chm")
End Sub

ShowHelp is a shared method of the Help class that allows you to specify a
parent control and the URL to a Help file. In this case the parent control is the
form itself.
How the Scribble Application Works
Without going through all the details of exactly how the Scribble application
works, it is instructive to look at some of the major architectural strategies
behind the application
The Stroke and ScribbleDoc Classes
The ScribbleDoc.vb file contains code that defines two classes in the Scribble
Namespace, Stroke, and ScribbleDoc.
The Stroke class holds an ArrayList of points, and the ScribbleDoc class holds
an ArrayList of Stroke objects.
The ScribbleView Form
As you draw on an instance of the ScribbleView form, the mouse event
handlers of the form add points to a Stroke object and add Stroke objects to a
ScribbleDoc object for that form.
In the MouseDown event a new Stroke object is created. In the MouseMove
event, points are added to the Stroke object, and in the MouseUp event the
completed Stroke object is added to the ScribbleDoc object for that form.
In the Paint event, graphics objects and methods are used to draw shapes based
on the points that were collected in the mouse events.
Saving and Loading Scribbles
One of the most interesting aspects of the Scribble application is the way that it
saves drawings to disk and reloads them. The drawings are not stored as
bitmaps. Instead, the application uses serialization to save a binary
representation of the array of Stroke objects that comprise each drawing.
The Scribble Application
Visual Basic .NET Professional Skills Development 22-21
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

The Stroke class is marked with an attribute that identifies it as serializable:

<Serializable()> Public Class Stroke

This serializable attribute is a powerful .NET feature. It allows you to save
binary representations of objects with very little work.
The SaveDocument method of the ScribbleDoc class, which is defined in
ScribbleDoc.vb, runs after the user has specified a file name for saving the
drawing. The SaveDocument code only requires four lines of code to save a
scribble to disk.
The code creates a FileStream object, using the FileName selected by the
user, and specifies a FileMode of Create, indicating that a new file will be
created. The code then creates a BinaryFormatter object and calls its
Serialize method, passing in the FileStream and an ArrayList of Stroke
objects. The .NET Framework does all the hard work of serializing the stroke
objects and saving them to the FilesStream, which the code then closes.

Public Sub SaveDocument(ByVal FileName As String)
Try
Dim FileStream As Stream = _
File.Open(FileName, FileMode.Create)
Dim FileFormatter As New BinaryFormatter()
FileFormatter.Serialize(FileStream, StrokeList)
FileStream.Close()
IsDirty = False
Catch Ex As Exception
MessageBox.Show(Ex.ToString())
End Try
End Sub

Loading a scribble by deserializing a saved array of Stroke objects is just as
easy. This time the code creates a FileStream with a FileMode of Open. The
code again uses a BinaryFormatter object, calling its Deserialize method to
convert the FileStream back into an ArrayList of Stroke objects.
See ScribbleDoc.
SaveDocument in
ScribbleDoc.vb
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-22 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Public Sub OpenDocument(ByVal FileName As String)
Try
Dim FileStream As Stream = _
File.Open(FileName, FileMode.Open)
Dim FileFormatter As New BinaryFormatter()
StrokeList = CType( _
FileFormatter.Deserialize(FileStream), ArrayList)
FileStream.Close()
Catch Ex As Exception
MessageBox.Show(Ex.ToString())
End Try
End Sub

Drawing the Scribbles
The code that actually draws the scribble shapes on the forms is very simple. It
cycles through the ArrayList of points stored in a Stroke object and uses a Pen
object to draw lines between each successive pair of points. This method is
called from the Paint event handler of a ScribbleView form, which passes a
Graphics object to the DrawStroke method. Youll learn about the Graphics
and Pen objects used in this method in the next section of this chapter.

Public Sub DrawStroke(ByVal NewGraphic As Graphics)
Try
Dim MyPen As New Pen(Color.Black, PenWidth)
Dim i As Integer
For i = 1 To PointArray.Count - 1
NewGraphic.DrawLine( _
MyPen, CType(PointArray((i - 1)), Point), _
CType(PointArray(i), Point))
Next i
MyPen.Dispose()
Catch Ex As Exception
MessageBox.Show(Ex.ToString())
End Try
End Sub 'DrawStroke

See
Stroke.DrawStroke
in ScribbleDoc.vb
Drawing on Forms
Visual Basic .NET Professional Skills Development 22-23
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Drawing on Forms
The .NET Framework includes a set of namespaces under System.Drawing
that are devoted to graphics. The classes in these namespaces are object-
oriented wrappers around the graphics capabilities built into Windows. The
previous technology for calling Windows graphics functions was called the
graphics device interface (GDI). The new set of managed classes for creating
and manipulating graphics is called GDI+.
The MDI-GDI+ project includes a sample form that demonstrates several
techniques for drawing on Windows forms. If you have been playing with the
Scribble project in the sample solution, set the MDI-GDI+ project to be the
startup project. To open the sample form, named frmDrawing, run the project
and click the switchboard button labeled Drawing on Forms.
When you run frmDrawing, a dialog box pops up that lets you choose an
Object, Border, and Fill. When you click the Draw button at the bottom of the
form, the specified shape is drawn on the form, as shown in Figure 12.

Figure 12. Drawing on a form.
OwnerDraw Controls
One way to use GDI+ in Windows forms is to take control of drawing the
items in combo boxes, list boxes, and menus. When you do this, the controls
See frmDrawing
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-24 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

are referred to as OwnerDraw controlsyou are the owner and you are doing
the drawing yourself in code rather than leaving the task to the system. The
frmDrawing sample form contains several combo boxes for selecting colors
that are OwnerDraw controls. These combo boxes display a rectangle showing
the color for each item, as shown in Figure 13.

Figure 13. OwnerDraw controls, such as the combo boxes on this form for picking
colors, enable you to write code to define each item in the list.
DrawMode
To create an OwnerDraw combo box (or list box), you must set the
DrawMode property of the control to either OwnerDrawFixed or
OwnerDrawVariable. The default value for this property is Normal.
The difference between using a DrawMode setting of OwnerDrawFixed and
OwnerDrawVariable is whether or not you can vary the height of the items in
the list.
Events to Handle
To use either OwnerDrawFixed or OwnerDrawVariable as the DrawMode of a
control, you need to handle the controls DrawItem event. This event is raised
every time the control creates a new item for display and it provides you with a
DrawItemEventArgs object that you use to create the graphics for the current
item.
Drawing on Forms
Visual Basic .NET Professional Skills Development 22-25
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

To take advantage of the OwnerDrawVariable setting, you must also handle
the controls MeasureItem event, which is raised before DrawItem and
enables you to adjust the size of the item.
All the OwnerDraw combo boxes in frmDrawing have their DrawMode
property set to OwnerDrawFixed, so that form does not include any
MeasureItem event handler. However, later in the chapter youll see an
example of creating an OwnerDraw menu, which always requires you to
handle the MeasureItem event of the menu item. MenuItems do not have a
DrawMode propertyonly a Boolean OwnerDraw property.
Creating an OwnerDraw Color Combo Box
The first step in creating an OwnerDraw combo box is to set the DrawMode
property of the control. In frmDrawing, the DrawMode for all the color combo
boxes is set to OwnerDrawFixed.
The list of colors for each combo box is stored in a class-level private
ArrayList variable.

Private malBorderColors As New ArrayList()
Private malSolidColors As New ArrayList()
Private malFromColors As New ArrayList()
Private malToColors As New ArrayList()
Private malForeColors As New ArrayList()
Private malBackColors As New ArrayList()

The form creates a separate ArrayList variable for each of the combo boxes. If
you bind more than one combo box to the same array, then every time you
make a selection in one of the controls the selection in the other will
automatically change to match the first. In other words, all the combo boxes
bound to the same ArrayList will stay in sync with each other. Binding each
control to its own ArrayList prevents this.
See frmDrawing.vb
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-26 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

The Load event handler of the form calls code that populates the ArrayLists
and binds the combo boxes to them.

Private Sub frmDrawing_Load( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' Open the blank canvas form
mfrmCanvas.Show()

' Fill the color arrays.
Call FillColorArray(malBorderColors)
Call FillColorArray(malSolidColors)
Call FillColorArray(malFromColors)
Call FillColorArray(malToColors)
Call FillColorArray(malForeColors)
Call FillColorArray(malBackColors)

' Bind the color combo boxes
cboBorderColor.DataSource = malBorderColors
cboSolidColor.DataSource = malSolidColors
cboFromColor.DataSource = malFromColors
cboToColor.DataSource = malToColors
cboForeColor.DataSource = malForeColors
cboBackColor.DataSource = malBackColors

The FillColorArray subroutine relies on the fact that the Color class has shared
properties that return all the named colors. The code uses Reflection to get all
the properties of the Color class, and it filters out all the ones that arent colors.
It also filters out the Transparent color which wouldnt work in this context.
The code extracts the name from each color object found and adds the color
names to the ArrayList that was passed into the subroutine.
Drawing on Forms
Visual Basic .NET Professional Skills Development 22-27
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Sub FillColorArray(ByVal colorNames As ArrayList)
Dim pi As System.Reflection.PropertyInfo
Dim aPropInfo() As System.Reflection.PropertyInfo
' Get an array of PropertyInfo objects
' from System.Drawing.Color.
aPropInfo = GetType(System.Drawing.Color).GetProperties
For Each pi In aPropInfo
If pi.PropertyType.Name = "Color" _
And pi.Name <> "Transparent" Then
colorNames.Add(pi.Name)
End If
Next
End Su

The only task remaining is to handle the DrawItem event of the combo boxes
and to use the DrawItemEventArgs object to draw each item. One event
handler is used for all the color combo boxes.

Private Sub DrawColorItemEventHandler( _
ByVal sender As Object, _
ByVal e As DrawItemEventArgs) _
Handles cboBorderColor.DrawItem, _
cboSolidColor.DrawItem, _
cboFromColor.DrawItem, _
cboToColor.DrawItem, _
cboForeColor.DrawItem, _
cboBackColor.DrawItem
Call DrawColorListItem( _
CType(sender, ListControl), e)
End Sub

The event handler calls a generic subroutine, DrawColorListItem, passing it
references to the control that raised the event and to the DrawItemEventArgs
object. The DrawColorListItem subroutine uses GDI+ objects to draw a
colored rectangle and text for each item in the combo box list.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-28 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Drawing Shapes and Text
These are the primary types of objects used to draw with GDI+:
Graphics: A Graphics object is always needed when you use GDI+. It
represents the abstract surface that you are drawing on. It is roughly
analogous to the device context that was used in older versions of
GDI. All drawing is done in relation to a Graphics object. To create
shapes and text, you call methods of the Graphics class, such as
DrawLine, DrawRectangle, DrawEllipse, DrawArc, DrawPie, and
DrawText. There are corresponding Fill methods that use Brush
objects to fill in the shapes that are created with the Draw methods.
Pen: Pen objects are used to define the characteristics of the borders
created around your graphics shapes. Pens are used to draw lines and
outlines.
Brush: Brush objects are used to fill in areas. This includes filling the
space inside an ellipse or a rectangle, as well as filling the shapes
defined by fonts when you draw text.
Rectangle: In addition to being able to create rectangles by calling the
DrawRectangle method of a Graphics object, you also use the separate
Rectangle class to define areas within your graphics surface.
Region: Irregularly shaped areas are defined by using Region objects,
which are composed of Rectangles and Paths.
The DrawColorListItem procedure that is called from
DrawColorItemEventHandler in frmDrawing illustrates most of the
fundamental GDI+ techniques used to draw shapes and text.
The code begins by declaring a brush object that is used to fill in the text in the
list item, and a Pen object that is used to draw the rectangle that appears to the
left of the text.

Private Sub DrawColorListItem( _
ByVal sender As ListControl, _
ByVal e As DrawItemEventArgs)
Dim brText As Brush
Dim penOutline As New Pen(Color.Black)

Next, the code creates an IList object, which is used to obtain the ArrayList
that the list control is bound to.
See
DrawColorListI tem
in frmDrawing
Drawing on Forms
Visual Basic .NET Professional Skills Development 22-29
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Dim ilColors As IList = _
CType(sender.BindingContext(sender.DataSource), _
CurrencyManager).List

A Rectangle is created to define the size and position of the color rectangle
that will be drawn. This code uses the DrawItemEventArgs object (e) that was
passed into the procedure from the event handler. The rectangle is offset 5
pixels from the left edge of list item, and is given a size of 15 by 10 pixels.

Dim rct As New Rectangle( _
e.Bounds.X + 5, e.Bounds.Y + 5, _
15, 10)

The DrawItemEventArgs object (e) has an Index property that identifies which
item is currently being drawn. The code uses the index to find the
corresponding color name in the ArrayList that is stored in the IList object,
ilColors. Each item in the list is stored as an Object, so the code converts it to a
String to get the name of color for the current item.

Dim strColor As String = _
CType(ilColors.Item(e.Index), String)

The code then uses that color name to create the brush that will be used to fill
in the rectangle. The Color class has a shared FromName method that converts
a valid color name into a Color object.

Dim brColor As New SolidBrush( _
Color.FromName(strColor))

The color used for the text will vary depending on whether or not the item
being drawn is currently selected (highlighted) or not. Unselected text will be
filled in with a black brush, but selected items need to use a white brush. The
DrawItemEventArgs object provides a bit field State property that the code
uses to detect whether the current item is selected.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-30 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


If (e.State And DrawItemState.Selected) = _
DrawItemState.Selected Then
brText = Brushes.White
Else
brText = Brushes.Black
End If

Finally, the code is ready to do the actual drawing. It first calls a method of the
DrawItemEventArgs object to have Windows draw the background of the list
item according to the current Windows settings.

e.DrawBackground()

Next, the code uses the Graphics property of the DrawItemEventArgs object to
get a Graphics object that it can use to draw the text showing the color name
and the rectangle filled with a brush of that color. The code uses the Font of
the control as the font for the list item, and it offsets the text 5 pixels from the
rectangle.

e.Graphics.DrawRectangle(penOutline, rct)
e.Graphics.FillRectangle(brColor, rct)
e.Graphics.DrawString( _
strColor, _
CType(sender, Control).Font, brText, _
rct.Left + rct.Width + 5, e.Bounds.Y)

The last step is to have Windows draw the dotted focus rectangle that appears
around the active controls on a form.

e.DrawFocusRectangle()
End Sub

The SelectedIndexChanged event handler of each combo box calls a procedure
that sets the Backcolor of the PictureBox to the right of the combo box to
match the selected color.
Drawing on Forms
Visual Basic .NET Professional Skills Development 22-31
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Private Sub SyncPictureBoxToColorList( _
ByVal pic As PictureBox, ByVal ctl As ListControl)
If ctl.SelectedIndex > -1 Then
pic.BackColor = _
Color.FromName(ctl.Text)
Else
pic.BackColor = _
Color.FromName("White")
End If
End Sub

Working with Hatch Styles
The frmDrawing form also contains a combo box, cboHatch, that allows the
use to select a Hatch style used to fill the shape. This combo box is an
OwnerDraw control that displays a rectangle showing the hatch style that will
be applied. In addition to providing a variety of fill patterns, hatch styles also
allow you to specify the background and foreground colors used when drawing
the hatch pattern.
The code in frmDrawing that implements the combo box for hash styles is very
similar to the code used for the OwnerDraw color combo boxes.
One difference is the way that the code populates the control with a list of
hatch styles. The code calls the shared GetNames method of the Enum class to
get an array of all the names used in the HatchStyle enumeration.

Dim mastrHatchStyleNames() As String = _
System.Enum.GetNames( _
GetType(System.Drawing.Drawing2D.HatchStyle))

See frmDrawing.vb
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-32 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

To populate the list, the code cycles through that array and adds items to the
cboHatch combo box in the Load event handler.

Dim i As Integer
For i = 0 To mastrHatchStyleNames.Length - 1
cboHatch.Items.Add(mastrHatchStyleNames(i))
Next

The cboHatch_DrawItem event handler calls a procedure named
DrawHatchListItem, which is very similar to the DrawColorListItem
procedure that you examined in the previous section.
The code to extract the selected hatch style name is a bit simpler, but more
complex code is required to convert the name to a HatchStyle enumeration
object, because there is no FromName method as there is for colors. Also, the
code creates a HatchBrush, instead of a plain Brush object, and it sets the
ForeColor and BackColor properties of the HatchBrush in the constructor,
getting those values from the appropriate combo boxes.

Dim strHatch As String = _
mastrHatchStyleNames(e.Index)

Dim brHatch As New System.Drawing.Drawing2D.HatchBrush _
(hatchstyle:=CType(System.Enum.Parse( _
GetType(Drawing2D.HatchStyle), _
strHatch), Drawing2D.HatchStyle), _
ForeColor:=Color.FromName(cboForeColor.Text), _
BackColor:=Color.FromName(cboBackColor.Text))

When drawing the rectangle, the code can pass the HatchBrush object to the
DrawRectangle method even though the method is defined to accept a Brush
object, because HatchBrush is derived from Brush. This is a good example of
inheritance-based polymorphism at work.

e.Graphics.FillRectangle(brHatch, rct)

Drawing on Forms
Visual Basic .NET Professional Skills Development 22-33
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Drawing and Filling Shapes
Once you understand the code that is used to draw rectangles in the
OwnerDraw combo box lists, you are well on your way to understanding the
remainder of the code in the form that draws shapes on the frmCanvas form.
The btnDraw.Click event handler calls GetSettingsAndDraw, passing it a
Graphics object obtained from the frmCanvas form. GetSettingsAndDraw does
all the work of gathering up the values that the user has selected on the form.
Based on those selections, GetSettingsAndDraw calls the DrawLine,
DrawEllipse, or DrawRectangle method, passing in a brush of the correct type
to fill the shape, if a fill selection was specified. Handling gradient fills is very
similar to handling hatch fills, except that you use a LinearGradientMode,
instead of a HatchStyle, and a beginning and ending color, instead of a
forecolor and backcolor.

Private Sub btnDraw_Click( _
ByVal sender As Object, _
ByVal e As System.EventArgs) Handles btnDraw.Click
Try
GetSettingsAndDraw(mfrmCanvas.CreateGraphics)
Catch ex As System.ObjectDisposedException
'This exception occurs if
' frmCanvas was closed.
mfrmCanvas = New frmCanvas()
mfrmCanvas.Show()
GetSettingsAndDraw(mfrmCanvas.CreateGraphics)
End Try
End Sub

GetSettingsAndDraw does all the work of gathering up the values that the user
has selected on the form, and it calls the Draw procedure, which actually does
the drawing.
The Draw procedure evaluates the parameter values and calls the DrawLine,
DrawEllipse, or DrawRectangle method, passing in a brush of the correct type
to fill the shape, if a fill selection was specified.
Handling gradient fills is very similar to handling hatch fills, except you have
to use a LinearGradientMode, instead of a HatchStyle, and a beginning and
ending color, instead of a forecolor and backcolor. The form only works with
linear gradient fills, but GDI+ also supports path gradient fills.
See btnDraw.Click
in frmDrawing
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-34 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Overriding OnPaint
There is one serious problem with the way that frmDrawing works. If you
move a window on top of the drawing and then move it away, the portion of
the image that was covered does not get repainted, as shown in Figure 14.
Thats because there is no code to tell the form to repaint that image.

Figure 14. Moving an object on top of the form and then away causes covered
parts of the image to vanish.
Fortunately, this problem is easy to correct. You just need to override the
OnPaint method in frmCanvas, so that when the form repaints itself it has a
way of finding out what to paint. The one line of code that you need is already
in place, but it is commented out. Try uncommenting that line and testing the
form again.

Protected Overrides Sub OnPaint( _
ByVal e As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(e)
' Uncomment this line to
' make the last drawing persist.
StartUp.frmDrawing.GetSettingsAndDraw(e.Graphics)
End Sub

See OnPaint in
frmCanvas
Drawing on Forms
Visual Basic .NET Professional Skills Development 22-35
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Youll find that with that line of code running, the last drawing you create will
persist even after it has been covered up. In most applications, your graphics
code would go in this procedure so that the repainting would happen
automatically.
NOTE In this sample form, youll find that after uncommenting the line
of code shown above, a shape gets drawn using the default settings
in frmDrawing when frmCanvas opens. This is because the
OnPaint code is running even before you click the Draw button. If
you ever need to trigger the Paint event from your code, you can
do so by calling the Invalidate method of the formthis causes
the form to immediately be repainted.
Disposing of Graphics Objects
The Common Language Runtime (CLR) does a very good job of managing memory
resources that are consumed by objects. Garbage collection automatically releases this
memory when it is no longer being used.
However, some resources arent managed efficiently by the CLR. Database
connections, for example, usually need to be released well before garbage collection
occurs.
GDI+ objects also fall into the category of objects that you need to clean up yourself.
Most graphics objects, including Brush objects, Pen objects, and the Graphics object
itself, have a Dispose method.
A good rule of thumb is that when an object has a dispose method, you should call it
when you are finished with the object.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-36 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Creating Transparent Areas in Forms
The Opacity property of a form enables you to add degrees of transparency to
the overall image of a form. But what if you just want to make a particular
section of a form transparent? There is a property of the form that you can use
for this purpose.
TransparencyKey
Windows Forms have a property named TransparancyKey, which you can set
to any color. Any portion of the form that has a background color equivalent to
the selected TransparencyKey color will be transparent when the form is
displayed.
To see an illustration of this, click the Transparency button on the
switchboard form. Select a color from the Transparent Color menu, as shown
in Figure 15. The shapes on the form that were previously displayed with that
color will become transparent

Figure 15. The Transparency form allows you to select a color that will appear to
be transparent.

See
frmTransparency
Creating Transparent Areas in Forms
Visual Basic .NET Professional Skills Development 22-37
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

The event handler for the forms menu item click events sets the forms
TransparencyKey property by using the FromName shared method of the
Color class to convert a color name into a color object.
Private Sub Menu_ClickHandler( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles mnuRed.Click, _
mnuBlue.Click, _
mnuYellow.Click, _
mnuWhite.Click
Me.TransparencyKey = _
Color.FromName(CType(sender, MenuItem).Text)
End Sub

You can also write code that sets the TransparencyKey to equal the backcolor
of the form or of a control on the form.
WARNING! Transparent effects are not supported in all versions of Windows.
Only Windows 2000 or later (including Windows XP) supports these
effects. The TransparencyKey property will have no effect on
machines running Windows 98 or Windows ME.
Most machines that do support transparency will allow the user to
click through to objects that appear behind the transparent areas.
However, some users have reported that this click-through
behavior does not occur on their systems.
Creating OwnerDraw Menus
In addition to illustrating the use of the TransparencyKey property,
frmTransparency also demonstrates how to create an OwnerDraw menu.
The OwnerDraw property of each of the color MenuItems is set to True. Also,
the forms code includes event handlers for the MeasureItem and DrawItem
events of each of the color MenuItems. Both these events must be handled to
create and OwnerDraw menu.
See Menu_
ClickHandler in
frmTransparency
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-38 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Handling the MeasureItem Event
Here is the procedure that handles the MeasureItem event for all of the color
MenuItems on the form.

Private Sub MenuMeasureItemHandler( _
ByVal sender As Object, _
ByVal e As MeasureItemEventArgs) _
Handles mnuRed.MeasureItem, _
mnuBlue.MeasureItem, _
mnuYellow.MeasureItem, _
mnuWhite.MeasureItem
e.ItemHeight = 20
e.ItemWidth = 175
End Sub

Your responsibility in writing this event handler is very limited. You only need
to set the height and width of the item, using the ItemHeight and ItemWidth
properties of the MeasureItemEventArgs object.
TIP: In the example, the ItemHeight and ItemWidth values are hard-coded, but you
could calculate them, for example, based on the dimensions of your text. For
more information, see the Help topic titled Obtaining Font Metrics.
Handling the DrawItem Event
The code that handles the DrawItem event of the MenuItem objects is very
similar to the code you examined for the color combo boxes in frmDrawing.
This code is simpler, because it is not necessary to create an exhaustive list of
all the named colors.
See MenuDraw
I temHandler in
frmTransparency
Creating Transparent Areas in Forms
Visual Basic .NET Professional Skills Development 22-39
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Private Sub MenuDrawItemHandler( _
ByVal sender As Object, _
ByVal e As DrawItemEventArgs) _
Handles mnuRed.DrawItem, _
mnuBlue.DrawItem, _
mnuYellow.DrawItem, _
mnuWhite.DrawItem

' This brush is used to fill in
' the text shown for each item.
Dim brText As Brush
' This pen is used to draw a rectangle
' that will be filled with the color.
Dim penOutline As New Pen(Color.Black)

' Create a 15 by 10 pixel rectangle,
' slightly offset from the list item.
Dim rct As New Rectangle( _
e.Bounds.X + 5, e.Bounds.Y + 2, _
15, 10)

' In this case, the menu item
' text is the color name, so no
' other data structure is required.
Dim strColor As String = _
CType(sender, MenuItem).Text

' Create a brush of that color
' to fill in a rectangle to the
' left of the text.
Dim brColor As New SolidBrush( _
Color.FromName(strColor))

Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-40 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


' Use White text for selected items
' and black for the others.
If (e.State And DrawItemState.Selected) = _
DrawItemState.Selected Then
brText = Brushes.White
Else
brText = Brushes.Black
End If

' Have Windows draw the background.
e.DrawBackground()
' Draw and fill the rectangle for the color.
e.Graphics.DrawRectangle(penOutline, rct)
e.Graphics.FillRectangle(brColor, rct)
' Draw the text next to the rectangle.
e.Graphics.DrawString( _
strColor, _
Me.Font, brText, _
rct.Left + rct.Width + 5, e.Bounds.Y)
' There is no DrawFocusRectangle
' method for menus.

' Clean up
brColor.Dispose()
penOutline.Dispose()
e.Graphics.Dispose()
End Sub
Creating Shaped Forms
Visual Basic .NET Professional Skills Development 22-41
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Creating Shaped Forms
Every Windows Form has a Region property that defaults to being the
rectangle defined by the dimension properties of the form. However, you can
set the property to any GDI+ Region object. Region is a GDI+ class used for
describing complex shapes.
Figure 16 shows frmShape in the designer. When you run the project, click the
Shaping Forms button on the switchboard form to open frmShape. Click the
Shape button and the form will be displayed with a shape that is a combination
of an ellipse and the text entered in the text box.

Figure 16. frmShape will be transformed into a non-rectangular form when you
click the Shape button.
The code that runs in the click event handler for btnShape uses a
GraphicsPath object to create a Region. The code calls the AddString and
AddText methods of the GraphicsPath class to build up a complex shape that is
then applied to the form.
See
btnShape_Click in
frmShape
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-42 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Private Sub btnShape_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnShape.Click

Dim gp As New GraphicsPath()
gp.AddString( _
txtString.Text, New FontFamily("Arial"), _
FontStyle.Bold, 150, New PointF(97, 50), _
StringFormat.GenericDefault)
gp.AddEllipse(New Rectangle(0, 0, 100, 300))
Me.Region = New Region(gp)
End Sub

As an added effect, when the form closes, it fades away. The code in the
btnClose.Click event handler activates a timer. Each Tick event of the timer
decrements the Opacity property of the form by .05. When the opacity of the
form reaches 0, the code calls the forms Close method.

Private Sub btnClose_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnClose.Click
Timer1.Enabled = True
End Sub

Private Sub Timer1_Tick( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles Timer1.Tick
If Me.Opacity = 0 Then
Me.Close()
Else
Me.Opacity -= 0.05
End If
End Sub

See btnClose_Click
Creating Shaped Forms
Visual Basic .NET Professional Skills Development 22-43
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Summary
An MDI application has a parent window that creates a shell
environment for the application, with other child windows contained
within it.
You can designate any form as an MDI parent, simply by setting its
IsMdiContainer property to True.
Child forms may have their own menus, which can replace or merge
with those of the parent container.
The .NET Framework includes a Clipboard object to cut, copy, or
paste values to or from the Windows Clipboard.
The .NET Framework includes a set of namespaces under
System.Drawing that are devoted to graphics and that serve as object-
oriented wrappers around the graphics capabilities built into Windows.
You can use OwnerDraw controls to take control of drawing the items
in combo boxes, list boxes, and menus.
A Graphics object in GDI+ represents the abstract surface that you
draw on.
Pen objects are used to define the characteristics of lines and of the
borders created around your graphics shapes.
Brush objects are used to fill in areas.
You use the Rectangle class to define areas within a graphics surface.
Region objects can be composed of complex shapes.
You should call the Dispose method when you are finished using
graphics objects.
The Opacity property of a form enables you to add degrees of
transparency to the overall image of a form.
Any portion of the form that has a background color equivalent to the
TransparencyKey color will be transparent when the form is displayed.
To create non-rectangular forms, set the Region property of the form.
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-44 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

(Review questions and answers on the following pages.)
Creating Shaped Forms
Visual Basic .NET Professional Skills Development 22-45
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Questions
1. How do you designate a form as an MDI parent?
2. Which object does the .NET Framework provide that enables you to cut,
copy, and paste data?
3. How can you take control of drawing items in ComboBox controls?
4. Which object in GDI+ represents the abstract surface that you are drawing
on?
5. Which method should you call when you are done using graphics objects?
6. What property should you set in order to make a portion of the form
transparent when the form is displayed?

Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-46 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Answers
1. How do you designate a form as an MDI parent?
Set its IsMdiContainer property to True
2. Which object does the .NET Framework provide that allows you to cut,
copy and paste data?
The Clipboard object, plus use methods of TextBox and
RichTextBox controls
3. How can you take control of drawing items in ComboBox controls?
By setting the DrawMode property and handling the DrawItem
(and possibly the MeasureItem) events.
4. Which object in GDI+ represents the abstract surface that you are drawing
on?
A Graphics object
5. Which method should you call when you are done using graphics objects?
The Dispose method
6. What property should you set in order to make a portion of the form
transparent when the form is displayed?
Set TransparencyKey to the backcolor of the object you want to
be transparent.
Creating Shaped Forms
Visual Basic .NET Professional Skills Development 22-47
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Lab 22:
Multiple Document
Interfaces (MDI)
and Graphics
(GDI+)
TIP: Because this lab includes code that you must type in, weve tried to make it
simpler for you. Youll find all the code in MDI-GDILab.txt, in the same
directory as the sample project. To avoid typing the code, you can copy/paste
it from the text file instead.
Lab 22:
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-48 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Lab 22 Overview
In this lab youll work with MDI parent and child forms, youll create an
OwnerDraw combo box, and youll draw shapes on a form.
To complete this lab, youll need to work through three exercises:
MDI Forms
Creating an OwnerDraw ComboBox
Drawing on Forms
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-by-
step instructions.
The files for this lab are in the MDI-GDILab directory, and a completed
version is in the MDI-GDILabCompleted directory. The sample project
includes a startup switchboard form named frmMain that you can use to open
the other sample forms when you run the project.
MDI Forms
Visual Basic .NET Professional Skills Development 22-49
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

MDI Forms
Objective
In this exercise, youll change frmShell to be an MDI parent form.
Youll write code to open a new instance of frmDocument when a user selects
New from the File menu in frmShell, and to make that instance of
frmDocument be a child of frmShell.
Youll adjust the properties of the File menu items in frmShell and
frmDocument, so that the Close item, which is in frmDocument, displays
between the New and Exit items in frmShell.
Things to Consider
Which properties must you change in frmShell and frmDocument?
Does the code to open frmDocument need to do anything special to
make it a child of frmShell?
Which properties do you adjust to ensure that the menus merge
properly?
Step-by-Step Instructions
1. Open frmShell in Design view and set its IsMdiContainer property to
True.
2. Expand the File menu in the frmShell Design view and double-click the
New menu item. Add this code to the event handler:

Private Sub mnuFileNew_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles mnuFileNew.Click
Dim frm As New frmDocument()
frm.MdiParent = Me
frm.Show()
End Sub

Lab 22:
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-50 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

3. Test the forms. Run the project by pressing F5. Click the MDI button to
open frmShell. Select File|New to create a new frmDocument MDI child
window. Notice that two separate File menus are displayed in the parent
window. Select Close from the second File menu to close the document
window. Select Exit from the first File menu to close the parent window.
Close the switchboard window.
4. In Visual Studio, open frmDocument in Design view and select the
mnuFile MenuItem. Set its MergeType property to MergeItems.
5. Select the mnuFileClose MenuItem in frmDocument. Set its
MergeOrder property to 2.
6. Open frmShell in Design view. Select the mnuFile MenuItem in frmShell
and set its MergeType property to MergeItems.
7. Select the mnuFileNew MenuItem in frmShell. Set its MergeOrder
property to 1.
8. Select the mnuFileExit MenuItem in frmShell. Set its MergeOrder
property to 3.
9. Test the application by repeating Step 3. After opening a new
frmDocument window, the menus are now properly merged, as shown in
Figure 17.

Figure 17. After setting the MergeType and MergeOrder properties, the menus
merge correctly.
Creating an OwnerDraw ComboBox
Visual Basic .NET Professional Skills Development 22-51
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Creating an OwnerDraw ComboBox
Objective
In this exercise, youll change the cboGradient ComboBox control in
frmDrawing to be an OwnerDraw ComboBox that displays a rectangle
showing the gradient fill matching each item and using the colors selected in
the cboFromColor and cboToColor combo boxes.
Youll also write code to display the selected gradient in the PictureBox
control to the right of the combo box.
Things to Consider
Do you need to change any properties of cboGradient?
Where do you place the code that draws each item in the combo box?
Is there another combo box on the form that you can use as a model
for the changes to cboGradient?
What kind of object will you need to use to fill the rectangle with a
linear gradient pattern?
Is there code in the Draw procedure that you can use to see how to
create the gradient effect?
Step-by-Step Instructions
1. Open frmDrawing in Design view. Select cboGradient and set its
DrawMode property to OwnerDrawFixed.
2. Right-click on the form and select View Code. In the code window, select
cboGradient in the upper-left drop-down list. Select the DrawItem event
in the drop-down list on the right. Add this code to the event handler:
Lab 22:
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-52 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Private Sub cboGradient_DrawItem(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DrawItemEventArgs) _
Handles cboGradient.DrawItem
Call DrawGradientListItem( _
CType(sender, ListControl), e)
End Sub

3. Add this DrawGradientListItem procedure to draw each item. (The code
follows the pattern in DrawHatchListItem, and it employs the drawing
technique found in the Draw procedure to create and use a
LinearGradientBrush.)
Creating an OwnerDraw ComboBox
Visual Basic .NET Professional Skills Development 22-53
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Private Sub DrawGradientListItem( _
ByVal sender As ListControl, _
ByVal e As DrawItemEventArgs)
' Call from the DrawList event,
' passing in the event parameters.

' This brush is used to fill in
' the text shown for each item.
Dim brText As Brush
' This pen is used to draw a rectangle
' that will be filled with the hatch style.
Dim penOutline As New Pen(Color.Black)

' Create a 15 by 10 pixel rectangle,
' slightly offset from the list item.
Dim rct As New Rectangle( _
e.Bounds.X + 5, e.Bounds.Y + 5, _
15, 10)

Dim strGradient As String = _
mastrGradientNames(e.Index)

' Create a GradientBrush, based on the
' LinearGradientMode, FromColor, and ToColor.
Dim brGradient As New _
System.Drawing.Drawing2D.LinearGradientBrush _
(rect:=rct, _
color1:=Color.FromName(cboFromColor.Text), _
color2:=Color.FromName(cboToColor.Text), _
linearGradientMode:=CType(System.Enum.Parse( _
GetType(Drawing2D.HatchStyle), _
strGradient), Drawing2D.LinearGradientMode))

Lab 22:
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-54 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

' Use White text for selected items
' and black for the others.
If (e.State And DrawItemState.Selected) = _
DrawItemState.Selected Then
brText = Brushes.White
Else
brText = Brushes.Black
End If

' Have Windows draw the background.
e.DrawBackground()
' Draw and fill the rectangle for the color.
e.Graphics.DrawRectangle(penOutline, rct)
e.Graphics.FillRectangle(brGradient, rct)
' Draw the text next to the rectangle.
e.Graphics.DrawString( _
strGradient, _
CType(sender, Control).Font, brText, _
rct.Left + rct.Width + 5, e.Bounds.Y)
' Have Windows draw the dotted focus rectangle.
e.DrawFocusRectangle()

' Clean up
brGradient.Dispose()
penOutline.Dispose()
e.Graphics.Dispose()
End Sub

4. Create a procedure that updates the piccboGradient PictureBox control,
based on the selection in cboGradient, cboFromColor, and cboToColor.
Creating an OwnerDraw ComboBox
Visual Basic .NET Professional Skills Development 22-55
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Private Sub SyncPictureBoxToGradientList()

If cboGradient.SelectedIndex > -1 Then
' Create a LinearGradientBrush, based on the selected
' LinearGradientMode, FromColor, and ToColor.
Dim brGradient As New _
System.Drawing.Drawing2D.LinearGradientBrush _
(rect:=piccboGradient.DisplayRectangle, _
color1:=Color.FromName(cboFromColor.Text), _
color2:=Color.FromName(cboToColor.Text), _
linearGradientMode:=CType(System.Enum.Parse( _
GetType(Drawing2D.HatchStyle), _
cboGradient.Text), Drawing2D.LinearGradientMode))

Dim g As Graphics = piccboGradient.CreateGraphics
g.FillRectangle(brGradient,
piccboGradient.DisplayRectangle)

Else
piccboGradient.BackColor = _
Color.FromName("White")
End If
End Sub

5. Add an event handler for the SelectedIndexChanged event of
cboGradient to call SyncPictureBoxToGradientList.

Private Sub cboGradient_SelectedIndexChanged( _
ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cboGradient.SelectedIndexChanged
Call SyncPictureBoxToGradientList()
End Sub

6. Add code to the existing event handlers for the SelectedIndexChanged
events of cboFromColor and cboToColor, to call
SyncPictureBoxToGradientList.
Lab 22:
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-56 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Private Sub cboFromColor_SelectedIndexChanged( _
ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cboFromColor.SelectedIndexChanged
Call SyncPictureBoxToColorList( _
piccboFromColor, _
CType(sender, ListControl))
' Update the Gradient PictureBox
Call SyncPictureBoxToGradientList()
End Sub

Private Sub cboToColor_SelectedIndexChanged( _
ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cboToColor.SelectedIndexChanged
Call SyncPictureBoxToColorList( _
piccboToColor, _
CType(sender, ListControl))
' Update the Gradient PictureBox
Call SyncPictureBoxToGradientList()
End Sub

7. Now, run the application, click the OwnerDraw button, and test that the
gradient shows in the drop-down list and in the PictureBox control when
you select a gradient fill.
Drawing on Forms
Visual Basic .NET Professional Skills Development 22-57
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Drawing on Forms
Objective
In this exercise, youll first work with frmRectangle1, which contains a Button
control. Add code that runs when a user clicks the button and that draws a
rectangle centered on the Button. The rectangle should be 20 pixels larger than
the button, both horizontally and vertically, and it should have a black border
with a thickness of 2 pixels.
The rectangle your code creates in frmRectangle1 will disappear if you move
the form so that it is covered and then uncovered. In frmRectangle2, write code
that creates the same rectangle and displays it when the button is clicked, but
that keeps the rectangle visible even after it has been covered and uncovered.
Things to Consider
Which properties do you use to retrieve the dimensions of a Button
control?
Which objects do you use to draw a rectangle on a form?
Where should your code go if you want the rectangle to show after
being covered and uncovered?
Step-by-Step Instructions
1. Open frmRectangle1 in the designer and double-click the Draw
Rectangle Button control to create and event handler for
btnDrawRectangle.
2. Declare four variables to retrieve the desired position and dimensions of
the new rectangle, based on the properties of btnDrawRectangle. Subtract
10 from the Left and Top, and add 20 to the Width and Height to create a
rectangle that is centered on the button and 20 pixels larger in both
directions.

Dim intX As Integer = btnDrawRectangle.Left - 10
Dim intY As Integer = btnDrawRectangle.Top - 10
Dim intWidth As Integer = btnDrawRectangle.Width + 20
Dim intHeight As Integer = btnDrawRectangle.Height + 20

Lab 22:
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-58 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

3. Declare Pen and Rectangle variables to use when drawing the rectangle.

Dim pen As Pen
Dim rct As Rectangle

4. Instantiate the pen variable, setting the Color to black and the thickness
to 2.

pen = New Pen(Color.FromName("black"), 2)

5. Instantiate the rct variable, setting the x, y, Width, and Height properties
to the values computed from the position and size of the Button.

rct = New Rectangle(x:=intX, y:=intY, _
Width:=intWidth, Height:=intHeight)

6. Create a Graphics object and draw the rectangle.

Dim gr As Graphics
gr = Me.CreateGraphics
gr.DrawRectangle(pen:=pen, rect:=rct)

7. Run cleanup code to dispose of the Graphics object.

gr.Dispose()

8. Press F5 to run the project. Test the form by clicking the Rectangle 1
button on the startup switchboard form and then clicking the Draw
Rectangle button. The form should look like Figure 18.
Drawing on Forms
Visual Basic .NET Professional Skills Development 22-59
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.


Figure 18. Drawing a rectangle around a Button control.
9. Move the form so that part of the rectangle is off the screen and then move
it back. The part of the rectangle that was covered will vanish. Close the
form and the switchboard form.
10. Open frmRectangle2 in Visual Studio. Right-click and select View Code.
Create a Boolean field that you will use to keep track of when the button
on the form has been clicked.

Private mblnDrawRectangle As Boolean

11. In the upper-left drop-down list in the code window, select (Overrides). In
the list on the right, select OnPaint. Add code to this procedure to draw
the rectangle. Other than its placement in the OnPaint procedure, this code
is identical to the code used in frmRectangle1, except where it instantiates
the gr variable, which now is created using the PaintEventArgs parameter,
e.

Protected Overrides Sub OnPaint( _
ByVal e As System.Windows.Forms.PaintEventArgs)
' Run this code
' only after the button
' has been clicked.
If mblnDrawRectangle Then
' Get the rectangle position
' and dimensions.
Dim intX As Integer = btnDrawRectangle.Left - 10
Dim intY As Integer = btnDrawRectangle.Top - 10
Dim intWidth As Integer = btnDrawRectangle.Width + 20
Lab 22:
Multiple Document Interfaces (MDI) and Graphics (GDI+)
22-60 Visual Basic .NET Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company, LLC
All rights reserved. Reproduction is strictly prohibited.

Dim intHeight As Integer = btnDrawRectangle.Height + 20

' Set up the rectangle
Dim pen As Pen
Dim rct As Rectangle
pen = New Pen(Color.FromName("black"), 2)
rct = New Rectangle(x:=intX, y:=intY, _
Width:=intWidth, Height:=intHeight)

' Draw the rectangle
Dim gr As Graphics
gr = e.Graphics
gr.DrawRectangle(pen:=pen, rect:=rct)

' Cleanup
gr.Dispose()
End If
End Sub

12. Write code in the Click event handler of btnDrawRectangle to set the
Boolean variable to True, so that the code in the OnPaint procedure will
run. Call the forms Invalidate method to force a repaint now.

Private Sub btnDrawRectangle_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnDrawRectangle.Click
mblnDrawRectangle = True
' Force the form to
' be repainted.
Me.Invalidate()
End Sub

13. Test the form by moving it offscreen and back. The rectangle should
remain visible.

Das könnte Ihnen auch gefallen