.
In Depth Banner
Skip Navigation LinksWelcome > In Depth articles > Canvas (Part 2)

Select your preferred language

So, you've seen how to create the beginnings of a canvas class that added panning, zooming and selection capabilities that aren't built in to the Windows Forms system as standard. Now, you'll see how to add the capability to zoom in or out of a document. Previously, you saw how the transform applied to the Graphics used to draw the document was used to both pan the document in the case that the viewport was smaller than the page or, if the page was smaller than the viewing area, to offset the origin of the page to make a neat WYSIWYG representation of the document as a whole.

Figure 1 shows how the origin is offset for the two different cases. Example A shows the origin offset by the amount dictated by the scroll bars that supply a negative value which moves the page left or up to enable you to see a different portion of the document. Example B shows the origin moved to a position that lines up the center of the document with the center of the viewing area.

Figure 1: Translating the origin of the document.

Building on the principle of using transformations to offset the origin, lets look now at the ways a transform can be used to affect the apparent size of objects on screen.

Scaling the transform

The matrix class provided by System.Drawing enables you to perform transformations on coordinates. This means that you can subject any x,y value such as the position of an output pixel or the contents of a point to the transformation and the resulting position used to provide a new coordinate. When we zoom into an image, we effectively expand or multiply the coordinates pushing them further apart. Zooming out is performed by dividing the coordinate positions by some factor, thereby pushing them together on the screen. The code shown in listing 1 demonstrates how setting an explicit transform can change the size and position of the output pixels.

    private void DrawRect(Graphics g, Color c)

    {

      SolidBrush sb=new SolidBrush(c);

      g.FillRectangle(sb,10,10,100,100);

      sb.Dispose();

    }

 

    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

    {

      Matrix mx=new Matrix(2f,0,0,2f,0,0);

      e.Graphics.Transform=mx;

      DrawRect(e.Graphics,Color.Red);

      mx=new Matrix(1f,0,0,1f,0,0);

      e.Graphics.Transform=mx;

      DrawRect(e.Graphics,Color.Green);

      mx=new Matrix(0.5f,0,0,0.5f,0,0);

      e.Graphics.Transform=mx;

      DrawRect(e.Graphics,Color.Blue);

    }

Figure 2 shows the output from this code. You can see that regardless of the fact that the rectangle was drawn in exactly the same position and size, the pixels actually output were moved and scaled with respect to the origin.

Figure 2: Scaled rectangles.

To set the zoom level of the canvas class then, we just need to provide some method of setting the multiplying factor for the coordinates and apply them before we draw the page. Unfortunately though, the equation isn't as simple as that because the system also needs to cope with scrolling of the page, especially now that the apparent size of the page could be many times that of the original. For example, a 1024 by 768 document viewed at 200 percent will require 2048 by 1536 to display it. At 200 percent then, the scroll bars, that know nothing of the transform applied to the Graphics object, need to be able to range over the apparent virtual size of the page. As the zoom changes then so should the minimum scroll size of the page and the transform that keeps the origin in position relative to scrollbars or viewport.

As well as adding the property that sets the zoom level, the method that calculates scroll sizes and the paint routine will require extensive changes. As before, to provide the maximum possibilities for the user, we should also ensure that the control behaves well at design time and can inform the application when things change so an event should be added which fires when the zoom level changes.

Taking this laundry list one item at a time, the code is modified as follows.

Adding the zoom property.

A private field and public accessor are added:

    private float _zoom=1.0f;

 

    [Category("Behavior"),

    Description("The multiplying factor for the zoom level"),

    DefaultValue(1.0f)]

    public float Zoom

    {

      get{return _zoom;}

      set{

        _zoom = value;

        //A zoom may not be negative

        if(_zoom<0)

          _zoom=Math.Abs(_zoom);

        //a zoom may be very small but never zero.

        if(_zoom==0)

          _zoom=0.000001f;

        //The scrollbars should be recalculated

        CalcScroll();

        //and the host application code informed if needs be

        OnZoomChanged(EventArgs.Empty);

      }

    }

 

Note how the zoom level is always kept positive and never allowed to be zero.

Scrollbar calculations are modified.

You may remember that originally, the CalcScroll method looked like this;

    void CalcScroll()

    {

      Size cs = new Size(this._pageSize.Width,this._pageSize.Height);

      this.AutoScrollMinSize=cs;

      Invalidate();

    }

To cope with the zoom factor, the page-size in both dimensions must be multiplied by the zoom factor. The updated listing below shows this.

    void CalcScroll()

    {

      Size cs = new Size((int)(this._pageSize.Width*_zoom),(int)(this._pageSize.Height*_zoom));

      this.AutoScrollMinSize=cs;

      Invalidate();

    }

 Adding the property change event.

    public event EventHandler ZoomChanged;

 

    protected virtual void OnZoomChanged(EventArgs e)

    {

      if(this.ZoomChanged!=null)

        ZoomChanged(this,e);

      Invalidate();

    }

Modifying the OnPaint method.

This is the method in which the most extensive changes have to be made. This method sets up the graphics transform so that all subsequent drawing operations can be performed as though they were at a one to one ratio but the display shows us the correct zoom and pan.

Beginning with the basic matrix settings, this time, instead of starting out with the identity matrix as was used in the previous page, the zoom factor is used to symmetrically scale the output;

Matrix mx=new Matrix(_zoom,0,0,_zoom,0,0);

The size of the page, used to calculate which if any of the scrollbars should be seen and their offsets must be modified to take the zoom level into account. For this adjustment, the reciprocal of the zoom is used.

Size s=new Size((int)(this.ClientSize.Width*(1f/_zoom)),(int)(this.ClientSize.Height*(1f/_zoom)));

The Size structure 's' now contains the apparent virtual page size which can be used to move the origin of the page into the correct position, taking into account the scrollbar offsets if any.

if(s.Width>PageSize.Width)

  mx.Translate((s.Width/2)-(_pageSize.Width/2),0);

else

  mx.Translate((float)this.AutoScrollPosition.X*(1f/_zoom),0);

 

if(s.Height>PageSize.Height)

  mx.Translate(0,(s.Height/2)-(this._pageSize.Height/2)+(this.AutoScrollPosition.Y));

else

  mx.Translate(0,(float)this.AutoScrollPosition.Y*(1f/_zoom));

The Matrix mx now contains the transformation used to draw the rest of the canvas UI and the graphics which comprise the document.

 Testing the Canvas control.

The added dimension of zooming requires a zoom selection so to test the Canvas control this time, a TrackBar control has been added to the main display that enables you to zoom from a 10,000 foot view to around 250 percent of full size.

Zooming is handled very simply. The trackbar is set for a minimum of zero and a maximum of 100 and the Scroll event is handled by the code shown below.

    private void trackBar1_Scroll(object sender, System.EventArgs e)

    {

      this.canvas1.Zoom=0.025f*this.trackBar1.Value;

    }

 

Figure 3 shows the Canvas control at work in the test program.

 

Figure 3: infinitely scalable zooming with the Canvas control.

 

Summary

 

So now you've seen how to apply a transformation to scale the output of the page and how that scaling modifies the behaviour of the scrollbars. Futhermore, you've seen how the virtual page relates to the actual page. This will be important for the final installment, adding mouse handling to the Canvas control that relates the mouse cursor to the document regardless of zoom or scroll position.

 


CS Version VB Version


Return to page 1 or Proceed to the next page

 

Copyright © Bob Powell 2000-.  All rights reserved.