The Imageviewer Project
Your first project in this chapter is a simple image viewer based on the Visual Basic .NET PictureBox control. You need to create a new project called PictureBox and place a PictureBox control and two command buttons on the form, as shown in Figure 22.3.
Figure 22.3. The Imageviewer form and control placement.
As you can see in Figure 22.3, the PictureBox control is the large panel-looking object on the form. You set the initial size of the PictureBox control to the values 416x272. Because Visual Basic .NET expresses all graphics objects in terms of pixels, the first pixel displayed on the picture box would be at coordinates (0,0), or the upper-left corner of the PictureBox . Similarly, the last pixel shown would be at coordinates (416,272), or the lower-right corner of the PictureBox .
All pixel coordinates for the PictureBox image are expressed in terms of the PictureBox coordinates, not in terms of the form itself. The fact that you placed the PictureBox control on the form at coordinates (8,8) has no bearing on the image displayed in the PictureBox .
The two buttons on the form are named btnOpen and btnExit . Between these two buttons is a label object named lblSize . You will use this label to display the original image size. You set its TextAlign property to MiddleCenter . In addition to these controls, there is a new control in the project ”the OpenFileDialog control, which is described in the following section.
The OpenFileDialog Control
To locate the OpenFileDialog control, you need to scroll the toolbox's Windows Forms tab until you see the OpenFileDialog control. Then you should double-click the OpenFileDialog control to move it into your project. Your display should look similar to the one shown in Figure 22.4.
Figure 22.4. The OpenFileDialog control.
Notice that the OpenFileDialog control does not actually appear on the form itself; rather, it is displayed in its own window below the form. (The OpenFileDialog control is not an object that is visible at runtime, which is one reason it is placed in its own window at design time.)
The OpenFileDialog control has a number of properties that you can set. At the top of the Properties window you can see an expandable entry named DynamicProperties . If you expand this entry and click the Advanced button, you see a list of the dynamic OpenFileDialog properties that are available. For the sake of this example, you need to use only a few of these properties. (You can use the Visual Basic .NET online help system to get details about the properties that are not used in this chapter.)
Listing 22.1 shows the code that is necessary to use the OpenFileDialog control in your project.
Listing 22.1 The btnOpen Code for the OpenFileDialog Control
Private Sub btnOpen_Click(ByVal sender As System.Object, ByVal e As _ System.EventArgs) Handles btnOpen.Click Dim path As String Static InitialSize As Size = PictureBox1.Size ' Save original ctrl size PictureBox1.Size = InitialSize ' Reset the control size With OpenFileDialog1 ' Initialize the file dialog .Filter = "Bitmap (*.BMP)*.bmpIcon (*.ICO)*.ICOGIF (*.GIF)_ *.GIFMetafile (*.WMF)*.WMFJPEG (*.JPG)*.JPG" .FilterIndex = 5 .InitialDirectory = "C:\" .Title = "Select Image File" End With If OpenFileDialog1.ShowDialog() = DialogResult.OK Then ' File selected? path = OpenFileDialog1.FileName Me.Text = "View a Graphics Image " & path CalculateControlSize(path) End If End Sub
The code in Listing 22.1 starts with a definition of InitialSize as Static . Recall from Chapter 8, "Scope and Lifetime of Variables ," that a Static data type has the scope of a local variable but a lifetime equal to that of the project. Therefore, InitialSize will retain the original size of the PictureBox for the life of the program. This means that InitialSize is set at program startup and is never reset thereafter. So what?
The reason you need to preserve the original size is that you use the control's size to fit each image within the PictureBox control. If you load in relatively small images, the PictureBox control is resized to fit the smaller picture. The next picture, therefore, would be forced to fit into the now-smaller PictureBox control. If you kept reading smaller images, eventually the PictureBox control would get so small you wouldn't be able to see the image. By resizing the PictureBox control to its original size each time you read another image, you ensure that you won't fall prey to the ever-shrinking- PictureBox -control problem.
The code then uses the With loop structure to move through the OpenFileDialog1 collection. The first property is the Filter property. The Filter property is used to limit the types of files that are shown in the dialog box. Therefore, Visual Basic .NET uses this statement:
.Filter = "Bitmap (*.BMP)*.bmpIcon (*.ICO)*.ICOGIF (*.GIF)_ *.GIFMetafile (*.WMF)*.WMFJPEG (*.JPG)*.JPG"
to place the file choices in the drop-down list box that is part of the OpenFileDialog control. This is shown in Figure 22.5.
Figure 22.5. The drop-down list box in the OpenFileDialog control.
Notice that the options presented in the drop-down list box shown in Figure 22.5 match the file choices shown in the code in Listing 22.1.
In the Filter property statement, each drop-down choice has two parts. The first part (for example, Bitmap (*.BMP)) is the string that is shown in the list box when the drop-down arrow is clicked. The second component (for example, *.bmp) is used by Visual Basic .NET as a file search parameter if that particular option is selected. These two parts are separated by the vertical bar character (). Collectively, these two parts form one file type that might appear in the list box. You can create multiple file types by simply separating each file option with another vertical bar.
The second property in the collection is FilterIndex . This property simply tells Visual Basic .NET which option to place in the list box as the default choice. You need to set this to 5 if you want to show the JPEG image format as the default choice. Note that the index is 1 based, not 0 based, which is why the JPEG choice is 5 instead of 4 .
The third property is InitialDirectory . This string causes Visual Basic .NET to use the directory that is specified as the starting point for locating the image files.
The fourth property is Title . This string is displayed in the title bar of the OpenFileDialog control. The string you select gives the user a hint as to the purpose of the dialog box.
The ShowDialog() Method
The ShowDialog() method causes Visual Basic .NET to display the OpenFileDialog control onscreen, using the properties that you have set. The screen should look similar to the one shown in Figure 22.5. The ShowDialog() method returns an Enum data type named DialogResult that may return the values Abort , Cancel , Ignore , No , None , OK , Retry , or Yes . You use an If statement to test DialogResult to make sure valid data is returned from the OpenFileDialog1 control.
In the If statement block, you set a variable named path to hold the path and name of the image file. You copy this information to the title bar of the form so that the users know which image file they are viewing. The code then calls the CalculateControlSize() procedure to do the rest of the work.
The CalculateControlSize() Procedure
The real work for the image viewer is done in the CalculateControlSize() procedure. The code for this procedure is shown in Listing 22.2.
Listing 22.2 The CalculateControlSize() Procedure
Private Sub CalculateControlSize(ByVal path As String) Dim OriginalX, OriginalY As Integer Dim NewSize As Size Dim Aspect As Double Dim pic As Image = Image.FromFile(path) OriginalX = PictureBox1.Size.Width ' Original control size OriginalY = PictureBox1.Size.Height lblSize.Text = "(" & CStr(pic.Width) & "x" & CStr(pic.Height) & ")" Aspect = CDbl(pic.Height) / CDbl(pic.Width) ' Find the aspect ratio If Aspect < 1.0 Then ' If height less than width NewSize.Height = CInt(Aspect * OriginalX) NewSize.Width = OriginalX PictureBox1.Size = NewSize Else ' width less than height NewSize.Width = CInt(OriginalY / Aspect) NewSize.Height = OriginalY PictureBox1.Size = NewSize End If ' Force image to fit the control PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage ' Load the picture into the control. PictureBox1.Image = Image.FromFile(path) End Sub
The code in Listing 22.2 defines some working variables, of which pic is the most interesting. The pic variable is an instance of an Image object that is initialized to the file selected by the user. (You pass the image filename to the procedure by using the path argument.) This allows you to determine the height and width of the selected image. You can use these parameters to determine the aspect ratio of the image and to display those parameters on the lblSize label. The following section discusses the aspect ratio, which is a critical element in properly displaying images.
The Aspect Ratio
In Listing 22.2, the variables OriginalX and OriginalY hold the size of the PictureBox control. Because you set the size of the PictureBox control by using the Static variable InitialSize in the btnOpen object's Click() event, OriginalX and OriginalY are always set to 416 and 272 . These values will rarely equal the exact dimensions of the image you are about to view. Therefore, you need to resize the PictureBox control relative to the image size. You do this by calculating the aspect ratio of the image.
The aspect ratio is simply the height of the image relative to its width. For example, if you select an image with the pixel dimensions 1600x1200, the aspect ratio is .75 (that is, .75 = 1200 / 1600). The problem is that the PictureBox control has an aspect ratio of .6538 (that is, .6538 = 272 / 416). It's the old round-peg-square-hole problem. If you try to shoehorn the image into the PictureBox control by using its current size, the image will look distorted . Not good.
You can use the aspect ratio to solve two display problems. First, you want to display the image without having to use scrollbars. Well, there's no way the original 1600x1200 image size is going to fit into a 416x272 PictureBox control without scrollbars unless you resize the image or the PictureBox control ”or both.
The If statement block tests the Aspect variable, which is the aspect ratio for the current image. If the aspect ratio is less than 1.0, you know the image is wider than it is tall. In that case, you use the aspect ratio to adjust the height of the PictureBox control and then set the width to its original value. Some actual numbers will help explain how this works.
Suppose you select an image that is 1600x1200. In that case, Aspect would equal .75, which would cause the If test to execute this statement:
NewSize.Height = CInt(Aspect * OriginalX)
which resolves to this:
312 = CInt(.75 * 416)
This sets the Height member of the NewSize object to 312 . The next statement sets the Width member of the NewSize object to its original value, 416 :
NewSize.Width = OriginalX
The next statement uses these new parameters to resize the PictureBox control to its new dimensions, 416x312:
PictureBox1.Size = NewSize
If you divide 312 by 416, you find that it equals .75, which is exactly the aspect ratio of the image you want to display. Because the relative size of the PictureBox control matches the aspect ratio of the image, you can display the image without distortion.
If you want to view an image whose height is greater than its width, the aspect ratio is greater than 1.0 and the Else statement block is executed. If you work through the math, you find that the Else statement block in Listing 22.2 adjusts the width of the PictureBox control to maintain the image's aspect ratio.
This statement sets the SizeMode property to StretchImage :
PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage
This tells Visual Basic .NET to scale the incoming image to fit within the confines of the (now resized) PictureBox control.
The final statement loads the image into the picture box and displays it:
PictureBox1.Image = Image.FromFile(path)
A sample run of the program is shown in Figure 22.6.
The only code not shown for the Imageviewer project is the code for the btnExit object's Click() event, which you already know how to write. As you can see from the sample program, viewing an image with Visual Basic .NET is very easy. In the next section, you will learn how to draw your own graphics objects.