Click here to Skip to main content
15,883,901 members
Articles / Programming Languages / C#

Creating a Scrollable and Zoomable Image Viewer in C#: Part 1

Rate me:
Please Sign up or sign in to vote.
4.00/5 (1 vote)
26 Jan 2012CPOL3 min read 13.9K   3  
First part in a series of articles that will result in a component for viewing an image

This is the first part in a series of articles that will result in a component for viewing an image. The final component will support zooming and scrolling.

In this first part, we're going to create a basic image viewer, without the scrolling and zooming. Rather than having a plain background however, we're going to create a two tone checker box effect which is often used for showing transparent images. We'll also allow this to be disabled and a solid colour used instead.

Creating the Component

The component inherits from Control rather than something like PictureBox or Panel as we want to provide a lot of our own behaviour.

The first thing we'll do is override some properties - to hide the ones we won't be using such as Text and Font, and to modify others, such as making AutoSize visible, and changing the default value of BackColor.

Next is to add some new properties. We'll create the following properties and respective change events:

  • BorderStyle - A standard border style
  • GridCellSize - The basic cell size
  • GridColor and GridColorAlternate - The colors used to create the checkerboard style background
  • GridScale - A property for scaling the GridCellSize for user interface options
  • Image - The image to be displayed
  • ShowGrid - Flag to determine if the checkerboard background should be displayed

As we are offering auto size support, we also override some existing events so we can resize when certain actions occur, such as changing the control's padding or parent.

Setting Control Styles

As well as setting up default property values, the component's constructor also adjusts several control styles.

  • AllPaintingInWmPaint - We don't need a separate OnPaintBackground and OnPaint mechanism, OnPaint will do fine.
  • UserPaint - As we are doing entirely our own painting, we disable the base Control's painting.
  • OptimizedDoubleBuffer - Double buffering means the painting will occur in a memory buffer before being transferred to the screen, reducing flicker.
  • ResizeRedraw - Automatically redraw the component if it is resized.
  • Selectable - We disable this flag as we don't want the control to be receiving focus.
C#
public ImageBox()
{
  InitializeComponent();

  this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | 
                ControlStyles.OptimizedDoubleBuffer| ControlStyles.ResizeRedraw, true);
  this.SetStyle(ControlStyles.Selectable, false);
  this.UpdateStyles();

  this.BackColor = Color.White;
  this.TabStop = false;
  this.AutoSize = true;
  this.GridScale = ImageBoxGridScale.Small;
  this.ShowGrid = true;
  this.GridColor = Color.Gainsboro;
  this.GridColorAlternate = Color.White;
  this.GridCellSize = 8;
  this.BorderStyle = BorderStyle.FixedSingle;
}

Creating the Background

The CreateGridTileImage method creates a tile of a 2x2 grid using many of the properties listed above which is then tiled across the background of the control.

C#
protected virtual Bitmap CreateGridTileImage(int cellSize, Color firstColor, Color secondColor)
{
  Bitmap result;
  int width;
  int height;
  float scale;

  // rescale the cell size
  switch (this.GridScale)
  {
    case ImageBoxGridScale.Medium:
      scale = 1.5F;
      break;
    case ImageBoxGridScale.Large:
      scale = 2;
      break;
    default:
      scale = 1;
      break;
  }

  cellSize = (int)(cellSize * scale);

  // draw the tile
  width = cellSize * 2;
  height = cellSize * 2;
  result = new Bitmap(width, height);
  using (Graphics g = Graphics.FromImage(result))
  {
    using (SolidBrush brush = new SolidBrush(firstColor))
      g.FillRectangle(brush, new Rectangle(0, 0, width, height));

    using (SolidBrush brush = new SolidBrush(secondColor))
    {
      g.FillRectangle(brush, new Rectangle(0, 0, cellSize, cellSize));
      g.FillRectangle(brush, new Rectangle(cellSize, cellSize, cellSize, cellSize));
    }
  }

  return result;
}

Painting the Control

As described above, we've disabled all default painting, so we simply need to override OnPaint and do our custom painting here.

C#
protected override void OnPaint(PaintEventArgs e)
{
  if (_gridTile != null && this.ShowGrid)
  {
    // draw the background
    for (int x = 0; x < this.ClientSize.Width; x += _gridTile.Size.Width)
    {
      for (int y = 0; y < this.ClientSize.Height; y += _gridTile.Size.Height)
        e.Graphics.DrawImageUnscaled(_gridTile, x, y);
    }
  }
  else
  {
    using (SolidBrush brush = new SolidBrush(this.BackColor))
      e.Graphics.FillRectangle(brush, this.ClientRectangle);
  }

  // draw the image
  if (this.Image != null)
  {
    e.Graphics.DrawImageUnscaled(this.Image, new Point(this.Padding.Left + 
      this.GetBorderOffset(), this.Padding.Top + this.GetBorderOffset()));
  }

  // 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;
  }
}

First, we either draw a solid background using the BackColor property if ShowGrid is false, otherwise we tile the grid image created earlier.

Next, we draw the actual image, if one has been set. The image is offset based on the border style and padding.

Finally, we draw the border style to ensure it appears on top of the image if autosize is disabled and the control is too small.

Sample Project

You can download the first sample project from the link at the top of this article. The next article in the series will look at implementing scrolling for when the image is larger than the display area of the 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 --