Click here to Skip to main content
Click here to Skip to main content
Go to top

Magnifying Effect in WPF

, 10 Mar 2011
Rate this:
Please Sign up or sign in to vote.
Creating a magnification effect in WPF and how to use my Magnifier control

Screenshot_1.png

Introduction

The purpose of this article is to give you an idea on how you can go about creating a magnification effect in your WPF applications. The concepts you'll pick up will hopefully be of use in a full fledged application that would require such functionality. I will also explain how you can go about using my Magnifier control, a UserControl that functions like an Image control with a magnification region.

Requirements

To run the project provided from the download link above, you require either of the following:

  • Visual Studio 2010
  • Expression Blend

If you have Visual Studio 2008, you can download the source files from here.

NB: If you're using the Express Edition of Visual Studio, ensure that you open the solution using Visual Basic Express.

Magnifier (The Demo)

How It Works

Move the mouse pointer over the image. A magnification region will show up. Move your mouse to move the magnification region around the image. (If you start feeling concerned, I urge you not to worry. You should see the other guy's car.)

Design and Layout

I put everything together in Expression Blend. The following image shows how elements are laid out in the Objects and Timeline panel:

Layout.png

The elements of interest here are MainGrid and ClipGrid, both of which are children of the default LayoutRoot. ClipGrid is actually a copy of MainGrid, I just renamed it after copy-pasting. Both Grid controls have Image controls that have the same image.

NB: The Cursor property of ClipImage is set to None.

CursorProperty.png

The Code

We have three global variables:

    Private ClipRctGeo As New RectangleGeometry()
    Private ClipPath As New Path()
    Private ScaleTr As New ScaleTransform()

In the MainWindow Initialized event handler, we set the properties of these objects:

    Private Sub MainWindow_Initialized(ByVal sender As Object, _
                                       ByVal e As System.EventArgs) Handles Me.Initialized
        ' Set the position and dimensions of
        ' the clipping rectangle that defines
        ' the magnification region.
        ClipRctGeo.Rect = New Rect(0, 0, 80, 80)

        ClipPath.Stroke = Brushes.Gainsboro
        ClipPath.StrokeThickness = 2
        ClipPath.Data = ClipRctGeo

        ClipGrid.Children.Add(ClipPath)
        ClipGrid.Clip = ClipRctGeo

        ' Set magnification to 150%.
        ScaleTr.ScaleX = 1.5
        ScaleTr.ScaleY = 1.5

        ClipGrid.RenderTransform = ScaleTr
        ClipGrid.Visibility = Windows.Visibility.Hidden
    End Sub  

ClipPath defines the border/stroke color of the RectangleGeometry, ClipRctGeo. I honestly didn't know what gainsboro was, but it works well on both light and dark backgrounds. Notice that we're clipping the Grid control, ClipGrid.

The following is what we do when MainGrid's MouseEnter event is fired (remember that ClipGrid, which is stacked above MainGrid in LayoutRoot, has been clipped during MainWindow's Initialized event and its Visibility property set to Hidden.)

    Private Sub MainGrid_MouseEnter(ByVal sender As Object, _
                                    ByVal e As System.Windows.Input.MouseEventArgs) _
                                    Handles MainGrid.MouseEnter
        ' Set magnifying location.
        MoveMagnifier(e)

        If ClipGrid.Visibility = Windows.Visibility.Hidden Then
            ClipGrid.Visibility = Windows.Visibility.Visible
        End If
    End Sub

The MoveMagnifier method does the following:

    Private Sub MoveMagnifier(ByVal e As System.Windows.Input.MouseEventArgs)
        Dim mouseX As Double = e.GetPosition(ClipGrid).X
        Dim mouseY As Double = e.GetPosition(ClipGrid).Y

        ScaleTr.CenterX = mouseX
        ScaleTr.CenterY = mouseY

        ' Set the location and dimensions of the
        ' clipping rectangle.
        ClipRctGeo.Rect = New Rect((mouseX - 40), (mouseY - 40), 80, 80)
    End Sub

In the method above, we set the center points of the ScaleTransform object so that 'magnification' occurs from the location of the mouse pointer, which isn't visible since we set the Cursor property of ClipImage to None. I'm sure by now you have figured out that by scaling ClipGrid we create the illusion that the image of interest is being magnified.

The magnification region moves when ClipGrid's MouseMove event handler is called:

    Private Sub ClipGrid_MouseMove(ByVal sender As Object, _
                                   ByVal e As System.Windows.Input.MouseEventArgs) _
                                   Handles ClipGrid.MouseMove
        ' Move magnifying region.
        MoveMagnifier(e)
    End Sub

