Introduction
In this tutorial, I will show you how to create a WPF image viewer in Expression Blend. The application will (hopefully) look like the following image once you are through:.
Background
The application will have some basic functionality allowing the user to pan and zoom an image. Zooming will be achieved by scrolling the mouse wheel. The buttons in the application are custom buttons, and you'll need to make use of the VS2008 Image Library.
Design
Let's start off by designing the UI of the application. Start Expression Blend and create a new WPF application project named 'Image Viewer'. Ensure that the language is set to Visual Basic in the Language combo box.
- Select the Window element in Objects and Timeline. Select Background in the Brushes section of the Properties panel, and set the background to a gradient brush. Set the first gradient stop to white, and the second to a light shade of grey.
- Select the canvas tool from the toolbox. Draw out a canvas in the
LayoutRoot
.
- In the Layout section of the Properties panel, set the top, left, and right margins of the canvas to 15, and the bottom margin to 60.
- In the Appearance section of the Properties panel, click on the Advanced Properties button. Check the ClipToBounds checkbox.
- Rename the canvas to '
ImgCanvas
'.
- Select the Assets panel and type the word 'content' in the search box. You will be presented with several tools. Select the
ContentControl
tool.
- Draw out a
ContentControl
in ImgCanvas
. Using the resize handles, if necessary, set it to fit the canvas edge-to-edge. (Turn on snapping to snaplines so that the edges of the ContentControl
snap onto those of the canvas.)
Snapping to snaplines button
- Rename
ContentControl
to 'ImgContentCtrl
' and in the Common Properties section of the Properties panel, delete the text in the Content
property.
- In the Layout section, set the
Left
and Top
properties of ImgContentCtrl
to zero.
- Select the Grid tool from the toolbox, and draw out a grid in
ImgContentCtrl
. Set the grid's edges to fit exactly with those of the ContentControl
.
- Rename the grid to '
ImgGrid
'.
- Type the word 'image' in the search box of the Assets panel. With
ImgGrid
still selected, double click on the Image
tool to add an image object to ImgGrid
.
- Right click the
Image
object in Objects and Timeline and select Auto Size > Fill from the context menu.
- Rename the
Image
object to 'ImgObject
'.
- Type the word 'thumb' in the search box of the Assets panel. You are presented with several tools. Double click on the
Thumb
tool to add it to ImgGrid
. (ImgGrid
has to be the active content control. The active content control has a blue border.)
- Rename the thumb control to '
ImgThumb
'. Right click ImgThumb
and select Auto Size > Fill from the context menu.
- In the Appearance section of the Properties panel, set the opacity of
ImgThumb
to zero.
The design work is nearly complete, but we need to add some button controls. We shall create two custom buttons, one for showing an Open File dialog, the other for making the image viewable in full after a zoom action. I prefer designing my custom buttons in Expression design, but in this case, to make work easier, we shall make use of some of the images in the VS2008 Image Library. I will specifically make use of some of the images found in the path "C:\Program Files\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033\VS2008ImageLibrary\Objects\png_format\WinVista".
Let's design the custom buttons:
- Select
LayoutRoot
in Objects and Timeline to make it the active control.
- Select the
Grid
tool from the toolbox and draw a grid close to the bottom-left edge of the LayoutRoot
. I have set my grid to a width of 40 and a height of 36. The top and right margins are zero, and the left margin is 34, while the bottom margin is 8.
- With the new grid still selected, type 'image' in the search box of the Assets panel. Double click on the Image tool to add an image object to the new grid.
- In the Common Properties section of the Properties panel, select the button with ellipses right next to the Source property combo box. This opens the Add Existing Item dialog box. Navigate and open the file named Folder_Open in the path "C:\Program Files\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033\VS2008ImageLibrary\Objects\png_format\WinVista".
- Right click the new grid in Objects and Timeline and select Make Into Control from the context menu. In the Make Into Control dialog that appears, select the button control (it is the default) and click on OK.
- The custom button's template will be opened when you click on OK. Select the
ContentPresenter
in Objects and Timeline. Delete the text in the Content
property in the Common Properties section of the Properties panel.
Next we shall animate the custom button so that it increases slightly in size during Mouse_Over
events.
- Click on the New button in Objects and Timeline (the New button is the one with a plus sign).
- Click on OK in the resulting dialog to create a new storyboard with the default name
Storyboard1
.
- Select the grid element in Objects and Timeline then drag the playhead to 5 milliseconds.
- Hold down Shift + Alt, and using the grid element's resize handles, slightly increase the size of the grid by dragging outwards.
- In the Objects and Timeline panel, click on the dropdown arrow next to the New button and select Duplicate from the context menu. A duplicate of
Storyboard1
, Storyboard_Copy1
, is created and opened.
- In Objects and Timeline, click on the dropdown arrow next to the New button and select Reverse from the context menu.
- In Objects and Timeline, click on the Close Storyboard button.
- Select the Triggers panel and then select the
IsMouseOver
property trigger.
- Click on the Add new action button in the Actions when activating section.
Storyboard1
is set to begin as the default action.
- Click on the Add new action button in the Action when deactivating section.
Storyboard1
is set to begin as the default action. Click on the dropdown arrow of the first combo box and select Storyboard_Copy1.
- Click on the Scope Up button in Objects and Timeline to exit the edit-template mode.
- Rename the button to '
OpenButton
'.
- With
OpenButton
still selected, copy-paste a new button onto the Window element. The new button is pasted on top of the first button. Using the right arrow key, nudge it to the right of the first button.
- Rename the new button to '
BestFitButton
'.
- Right click
BestFitButton
and select Edit Template > Edit a Copy from the context menu. Click on OK in the resulting dialog to open the button's template.
- In Objects and Timeline, select the image object and click on the ellipses button next to the
Source
property combo box in the Common Properties section of the Properties panel.
- In the Add Existing Item dialog, navigate to the folder "C:\Program Files\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033\VS2008ImageLibrary\Objects\png_format\WinVista" and open the file named "generic_picture".
- Click on the Scope Up button to exit edit-template mode.
- In the Common Properties section of the Properties panel, set the
Tooltip
property of OpenButton
to 'Open File' and that of BestFitButton
to 'Best Fit'.
The design work is finally complete. Coding is next, so save your work if you haven't been doing so.
Coding
Next, we shall add code to enable the user to open an image file, zoom-in and out on an image at the cursor location, pan the image, and restore the image to a suitable view after a zoom action.
- Select the Project panel and expand MainWindow.xaml. Double click on MainWindow.xaml.vb to open it in Blend's code editor.
- Add the following code:
Private myScale as New ScaleTransform
Public Sub New()
Me.InitializeComponent()
ImgContentCtrl.RenderTransform = myScale
End Sub
- Next, we'll add code to enable panning. Switch back to the design window and select
ImgThumb
in the Objects and Timeline panel.
- Click on the Events button in the Properties panel to display the
Thumb
control's events.
- Type 'ImgThumb_DragDelta' in the
DragDelta
event textbox and press Enter.
- In
ImgThumb
's DragDelta
event handler, type in the following code:
Dim left as Double = Canvas.GetLeft(ImgContentCtrl)
Dim top as Double = Canvas.GetTop(ImgContentCtrl)
Canvas.SetLeft(ImgContentCtrl, (left + e.HorizontalChange))
Canvas.SetTop(ImgContentCtrl, (top + e.VerticalChange))
- The previous code caters for panning actions, so let's add code for zooming. Switch back to the designer window and scroll down
ImgThumb
's events for the MouseWheel
event. Type 'ImgThumb_MouseWheel' in the MouseWheel
event textbox and press Enter.
- Type in the following code in
ImgThumb
's MouseWheel
event handler:
Dim deltaValue as Integer
deltaValue = e.Delta
myScale.CenterX = e.GetPosition(ImgContentCtrl).X
myScale.CenterY = e.GetPosition(ImgContentCtrl).Y
If deltaValue > 0 Then
If myScale.ScaleX < 5 Then
myScale.ScaleX += 0.1
myScale.ScaleY += 0.1
End If
Else
If myScale.ScaleX > 0.8 Then
myScale.ScaleX -= 0.1
myScale.ScaleY -= 0.1
End If
End If
That does it for zooming and panning. Next, we'll add code for opening an image file and restoring the image to full view after zooming or panning.
- Switch back to the designer window and select
OpenButton
in Objects and Timeline.
- In the Properties panel, look for the
Click
event (it's at the top, so scroll up) and type 'OpenButton_Click' in the Click
event textbox. Press Enter.
- Type the following code just before the class declaration:
Imports Microsoft.Win32
- In
OpenButton
's Click
event handler, type in the following code:
Dim OpenDialog as New OpenFileDialog
With OpenDialog
.Filter = "Image Files (*.jpeg)|*.jpg|All Files (*.*)|*.*"
.Title = "Open Image File"
End With
OpenDialog.ShowDialog()
If OpenDialog.FileName <> String.Empty Then
Dim newImage As New BitmapImage()
newImage.BeginInit()
newImage.UriSource New Uri(OpenDialog.FileName, UriKind.RelativeOrAbsolute)
newImage.EndInit()
ImgObject.Source = newImage
End If
BestFit()
- Switch back to the design window and select
BestFitButton
. Type 'BestFitButton_Click' in the Click
event textbox of the Properties panel and press Enter.
- Type in the following code in
BestFitButton
's Click
event handler:
BestFit()
- The
BestFit()
method, that is called in both the BestFitButton
and OpenButton
Click
event handlers, sets the image to a suitable fit in the canvas so that it is fully viewable after a zoom or scroll action. Type in the following code in Blend's code editor:
Private Sub BestFit()
myScale.ScaleX = 1
myScale.ScaleY = 1
Canvas.SetLeft(ImgContentCtrl, 0)
Canvas.SetTop(ImgContentCtrl, 0)
End Sub
You can now run the project and open an image file. Try zooming and panning. If there are any errors, check your code. Notice that when you maximize the window, the image doesn't stay centered. To remedy this, stop the project if it's still running. Right-click ImgCanvas
and select GroupInto > ViewBox. Run the project again, and once you've opened an image file, maximize the window.
Conclusion
The image viewer application doesn't have scrollbars, but you can implement them by grouping ImgCanvas
in a ScrollViewer
and increasing ImgCanvas
' size as you zoom-in. You'll have to play around with the code to get it working perfectly, but I believe the current implementation is suitable. I hope the article was helpful. Thanks for reading.
History
- 21 July, 2010: Initial post.