WPF - The Zoom Decorator: Part1

|

Adding a "zoom" slider and other required paraphernalia to create a simple "zooming" experience in your WPF applications is the point of the next couple of posts.

This one, the first, will deal with solving the "zooming" problem; the next post will then explore how to make the solution reusable by turning it in to a Decorator much like the Border or Viewbox controls shipped with WPF.

Zoom 
View a live version or download the XAML from here.

One of my goals with the zooming solution was to make it simple and if possible a zero code effort (not that I'm adverse to code if needs be, I'm not a XAML zealot, I just want to keep the solution as simple as possible). First I looked at the Viewbox control as that appeared to offer "zooming" out-of-the-box; however I abandoned that approach pretty quickly, Viewbox does not appear to be designed for this kind of thing. Next I turned to a ScaleTransform, which is perfect for our needs:

<Grid ...
      Height="200"
      Width="200">

    <Border ClipToBounds="True" ...>
        <ContentPresenter RenderTransformOrigin="0.5,0.5">
            <ContentPresenter.RenderTransform>
                <ScaleTransform
                    ScaleX="{Binding Path=Value,
                                ElementName=zoomSlider}"
                    ScaleY="{Binding Path=Value,
                                ElementName=zoomSlider}"
/>    
            </ContentPresenter.RenderTransform>
            <ContentPresenter.Content>
                <!-- CONTENT TO BE ZOOMED GOES HERE -->
            </ContentPresenter.Content>
        </ContentPresenter>       
    </Border>

    <Slider x:Name="zoomSlider"
            Maximum="2"
            Minimum="0"
 
            Value="1.0" ... />
</Grid>

There are four controls shown here with the important values highlighted.

A Grid is used to house the controls as this is simplest layout control for our needs. Placed directly within the Grid is a Border control and a Slider.

The Border acts as container for the content and is therefore clipped, to stop any zoomed content from "leaking".

The Slider is used for changing the zoom level, the interesting thing to note here is that it's value can only be a value between 0 and 2; this is for the ScaleTranform, a value of 1 for the ScaleX and ScaleY of transform equals 100 percent, i.e. no scale, but a value of 2 would be 200 percent and value of 0 would be zero percent and so on. These values therefore map nicely onto the transform without requiring any conversion code, this was not the case with the Viewbox solution and that was the main reason I abandoned the Viewbox approach. Also note where the Slider appears in the control hierarchy, it is last so that it will always be on top of any content being zoomed, this is important as you would loose the slider as the content grows bigger than the container.

Finally, inside the Border I use a ContentPresenter to apply the transform to and to act as the home for the actual content. This might seem like an interesting approach, why not just put the transform on the Border and the ClipToBounds on the Grid? The reason is if we zoom the Border the lines that make-up the borders visual appearance would also zoom, giving quite a strange effect, as shown below - zoom chrome without any content:

ZoomBorder 
View a live version or download the XAML from here.

Also by using a ContentPresenter the next phase, of making a Decorator out of this XAML, should be easier(?); I don't know yet as I have not done it, but I'm sure we'll learn something on the way. Happy zooming...

4 comments:

Anonymous said...

I like the zoom, but I have 2 Stackpanels. When I zoom in on 1 stackpanel, I want the other stackpanel to zoom out and vice versa. Is there a way to do this?

Paul said...

The short answer is yes; there are a number of ways to achieve this result. However, I cannot think of a way as simple as my post outlines, all the solutions I can think of for what you have described require some code.

If you would like to share more information about the specific scenario I'd be happy to offer some advice :-)

Thanks for reading.

-PJ

Paul said...

Really note the importance of the transform being a LAYOUT transform, and not a RENDER transform. Fell over this today .. should read my own work more often :)

Anonymous said...

THANK YOU!!!! In all capitol letters...

-Berge