This is what happens when you move the mouse pointer beyond the magnifiable region:

    Private Sub ClipGrid_MouseLeave(ByVal sender As Object, _
                                    ByVal e As System.Windows.Input.MouseEventArgs) _
                                    Handles ClipGrid.MouseLeave
        ' Hide the canvas when the pointer is beyond
        ' the region of interest.
        ClipGrid.Visibility = Windows.Visibility.Hidden
    End Sub

Image isn't Everything

Since we're actually 'magnifying' a layout container, which is a Grid control in this case, it means that we're not limited to creating a magnification effect on images alone. The following image shows the apparent magnification of a RichTextBox control:

Screenshot_2.png

You can download the demo related to the screenshot above from here.

The Magnifier Control

As I explained in the Introduction section, the Magnifier control functions as an Image control with a magnification region. To use the Magnifier control, add a reference to Magnifier.dll that is available from the download link above. The DLL should be listed in the References folder of your project.

UserControlReferenced.png

Activate the Assets panel and start typing the word 'magnifier' in the Search text box. You should see the Magnifier control displayed when you type the first few letters.

AssetPanel.png

Double click on the Magnifier control to add it to your layout container or select it and just drag it out. In the Miscellaneous section of the Properties panel, look for the ImagesSource property and click on the ellipses button to choose an image from wherever the image you desire is located.

ImagesSource.png

The Code

The code for the Magnifier control is similar to the code I explained earlier with the addition of a DependencyProperty,

Class Magnifier
    Private ClipRctGeo As New RectangleGeometry()
    Private ClipPath As New Path()
    Private ScaleTr As New ScaleTransform()

    Public Sub New()
        MyBase.New()

        Me.InitializeComponent()

        ' Insert code required on object creation below this point.
    End Sub

    Private Sub MagnifierControl_Initialized(ByVal sender As Object, _
                                             ByVal e As System.EventArgs) _
                                             Handles Me.Initialized
        ClipRctGeo.Rect = New Rect(0, 0, 80, 80)

        ClipPath.Stroke = Brushes.Gainsboro
        ClipPath.StrokeThickness = 2
        ClipPath.Data = ClipRctGeo

        ClipGrid.Children.Add(ClipPath)
        ClipGrid.Clip = ClipRctGeo

        ' Set magnification to 150%.
        ScaleTr.ScaleX = 1.5
        ScaleTr.ScaleY = 1.5

        ClipGrid.RenderTransform = ScaleTr
        ClipGrid.Visibility = Windows.Visibility.Hidden
    End Sub

    Private Sub MoveMagnifier(ByVal e As System.Windows.Input.MouseEventArgs)
        Dim mouseX As Double = e.GetPosition(ClipGrid).X
        Dim mouseY As Double = e.GetPosition(ClipGrid).Y

        ScaleTr.CenterX = mouseX
        ScaleTr.CenterY = mouseY

        ' Set the location and dimensions of the
        ' clipping rectangle.
        ClipRctGeo.Rect = New Rect((mouseX - 40), (mouseY - 40), 80, 80)
    End Sub

    Private Sub ClipGrid_MouseLeave(ByVal sender As Object, _
                                ByVal e As System.Windows.Input.MouseEventArgs) _
                                Handles ClipGrid.MouseLeave
        ' Hide the canvas when the pointer is beyond
        ' the region of interest.
        ClipGrid.Visibility = Windows.Visibility.Hidden
    End Sub

    Private Sub ClipGrid_MouseMove(ByVal sender As Object, _
                                   ByVal e As System.Windows.Input.MouseEventArgs) _
                                   Handles ClipGrid.MouseMove
        ' Move magnifying region.
        MoveMagnifier(e)
    End Sub

    Private Sub MainGrid_MouseEnter(ByVal sender As Object, _
                                    ByVal e As System.Windows.Input.MouseEventArgs) _
                                    Handles MainGrid.MouseEnter
        ' Set magnifying location.
        MoveMagnifier(e)

        If ClipGrid.Visibility = Windows.Visibility.Hidden Then
            ClipGrid.Visibility = Windows.Visibility.Visible
        End If
    End Sub

    Public Property ImagesSource() As ImageSource
        Get
            Return CType(GetValue(ImagesSourceProperty), ImageSource)
        End Get
        Set(ByVal value As ImageSource)
            SetValue(ImagesSourceProperty, value)
        End Set
    End Property

    Public Shared ImagesSourceProperty As DependencyProperty = _
        DependencyProperty.Register("ImagesSource", _
                                    GetType(ImageSource), _
                                    GetType(Magnifier), _
                                    New FrameworkPropertyMetadata( _
                                    New PropertyChangedCallback_
				(AddressOf ChangeSource)))

    Private Shared Sub ChangeSource(ByVal source As DependencyObject, _
                                    ByVal e As DependencyPropertyChangedEventArgs)
        CType(source, Magnifier).MainImage.Source = CType(e.NewValue, ImageSource)
        CType(source, Magnifier).ClipImage.Source = CType(e.NewValue, ImageSource)
    End Sub

