This content has moved - please find it at https://devblog.cyotek.com.

Although these pages remain accessible, some content may not display correctly in future as the new blog evolves.

Visit https://devblog.cyotek.com.

Zooming to fit a region in a ScrollableControl

I suspect the titles of these last pair of articles are a touch misleading as they talk about extended zoom operations without actually describing a zoom process (as this is already part of other ImageBox articles. Unfortunately I can't really think of better titles and the theory is generic enough to be applied to any type of zooming, not just the ImageBox.

My previous article touched on zooming in a ScrollableControl while keeping the content correctly aligned to a fixed point, usually the mouse position prior to the zoom. This article expands on that with another new feature in the upcoming ImageBox update, zooming to a given region. You've probably seen this behaviour in other paint programs, where you select a Zoom tool, draw a rectangle, and the document is automatically zoomed to fit.

Again, this is actually quite simple to achieve and this blog post is pretty much just a reference to me of how I did this so I don't forget next time I want something similar! We'll start off by quickly describing a method that I forgot to include in the previous post - CenterAt. This simple method centers the view port on the given document location. This use useful for when you want to use ScrollTo without providing a relative offset... or for centering the display on a selection rectangle.

public virtual void CenterAt(Point imageLocation)
{
  this.ScrollTo(imageLocation, new Point(this.ClientSize.Width / 2, this.ClientSize.Height / 2));
}

Very straightforward, it simply calls ScrollTo, using the center of the control as the offset. Now for the actual ZoomToRegion method:

public virtual void ZoomToRegion(RectangleF rectangle)
{
  double ratioX;
  double ratioY;
  int cx;
  int cy;

  ratioX = this.ClientSize.Width / rectangle.Width;
  ratioY = this.ClientSize.Height / rectangle.Height;
  cx = (int)(rectangle.X + (rectangle.Width / 2));
  cy = (int)(rectangle.Y + (rectangle.Height / 2));

  this.Zoom = (int)(Math.Min(ratioX, ratioY) * 100);
  this.CenterAt(new Point(cx, cy));
}

This accepts a RectangleF structure (you could use a Rectangle, but then if you attempt to draw selection regions on a zoomed out document, rounding from float to int would render your selections useless), and it then calculates a new zoom factor and offset.

  1. First, the ration of the width and height of the region against the width and height of the view port is calculated
  2. We use the smallest ratio (to ensure if that everything you selected appears when the zoom is applied) to calculate the new zom level
  3. After this, we define the center of the rectangle
  4. With all the calculations done, we set the zoom level of the control
  5. And finally, we call our new CenterAt method to center the view port on the center of the source region

In the actual ImageBox control, a new SelectionMode has been added - Zoom. As the name somewhat logically suggests, when this mode is active, after the user draws a selection rectangle, the control then zooms to match the rectangle they have drawn. This updated mode is called from the OnSelected method, as follows:

if (this.SelectionRegion != null && this.SelectionRegion.Width > ImageBox.SelectionDeadZone && this.SelectionRegion.Height > ImageBox.SelectionDeadZone)
{
  this.ZoomToRegion(this.SelectionRegion);
  this.SelectionRegion = RectangleF.Empty;
}

The mysterious ImageBox.SelectionDeadZone field is a constant currently set to 5, and basically ensures the user selects a valid rectangle first - when I was testing the first iteration of this code, having the mouse wobble as you clicked the control was enough to generate a 1x1 rectangle, definitely not a good user experience!

The only disadvantage is this functionality only lends itself to zooming it, and not out.

Update History

  • 2012-08-30 - First published
  • 2020-11-21 - Updated formatting

About The Author

Gravatar

The founder of Cyotek, Richard enjoys creating new blog content for the site. Much more though, he likes to develop programs, and can often found writing reams of code. A long term gamer, he has aspirations in one day creating an epic video game. Until that time, he is mostly content with adding new bugs to WebCopy and the other Cyotek products.

Leave a Comment

While we appreciate comments from our users, please follow our posting guidelines. Have you tried the Cyotek Forums for support from Cyotek and the community?

Styling with Markdown is supported