Skip Navigation LinksHome > View Post

WPF ScrollViewer Thumbnail

I've been working with WPF for a while now but the power of the WPF platform genuinely continues to amaze me.

Just recently I set about creating a ScrollViewer Thumbnail control for an application I was working on. You know the kind of thing, you have some content within a Scrollable area (or ScrollViewer in the case of WPF) and you'd like to provide an additional preview showing a zoomed out version of all the content and a small bounding box showing where the view port was currently located.

Like this in the LINQ-to-Entities designer:

LINQ-to-Entities scroll preview

Of course, I wanted my solution to be generic and be applicable to any ScrollViewer. You'll be amazed at just how easy it was to achieve this in WPF.

As always, you should look to databinding in WPF and see what it has to offer. Initially, you might rule it out from this scenario (because we're not dealing with data?) but that would be a huge mistake. This is all it takes to get a working WPF ScrollViewer Preview control up and working (imagine the scrollviewer in question is called 'myScrollviewer'):

<Viewbox DataContext="{Binding ElementName=myScrollViewer}">
    <Grid>
        <Rectangle
            Width="{Binding Content.ActualWidth}"
            Height="{Binding Content.ActualHeight}">
            <Rectangle.Fill>
                <VisualBrush Visual="{Binding Content}" />
            </Rectangle.Fill>
        </Rectangle>
        <Border BorderThickness="1"
            BorderBrush="Black"
            Background="#88FFFF00"
            Width="{Binding ViewportWidth}"
            Height="{Binding ViewportHeight}"
            HorizontalAlignment="Left"
            VerticalAlignment="Top">
            <Border.RenderTransform>
                <TranslateTransform
                    X="{Binding HorizontalOffset}"
                    Y="{Binding VerticalOffset}" />
            </Border.RenderTransform>
        </Border>
    </Grid>
</Viewbox>

And we're done. And here's how it looks when placed next to a ScrollViewer containing an image:

ScrollViewer Thumbnail

Breaking it down

Let's take a look at each piece that makes this work.

Firstly, we put the whole thing inside a Viewbox. This will be explained later. Note that we bind the DataContext of this element to the ScrollViewer we want to create a thumbnail for:

<Viewbox DataContext="{Binding ElementName=myScrollViewer}">

Then, we wrap the contents of the ViewBox in a grid. Mostly, this is just to allow the Viewbox to contain more than one control (but using a Canvas would blow the layout). The first control we drop inside the grid is a Rectangle that will be used to display the thumbnail itself. And the way we achieve this is to use a VisualBrush. A VisualBrush is like any other Brush (e.g. SolidColorBrush, LinearGradientBrush) except the fill is taken by rendering another visual element as the brush itself. In this case, we databind that visual property to the Content property of the ScrollViewer we want to thumbnail (remember, he's the current datacontext at this point in the Visual Tree). Clever eh?

<Rectangle
    Width="{Binding Content.ActualWidth}"
    Height="{Binding Content.ActualHeight}">
    <Rectangle.Fill>
        <VisualBrush Visual="{Binding Content}" />
    </Rectangle.Fill>
</Rectangle>

Note that we also bind the width and height of the Rectangle to the actual width and height of the ScrollViewer's content.

Next, we create our Yellow rectangle that shows the current visible area on the Thumbnail. I've used a Border for this with a 1 pixel black border and it's aligned top and left. Note that I've also bound the width and height of the border to the ViewportWidth and ViewportHeight of the ScrollViewer. The Viewport is the visible part of the content.

<Border BorderThickness="1"
    BorderBrush="Black"
    Background="#88FFFF00"
    Width="{Binding ViewportWidth}"
    Height="{Binding ViewportHeight}"
    HorizontalAlignment="Left"
    VerticalAlignment="Top">
    <Border.RenderTransform>
        <TranslateTransform
            X="{Binding HorizontalOffset}"
            Y="{Binding VerticalOffset}" />
    </Border.RenderTransform>
</Border>

Finally, we need to offset the Border by the same amount the current ScrollViewer is offset from it's content. Fortunately, the ScrollViewer control has both a HorizontalOffset and a VerticalOffset property. We just need to bind these to a TranslateTransform to move the yellow highlight. This diagram might help get your head around what's going on here.

Offset and Viewport Thumbnail

Finally, because we bound the width and height of the Rectangle (that is now painted with the content of the ScrollViewer) to the width and height of the ScrollViewer it's now exactly the same size. Not much of a thumbnail! That's why we placed it inside a Viewbox.

Essentially, a Viewbox will take it's contents and scale them up or down to fill the space consumed by the Viewbox itself. Job done.

This might not be the fastest most performant implementation that is possible but it's worked fine for my requirements. Of course, any use of a VisualBrush like this could have nasty implications on the performance of your application as it forces the render of parts of your UI that probably aren't currently being rendered. As always, your own mileage may vary.

Next, we'll look at making the thumbnail interactive and controllerising the implementation so it's even easier for us to reuse.

Tags: WPF

 
Josh Post By Josh Twist
11:30 AM
25 Nov 2008

» Next Post: Nesting Regions (or creating fractals) with Prism
« Previous Post: Windows Azure PoC for the RNLI

Comments are closed for this post.

Posted by Colin @ 30 Nov 2008 5:16 PM
Now thats cool!! :-)

Posted by Rick Eberle @ 02 Dec 2008 7:41 AM
Would love this if it could be a popup that came up when Mouseing over the scrollbar.

Posted by Josh @ 02 Dec 2008 10:28 AM
Hi Rick,

Stay tuned to the posts then! :)

Posted by Markenzie @ 20 Dec 2008 2:24 PM
Thanks a lot, Josh. I have been looking for this for days. The problem for me was I did not even know the right key words to use for the search. Thanks I am finally here. I am going to follow up with your December posts. Thanks man.

Posted by Rahul @ 01 Jan 2009 9:11 PM
Thats gr8. Could you plz. send the full code.
Thanks in advance

Posted by John @ 20 Feb 2009 2:25 PM
INCREDIBLE ! and so little code... wow

Posted by John Wakefield @ 14 Mar 2009 5:50 AM
Very cool - thanks for this...

Posted by Pardeep Bogra @ 10 Nov 2009 2:07 AM
Thanks a lot Josh.

I need to add one more functionality. I want that user can drag the rectangle to change the view on Scroll area. How we can do that.

Posted by josh @ 12 Nov 2009 12:20 AM
Hi Pardeep,

See this post: http://www.thejoyofcode.com/Making_the_ScrollViewerThumbnail_interactive.aspx

Josh

© 2005 - 2014 Josh Twist - All Rights Reserved.