End Class

Magnifier Control v2.0

ZoomScreenshot.png

Version 2.0 of the Magnifier control enables the user to zoom-in on the magnification region. Scrolling the mouse wheel up will zoom-in on the magnified region and vice-versa.

And this is the code that makes this possible.

    Private Sub ClipGrid_MouseWheel(ByVal sender As Object, _
                                    ByVal e As System.Windows.Input.MouseWheelEventArgs) _
                                    Handles ClipGrid.MouseWheel
        ' Zoom in when the user scrolls up
        ' and vice-versa.
        If e.Delta > 0 Then
            ' Zoom-in.
            If zoom < 2.5 Then
                zoom += 0.5
                ScaleTr.ScaleX = zoom
                ScaleTr.ScaleY = zoom
                MoveMagnifier(e)
            End If
        Else
            ' Zoom-out.
            If zoom > 1.5 Then
                zoom -= 0.5
                ScaleTr.ScaleX = zoom
                ScaleTr.ScaleY = zoom
                MoveMagnifier(e)
            End If
        End If
    End Sub

Other Options

There are other options that you can take to create a magnifying effect in WPF, so you can take your pick on which approach will suit you best. Thanks to Sacha Barber for making me aware of these:

Conclusion

Thanks for taking the time to read this article. I hope that it was useful and you picked up something that may eventually crop up in your future applications. Cheers!

History

  • 8th March, 2011: Initial post
  • 9th March, 2011: Added UserControl
  • 11th March, 2011: Added v2 of UserControl

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

 
QuestionHow do use it with xaml PinmemberHesojam1-May-12 20:17 
AnswerRe: How do use it with xaml PinmvpMeshack Musundi3-May-12 21:50 
GeneralRe: How do use it with xaml PinmemberHesojam5-May-12 3:41 
QuestionDoes this work if scale in MainGrid is not 1? PinmemberZhen Ye5-Aug-11 5:32 
AnswerRe: Does this work if scale in MainGrid is not 1? PinmemberMeshack Musundi5-Aug-11 20:41 
QuestionSo How do i use it in wpf PinmemberBhuvana724-Jun-11 8:23 
GeneralRe: So How do i use it in wpf PinmemberMeshack Musundi24-Jun-11 19:18 
GeneralQuite elegant application :) PinmemberJyothikarthk4-Apr-11 4:22 
GeneralRe: Quite elegant application :) PinmemberMeshack Musundi4-Apr-11 5:43 
GeneralExcellent control and article PinmemberIftikhar Ali22-Mar-11 9:58 
GeneralRe: Excellent control and article PinmemberMeshack Musundi22-Mar-11 19:57 
GeneralRe: Excellent control and article PinmemberIftikhar Ali23-Mar-11 1:14 
GeneralMy vote of 5 Pinmemberkarukutimothy9-Mar-11 22:33 
GeneralRe: My vote of 5 PinmemberMeshack Musundi10-Mar-11 5:22 
GeneralNice but quite an old idea PinmvpSacha Barber8-Mar-11 21:56 
GeneralRe: Nice but quite an old idea PinmemberMeshack Musundi9-Mar-11 0:32 
GeneralRe: Nice but quite an old idea PinmemberMeshack Musundi9-Mar-11 1:41 
GeneralRe: Nice but quite an old idea PinmvpSacha Barber9-Mar-11 2:15 
GeneralRe: Nice but quite an old idea PinmemberMeshack Musundi9-Mar-11 2:45 
GeneralRe: Nice but quite an old idea PinmvpSacha Barber9-Mar-11 5:24 
GeneralRe: Nice but quite an old idea PinmemberMeshack Musundi9-Mar-11 5:48 
GeneralRe: Nice but quite an old idea PinmvpSacha Barber9-Mar-11 7:46 
GeneralRe: Nice but quite an old idea PinmemberMeshack Musundi9-Mar-11 19:03 
GeneralRe: Nice but quite an old idea PinmvpSacha Barber9-Mar-11 19:17 
GeneralRe: Nice but quite an old idea PinmvpSacha Barber9-Mar-11 2:20 

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
Web04 | 2.8.140916.1 | Last Updated 11 Mar 2011
Article Copyright 2011 by Meshack Musundi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid