Click here to Skip to main content
Click here to Skip to main content

WPF Image Viewer

, 21 Jul 2010
Rate this:
Please Sign up or sign in to vote.
Learn how to create a WPF image viewer in Expression Blend.

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:.

Image24.jpg

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.

  1. 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.
  2. Image01.jpg

  3. Select the canvas tool from the toolbox. Draw out a canvas in the LayoutRoot.
  4. 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.
  5. Image02.jpg

  6. In the Appearance section of the Properties panel, click on the Advanced Properties button. Check the ClipToBounds checkbox.
  7. Image23.jpg

  8. Rename the canvas to 'ImgCanvas'.
  9. Select the Assets panel and type the word 'content' in the search box. You will be presented with several tools. Select the ContentControl tool.
  10. Image03.jpg

  11. 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.)
  12. Image04.jpg

    Image05.jpg

    Snapping to snaplines button
  13. Rename ContentControl to 'ImgContentCtrl' and in the Common Properties section of the Properties panel, delete the text in the Content property.
  14. In the Layout section, set the Left and Top properties of ImgContentCtrl to zero.
  15. Image06.jpg

  16. 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.
  17. Image07.jpg

  18. Rename the grid to 'ImgGrid'.
  19. Image08.jpg

  20. 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.
  21. Right click the Image object in Objects and Timeline and select Auto Size > Fill from the context menu.
  22. Rename the Image object to 'ImgObject'.
  23. 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.)
  24. Rename the thumb control to 'ImgThumb'. Right click ImgThumb and select Auto Size > Fill from the context menu.
  25. 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:

  1. Select LayoutRoot in Objects and Timeline to make it the active control.
  2. 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.
  3. Image09.jpg

    Image10.jpg

  4. 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.
  5. 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".
  6. 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.
  7. 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.

  1. Click on the New button in Objects and Timeline (the New button is the one with a plus sign).
  2. Image11.jpg

  3. Click on OK in the resulting dialog to create a new storyboard with the default name Storyboard1.
  4. Select the grid element in Objects and Timeline then drag the playhead to 5 milliseconds.
  5. Image12.jpg

  6. Hold down Shift + Alt, and using the grid element's resize handles, slightly increase the size of the grid by dragging outwards.
  7. Image13.jpg

  8. 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.
  9. In Objects and Timeline, click on the dropdown arrow next to the New button and select Reverse from the context menu.
  10. In Objects and Timeline, click on the Close Storyboard button.
  11. Image14.jpg

  12. Select the Triggers panel and then select the IsMouseOver property trigger.
  13. Image15.jpg

  14. Click on the Add new action button in the Actions when activating section. Storyboard1 is set to begin as the default action.
  15. 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.
  16. Image16.jpg

  17. Click on the Scope Up button in Objects and Timeline to exit the edit-template mode.
  18. Image17.jpg

  19. Rename the button to 'OpenButton'.
  20. 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.
  21. Image18.jpg

  22. Rename the new button to 'BestFitButton'.
  23. 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.
  24. 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.
  25. 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".
  26. Click on the Scope Up button to exit edit-template mode.
  27. 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'.

Image19.jpg

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.

  1. Select the Project panel and expand MainWindow.xaml. Double click on MainWindow.xaml.vb to open it in Blend's code editor.
  2. Add the following code:
  3. ' Create a ScaleTransform object
    Private myScale as New ScaleTransform
        
    Public Sub New()
             Me.InitializeComponent()
             ' Set the ContentControl's RenderTransform property
             ImgContentCtrl.RenderTransform = myScale
    End Sub
  4. Next, we'll add code to enable panning. Switch back to the design window and select ImgThumb in the Objects and Timeline panel.
  5. Click on the Events button in the Properties panel to display the Thumb control's events.
  6. Image20.jpg

  7. Type 'ImgThumb_DragDelta' in the DragDelta event textbox and press Enter.
  8. Image21.jpg

  9. In ImgThumb's DragDelta event handler, type in the following code:
  10. 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))
  11. 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.
  12. Image22.jpg

  13. Type in the following code in ImgThumb's MouseWheel event handler:
  14. ' Variable for holding the mouse's delta value.
    Dim deltaValue as Integer
            
    deltaValue = e.Delta
            
    ' Set the center point of the ScaleTransform object
    ' to the cursor location.
    myScale.CenterX = e.GetPosition(ImgContentCtrl).X
    myScale.CenterY = e.GetPosition(ImgContentCtrl).Y
            
    ' Zoom in when the user scrolls the mouse wheel up
    ' and vice versa.
    If deltaValue > 0 Then
        ' Limit zoom-in to 500%
        If myScale.ScaleX < 5 Then
            ' Zoom-in in 10% increments
            myScale.ScaleX += 0.1
            myScale.ScaleY += 0.1
        End If
    ' When mouse wheel is scrolled down...
    Else
        ' Limit zoom-out to 80%
        If myScale.ScaleX > 0.8 Then
            ' Zoom-out by 10%
            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.

  1. Switch back to the designer window and select OpenButton in Objects and Timeline.
  2. 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.
  3. Type the following code just before the class declaration:
  4. Imports Microsoft.Win32
  5. In OpenButton's Click event handler, type in the following code:
  6. 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()
        ' Load image file into Image Control
        ImgObject.Source = newImage
    End If
    ' Set the view of the ContentControl to a
    ' suitable scale and centered.
    BestFit()
  7. Switch back to the design window and select BestFitButton. Type 'BestFitButton_Click' in the Click event textbox of the Properties panel and press Enter.
  8. Type in the following code in BestFitButton's Click event handler:
  9. BestFit()
  10. 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:
  11. Private Sub BestFit()
        ' Set the scale of the ContentControl
        ' to 100%.
        myScale.ScaleX = 1
        myScale.ScaleY = 1        
            
        ' Set the position of the ContentControl 
        ' so that the image is centered.
        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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Meshack Musundi
