Click here to Skip to main content
15,885,216 members
Articles / Desktop Programming / WPF

Magnifying Effect in WPF

Rate me:
Please Sign up or sign in to vote.
4.38/5 (17 votes)
10 Mar 2011CPOL4 min read 79.7K   5.4K   36   28
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:

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

VB.NET
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.)

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

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

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

VB.NET
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,

VB.NET
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.

VB.NET
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)


Written By
Software Developer
Kenya Kenya
Experienced C# software developer with a passion for WPF.

Awards,
  • CodeProject MVP 2013
  • CodeProject MVP 2012
  • CodeProject MVP 2021

Comments and Discussions

 
QuestionCurious about implementation of ImageSource property in dll Pin
Morny16-Jul-18 4:06
Morny16-Jul-18 4:06 
QuestionHow do use it with xaml Pin
Hesojam1-May-12 20:17
Hesojam1-May-12 20:17 
AnswerRe: How do use it with xaml Pin
Meshack Musundi3-May-12 21:50
professionalMeshack Musundi3-May-12 21:50 
GeneralRe: How do use it with xaml Pin
Hesojam5-May-12 3:41
Hesojam5-May-12 3:41 
QuestionDoes this work if scale in MainGrid is not 1? Pin
Zhen Ye5-Aug-11 5:32
Zhen Ye5-Aug-11 5:32 
AnswerRe: Does this work if scale in MainGrid is not 1? Pin
Meshack Musundi5-Aug-11 20:41
professionalMeshack Musundi5-Aug-11 20:41 
QuestionSo How do i use it in wpf Pin
Bhuvana724-Jun-11 8:23
Bhuvana724-Jun-11 8:23 
GeneralRe: So How do i use it in wpf Pin
Meshack Musundi24-Jun-11 19:18
professionalMeshack Musundi24-Jun-11 19:18 
GeneralQuite elegant application :) Pin
Jyothikarthik_N4-Apr-11 4:22
Jyothikarthik_N4-Apr-11 4:22 
GeneralRe: Quite elegant application :) Pin
Meshack Musundi4-Apr-11 5:43
professionalMeshack Musundi4-Apr-11 5:43 
GeneralExcellent control and article Pin
Iftikhar Ali22-Mar-11 9:58
Iftikhar Ali22-Mar-11 9:58 
GeneralRe: Excellent control and article Pin
Meshack Musundi22-Mar-11 19:57
professionalMeshack Musundi22-Mar-11 19:57 
GeneralRe: Excellent control and article Pin
Iftikhar Ali23-Mar-11 1:14
Iftikhar Ali23-Mar-11 1:14 
GeneralMy vote of 5 Pin
karukutimothy9-Mar-11 22:33
karukutimothy9-Mar-11 22:33 
GeneralRe: My vote of 5 Pin
Meshack Musundi10-Mar-11 5:22
professionalMeshack Musundi10-Mar-11 5:22 
Thanks Tim. I will probably build a college in future, but not for profit. Smile | :)
GeneralNice but quite an old idea Pin
Sacha Barber8-Mar-11 21:56
Sacha Barber8-Mar-11 21:56 
GeneralRe: Nice but quite an old idea Pin
Meshack Musundi9-Mar-11 0:32
professionalMeshack Musundi9-Mar-11 0:32 
GeneralRe: Nice but quite an old idea Pin
Meshack Musundi9-Mar-11 1:41
professionalMeshack Musundi9-Mar-11 1:41 
GeneralRe: Nice but quite an old idea Pin
Sacha Barber9-Mar-11 2:15
Sacha Barber9-Mar-11 2:15 
GeneralRe: Nice but quite an old idea Pin
Meshack Musundi9-Mar-11 2:45
professionalMeshack Musundi9-Mar-11 2:45 
GeneralRe: Nice but quite an old idea Pin
Sacha Barber9-Mar-11 5:24
Sacha Barber9-Mar-11 5:24 
GeneralRe: Nice but quite an old idea Pin
Meshack Musundi9-Mar-11 5:48
professionalMeshack Musundi9-Mar-11 5:48 
GeneralRe: Nice but quite an old idea Pin
Sacha Barber9-Mar-11 7:46
Sacha Barber9-Mar-11 7:46 
GeneralRe: Nice but quite an old idea Pin
Meshack Musundi9-Mar-11 19:03
professionalMeshack Musundi9-Mar-11 19:03 
GeneralRe: Nice but quite an old idea Pin
Sacha Barber9-Mar-11 19:17
Sacha Barber9-Mar-11 19:17 

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

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