Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Add Zooming to a WPF Window or User Control

4.93/5 (10 votes)
6 Jan 2015CPOL4 min read 54.2K   2.1K  
Allow users to magnify your WPF application!

Introduction

This tip explains how to add "zooming" (i.e. scaling) to a WPF application. The code is simple and applicable to most WPF apps. The download demonstrates three ways of allowing the user to control the zoom factor.

Why Do This?

Have you ever had to sit in the back of the room when somebody was presenting an application, and the text in the app was too small to read easily? Another scenario is when several people gather around a monitor to discuss something on the screen. The person sitting directly in front of the monitor can see clearly, but what about the people behind him? I was thinking about this while preparing to present an application I had just developed when I had this idea...

I realize there are other ways to address this, such as changing the resolution of the monitor/projector. However, the relatively smooth zooming demonstrated below is a pretty cool visual affect that's easily implemented.

Demonstration

In the download app, the minimum zoom factor is hard-coded to 1.0 and the max is 1.5. The GIFs below were created from the download app to demonstrate three ways of controlling the zoom factor. Of course, many other ways are possible. For example, you might use the mouse wheel or have separate "Zoom In" and "Zoom Out" buttons.

The first GIF demonstrates the use of a Slider to control the zoom factor.

860914/Slider.gif

The second GIF demonstrates the use of WPF animation to smoothly toggle between min and max zoom when the user clicks a button. The position of the slider varies automatically with the zoom factor due to data binding. The speed is determined by the duration of the animation, which in this case is set to 600 milliseconds.

860914/Animated.gif

The third GIF demonstrates the use of a button to toggle instantly between zoom factor 1.0 and 1.5. It's a bit jarring, but the code is dead simple.

860914/Instant.gif

Notice how in all three of the above GIFs the text at the top, which is in a TextBlock control, reflows (word-wraps) as needed and the other controls move down to accommodate the taller TextBlock. This is done automatically by WPF's layout engine. If your app can handle being resized, it can probably handle being zoomed in this fashion because making the window smaller has the same affect on the layout as making the contents bigger.

The GIF below is of a production application zooming between a factor of 0.7 and 1.2 (chosen to fit CodeProject's width limit of 640 pixels). The duration of the animation is set to 2 seconds to make it easier to follow. Notice that the controls just above the data grid reflow from one row to two when they become too wide to fit the window and scroll bars appear when and where needed. That occurs because I designed the app to accommodate being resized by the user. I added the zoom feature as an afterthought and it "just worked"!

860914/RealApp.gif

Using the Code

The code in this article depends on the WPF window having a single child control that in turn contains all the other controls. Let's name that immediate child control "mainPanel". Zooming is just a matter of setting mainPanel.LayoutTransform to an instance of ScaleTransform and setting that object's ScaleX and ScaleY properties to the desired scale factor.

Visual Studio always creates new windows with a Grid control as the window's immediate child. I often replace the Grid with a DockPanel or StackPanel, but any control that has the LayoutTransform property should work (i.e., any class derived from FrameworkElement).

In the download app, the zooming/scaling of the mainPanel control is done in code-behind.

Instantly changing the scale factor from, for example, 1.0 to 1.5 can produce a startling or even confusing effect for the user. A smooth change versus a sudden jump lets the user see what's going on and looks more sophisticated. This is easily accomplished using the DoubleAnimation class, which applies to double properties such as ScaleX and ScaleY. For example, the following C# method (found in the download) is all the code-behind needed to smoothly toggle the zoom factor between 1.0 and 1.5 whenever it's called.

C#
private void btnAnimate_Click(object sender, RoutedEventArgs e)
{
    // We may have already set the LayoutTransform to a ScaleTransform.
    // If not, do so now.

    var scaler = mainPanel.LayoutTransform as ScaleTransform;

    if (scaler == null)
    {
        scaler = new ScaleTransform(1.0, 1.0);
        mainPanel.LayoutTransform = scaler;
    }

    // We'll need a DoubleAnimation object to drive
    // the ScaleX and ScaleY properties.

    DoubleAnimation animator = new DoubleAnimation()
    {
        Duration = new Duration(TimeSpan.FromMilliseconds(600)),
    };

    // Toggle the scale between 1.0 and 1.5.

    if (scaler.ScaleX == 1.0)
    {
        animator.To = 1.5;
    }
    else
    {
        animator.To = 1.0;
    }

    scaler.BeginAnimation(ScaleTransform.ScaleXProperty, animator);
    scaler.BeginAnimation(ScaleTransform.ScaleYProperty, animator);
}

Points of Interest

One thing I learned while writing the demo code for this article is that when a Dependency Property like ScaleX is set via animation, as in the above code, it isn't normally possible to set the property to another value after the animation completes. That's because in the Dependency Property Setting Precedence List, animations have a higher precedence than almost everything else including explicitly assigned local values. Because of this, some extra code was required in the implementation of the Slider control and "Instant Zoom" button that wouldn't have been required if no animation was used.

For example, here's the code for the "Instant Zoom" button. The "if (scaler.HasAnimatedProperties)" check is only needed due to the possibility that the user may have clicked the "Animated Zoom" button earlier. In that case, simply setting scaler.ScaleX = 1.5 (or 1.0) will have no effect without first removing the animation that's currently keeping the property set to the final "To" value of the animation.

C#
private void btnInstant_Click(object sender, RoutedEventArgs e)
{
    var scaler = mainPanel.LayoutTransform as ScaleTransform;

    if (scaler == null)
    {
        // Currently no zoom, so go instantly to max zoom.
        mainPanel.LayoutTransform = new ScaleTransform(1.5, 1.5);
    }
    else
    {
        double curZoomFactor = scaler.ScaleX;

        // If the current ScaleX and ScaleY properties were set by animation,
        // we'll have to remove the animation before we can explicitly set
        // them to "local" values.

        if (scaler.HasAnimatedProperties)
        {
            // Remove the animation by assigning a null
            // AnimationTimeline to the properties.
            // Note that this causes them to revert to
            // their most recently assigned "local" values.

            scaler.BeginAnimation(ScaleTransform.ScaleXProperty, null);
            scaler.BeginAnimation(ScaleTransform.ScaleYProperty, null);
        }

        if (curZoomFactor == 1.0)
        {
            scaler.ScaleX = 1.5;
            scaler.ScaleY = 1.5;
        }
        else
        {
            scaler.ScaleX = 1.0;
            scaler.ScaleY = 1.0;
        }
    }
}

License

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