Software Developer
Kenya Kenya
Meshack is an avid programmer with a bias towards WPF and VB.NET. He has about 5 years of programming experience initially starting off with Java before shifting to .NET, thanks to the allure of WPF. He has developed several applications, and written several articles about them, which can be viewed here on CodeProject. He currently resides in a small town in Kiambu county, Kenya.
 
Awards;
  • CodeProject MVP 2013
  • CodeProject MVP 2012
  • Best VB.NET article of August 2013
  • Best VB.NET article of February 2013
  • Best VB.NET article of October 2012
  • Best VB.NET article of July 2012
  • Best VB.NET article of February 2012
  • Best VB.NET article of January 2012
  • Best VB.NET article of November 2011
  • Best VB.NET article of June 2011
  • Best VB.NET article of May 2011
  • Best VB.NET article of March 2011
  • Best VB.NET article of February 2011
  • Best VB.NET article of January 2011
  • Best VB.NET article of December 2010
  • Best VB.NET article of November 2010

Comments and Discussions

 
GeneralMy vote of 4 PinmemberBehs14-Feb-13 13:32 
QuestionNice Tutorial - But have some questions PinmemberAscott039-Mar-12 15:31 
AnswerRe: Nice Tutorial - But have some questions PinmvpMeshack Musundi9-Mar-12 22:23 
GeneralMy vote of 5 Pinmembermanoj kumar choubey28-Feb-12 22:39 
GeneralRe: My vote of 5 PinmvpMeshack Musundi9-Mar-12 22:12 
GeneralRe: My vote of 5 Pinmemberjerryoz22-Mar-12 4:06 
QuestionProblem to code using Visual Basic 2010 express Pinmemberjason27262-Jan-12 21:24 
AnswerRe: Problem to code using Visual Basic 2010 express PinmemberMeshack Musundi3-Jan-12 2:16 
GeneralRe: Problem to code using Visual Basic 2010 express Pinmemberjason27263-Jan-12 21:31 
QuestionCan it used for ink canvas [modified] Pinmemberkeyzame22-Dec-10 2:22 
AnswerRe: Can it used for ink canvas PinmemberMeshack Musundi22-Dec-10 4:51 
AnswerRe: Can it used for ink canvas [modified] PinmemberMeshack Musundi22-Dec-10 6:37 
GeneralMy vote of 2 Pinmemberpadu_merloti2-Dec-10 8:14 
GeneralRe: My vote of 2 PinmemberMeshack Musundi3-Dec-10 21:10 
GeneralMy vote of 5 Pinmemberkarukutimothy10-Aug-10 10:11 
GeneralGood post PinmemberJameskerry25-Jul-10 23:26 
GeneralNice Pinmemberrspercy6022-Jul-10 2:11 
GeneralRe: Nice PinmemberMeshack Musundi22-Jul-10 5:45 
GeneralMy vote of 5 Pinmemberkarukutimothy22-Jul-10 0:26 
GeneralRe: My vote of 5 PinmemberMeshack Musundi22-Jul-10 5:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140814.1 | Last Updated 21 Jul 2010
Article Copyright 2010 by Meshack Musundi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid