Click here to Skip to main content
15,892,927 members
Articles / Programming Languages / C#

Creating a Scrollable and Zoomable Image Viewer in C# - Part 2

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
26 Jan 2012CPOL2 min read 17.9K   4  
In this post, we will update our component to support automatic scrolling when auto size is disabled and the image is larger than the client area of the control.

In the second part of our Creating a scrollable and zoomable image viewer in C# series, we will update our component to support automatic scrolling when auto size is disabled and the image is larger than the client area of the control.

Setting Up Auto Scrolling

Originally we inherited from Control, however this does not support automatic scrolling. Rather than reinventing the wheel at this point, we'll change the control to inherit from ScrollableControl instead. This will expose a number of new members, the ones we need are:

  • AutoScroll - Enables or disables automatic scrolling
  • AutoScrollMinSize - Specifies the minimum size before scrollbars appear
  • AutoScrollPosition - Specifies the current scroll position
  • OnScroll - Raised when the scroll position is changed

Using the above, we can now offer full scrolling.

As the control will take care of the scrolling behaviour, we don't want the AutoScrollMinSize property to be available, so we'll declare a new version of it and hide it with attributes.

C#
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), 
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Size AutoScrollMainSize
{
  get { return base.AutoScrollMinSize; }
  set { base.AutoScrollMinSize = value; }
}

Initially, the component only offered auto sizing and so we had defined an AdjustSize method which was called in response to various events and property changes. As we now need to set up the scrolling area if AutoScroll is enabled, this method is no longer as suitable. Instead, we add a pair of new methods, AdjustLayout and AdjustScrolling. Existing calls to AdjustSize are changed to call AdjustLayout instead, and this method now calls either AdjustScrolling or AdjustSize depending on the state of the AutoSize and AutoScroll properties.

The AdjustScrolling method is used to set the AutoScrollMainSize property. When this is correctly set, the ScrollableControl will automatically take care of displaying scrollbars.

C#
protected virtual void AdjustLayout()
{
  if (this.AutoSize)
    this.AdjustSize();
  else if (this.AutoScroll)
    this.AdjustScrolling();
}

protected virtual void AdjustScrolling()
{
  if (this.AutoScroll && this.Image != null)
    this.AutoScrollMinSize = this.Image.Size;
}

Reacting to Scroll Changes

By overriding the OnScroll event, we get notifications whenever the user scrolls the control, and can therefore redraw the image.

C#
protected override void OnScroll(ScrollEventArgs se)
{
  this.Invalidate();

  base.OnScroll(se);
}

Painting Adjustments

The initial version of our ImageBox tiled a bitmap across the client area of the control. In this new version, when we create the background tile, we now create a new TextureBrush. During drawing, we can call FillRectangle and pass in the new brush and it will be tiled for us.

Another shortcoming of the first version was the borders. These were painted last, so that if the image was larger than the controls client area, the image wouldn't be painted on top of the borders. Now, the borders are drawn first and a clip region applied to prevent any overlap.

Finally, of course, the position of the drawn image needs to reflect any scrollbar offset.

C#
protected override void OnPaint(PaintEventArgs e)
{
  int borderOffset;
  Rectangle innerRectangle;

  borderOffset = this.GetBorderOffset();

  if (borderOffset != 0)
  {
    // draw the borders
    switch (this.BorderStyle)
    {
      case BorderStyle.FixedSingle:
        ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, this.ForeColor, 
        ButtonBorderStyle.Solid);
        break;
      case BorderStyle.Fixed3D:
        ControlPaint.DrawBorder3D(e.Graphics, this.ClientRectangle, Border3DStyle.Sunken);
        break;
    }

    // clip the background so we don't overwrite the border
    innerRectangle = Rectangle.Inflate(this.ClientRectangle, -borderOffset, -borderOffset);
    e.Graphics.SetClip(innerRectangle);
  }
  else
    innerRectangle = this.ClientRectangle;

  // draw the background
  if (_texture != null && this.ShowGrid)
    e.Graphics.FillRectangle(_texture, innerRectangle);
  else
  {
    using (SolidBrush brush = new SolidBrush(this.BackColor))
      e.Graphics.FillRectangle(brush, innerRectangle);
  }

  // draw the image
  if (this.Image != null)
  {
    int left;
    int top;

    left = this.Padding.Left + borderOffset;
    top = this.Padding.Top + borderOffset;

    if (this.AutoScroll)
    {
      left += this.AutoScrollPosition.X;
      top += this.AutoScrollPosition.Y;
    }

    e.Graphics.DrawImageUnscaled(this.Image, new Point(left, top));
  }

  // reset the clipping
  if (borderOffset != 0)
    e.Graphics.ResetClip();
}

Sample Project

You can download the second sample project from the link below. The next article in the series will look at panning the image using the mouse within the client area of the image control.

Other Articles in this Series

License

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


Written By
Software Developer (Senior)
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --