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:

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'):

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

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:

<ViewboxDataContext="{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?

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.

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.

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.

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