2012-01-30 84 views
4

我必須在Windows窗體應用程序中顯示大圖像。用戶應該有可能標記圖像的一個區域,然後應該像下面所示的例子那樣進行縮放。使用Windows窗體縮放大圖片

Zoom illustration

如前面所提到的圖像將是相當大的,所以我的問題是:是否有可能與默認PictureBox控制做到這一點還是我使用第三方控制的更好?如果是這樣,請推薦一個包含提供這些功能的控件的庫。

正如所承諾的,這裏是我做了控制的來源:

/// <summary> 
/// A panel used to display an image and zoom into areas of the displayed 
/// image. 
/// </summary> 
public sealed class PictureZoomPanel : Panel 
{ 
    // The image to dispay, set in the Image property 
    private Image _image; 
    // The current zoom factor 
    private float _zoom = 1; 
    // The zoom rectangle on the panel. 
    private Rectangle _panelZoomRect; 
    // _panelZoomRect on the actual image 
    private Rectangle? _imageZoomRect; 
    // Used in the mouse event handlers 
    private bool _mouseDown; 
    // The pen used to draw the zoom rectangle 
    private Pen _zoomPen; 

    /// <summary> 
    /// Create a new <see cref="PictureZoomPanel"/> 
    /// </summary> 
    public PictureZoomPanel() 
    { 
     // To prevent flickering 
     DoubleBuffered = true; 
     // To make resizing smoother 
     ResizeRedraw = true; 
     // Set default zoom pen 
     ZoomPen = null; 
    } 

    /// <summary> 
    /// The image to be displayed 
    /// </summary> 
    [Category("Appearance"), 
    Description("The image to be displayed.")] 
    public Image Image 
    { 
     get { return _image; } 
     set 
     { 
      _image = value; 
      ZoomToFit(); 
     } 
    } 

    /// <summary> 
    /// The pen used to draw the zoom rectangle. 
    /// </summary> 
    [Category("Appearance"), 
    Description("The pen used to draw the zoom rectangle.")] 
    public Pen ZoomPen 
    { 
     get { return _zoomPen; } 
     set { 
      _zoomPen = value ?? new Pen(Color.Green, 2); 
     } 
    } 

    /// <summary> 
    /// Sets the zoom to a value where the whole image is visible. 
    /// </summary> 
    public void ZoomToFit() 
    { 
     _imageZoomRect = null; 
     _mouseDown = false; 
     _zoom = 1; 

     // If no image is present, there is nothing further to do 
     if (_image == null) 
      return; 

     var widthZoom = (float) Width/_image.Width; 
     var heightZoom = (float) Height/_image.Height; 

     // Make sure the whole image is visible 
     _zoom = widthZoom < heightZoom ? widthZoom : heightZoom; 

     // Force redraw 
     Invalidate(); 
    } 

    protected override void OnMouseDown(MouseEventArgs e) 
    { 
     if (_image == null) 
      return; 

     _mouseDown = true; 
     _panelZoomRect = new Rectangle(e.X, e.Y, 0, 0); 
    } 

    protected override void OnMouseUp(MouseEventArgs e) 
    { 
     if (_image == null || !_mouseDown) 
      return; 

     _mouseDown = false; 

     // Without this, doubling clicking the control would cause zoom 
     if (_panelZoomRect.Height == 0 || _panelZoomRect.Width == 0) 
      return; 

     // Tell the paint method to zoom 
     _imageZoomRect = CalculateImageZoomRectangle(); 
     _zoom = RecalculateZoom(); 
    } 

    protected override void OnMouseMove(MouseEventArgs e) 
    { 
     if (_image == null) 
      return; 

     // This makes sure that the left mouse button is pressed. 
     if (e.Button == MouseButtons.Left) 
     { 
      // Draws the rectangle as the mouse moves 
      _panelZoomRect = new Rectangle(
       _panelZoomRect.Left, 
       _panelZoomRect.Top, 
       e.X - _panelZoomRect.Left, 
       e.Y - _panelZoomRect.Top); 
     } 

     // Force redraw to make sure the zoomRegion is painted 
     Invalidate(); 
    } 

    private Rectangle CalculateImageZoomRectangle() 
    { 
     // Calculate all the coordinates to required to transform 
     var topLeft = new Point(_panelZoomRect.X, 
      _panelZoomRect.Y); 
     var topRight = new Point(_panelZoomRect.X + _panelZoomRect.Width, 
      _panelZoomRect.Y); 
     var bottomLeft = new Point(_panelZoomRect.X, 
      _panelZoomRect.Y - _panelZoomRect.Height); 
     var bottomRight = new Point(_panelZoomRect.X + _panelZoomRect.Height, 
      _panelZoomRect.Y - _panelZoomRect.Height); 

     var points = new [] { topLeft, topRight, bottomLeft, bottomRight }; 

     // Converts the points from panel to image position 
     var mx = new Matrix(_zoom, 0, 0, _zoom, 0, 0); 
     mx.Invert(); 
     mx.TransformPoints(points); 

     var rectangleWidth = points[1].X - points[0].X; 
     var rectangleHeight = points[0].Y - points[2].Y; 

     // _imageZoom != null, means that we are zooming in on an 
     // already zoomed in image. We must add the original values 
     // to zoom in deeper 
     return _imageZoomRect == null 
      ? new Rectangle(points[0].X, 
       points[0].Y, 
       rectangleWidth, 
       rectangleHeight) 
      : new Rectangle(points[0].X + _imageZoomRect.Value.X, 
       points[0].Y + _imageZoomRect.Value.Y, 
       rectangleWidth, 
       rectangleHeight); 
    } 

    private float RecalculateZoom() 
    { 
     if (!_imageZoomRect.HasValue) 
      return _zoom; 

     var widthZoom = (float)Width/_imageZoomRect.Value.Width; 
     var heightZoom = (float)Height/_imageZoomRect.Value.Height; 

     return widthZoom < heightZoom ? widthZoom : heightZoom; 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     if (_image == null) 
     { 
      OnPaintBackground(e); 
      return; 
     } 

     e.Graphics.Transform = new Matrix(_zoom, 0, 0, _zoom, 0, 0); 

     // Turn of interpolation when zoomed 
     e.Graphics.InterpolationMode = _imageZoomRect != null 
      ? InterpolationMode.NearestNeighbor 
      : InterpolationMode.Default; 

     DrawImage(e); 

     if (_mouseDown) 
      DrawZoomRectangle(e); 

     base.OnPaint(e); 
    } 

    private void DrawImage(PaintEventArgs e) 
    { 
     var destRec = !_imageZoomRect.HasValue 
      ? new Rectangle(0, 0, _image.Width, _image.Height) 
      : new Rectangle(0, 0, _imageZoomRect.Value.Width, 
       _imageZoomRect.Value.Height); 

     var sourceRec = !_imageZoomRect.HasValue 
      ? new Rectangle(0, 0, _image.Width, _image.Height) 
      : _imageZoomRect.Value; 

     e.Graphics.DrawImage(_image, destRec, 
      sourceRec.Location.X, sourceRec.Location.Y, 
      sourceRec.Width, sourceRec.Height, 
      GraphicsUnit.Pixel); 
    } 

    private void DrawZoomRectangle(PaintEventArgs e) 
    { 
     e.Graphics.Transform = new Matrix(); 
     e.Graphics.DrawRectangle(_zoomPen, _panelZoomRect); 
    } 
} 

回答

2

可以實現通過平移圖形對象縮放。我認爲double buffered Panel是更好的選擇工具,而不是使用PictureBox。

您處理繪畫事件,並從那裏調整它的矩陣表示。此外,您需要調整AutoScrollMinSize屬性以使滾動條表示縮放圖像的正確範圍。

簡單的例子:

Bitmap bmp = new Bitmap(@"c:\myimage.png"); 
int zoom = 2; 

private void Form1_Load(object sender, EventArgs e) { 
    panel1.AutoScrollMinSize = new Size(bmp.Width * zoom, bmp.Height * zoom); 
} 

private void panel1_Paint(object sender, PaintEventArgs e) { 
    using (Matrix m = new Matrix(zoom, 0, 0, zoom, 0, 0)) { 
    m.Translate(panel1.AutoScrollPosition.X/zoom, panel1.AutoScrollPosition.Y/zoom); 
    e.Graphics.Transform = m; 
    e.Graphics.DrawImage(bmp, new Point(0, 0)); 
    } 
} 

本文由鮑勃·鮑威爾是一個很好的文章閱讀:Zoom and pan a picture.

他提供這種方法用於縮放圖像的tracking the mouse movement

protected Point BacktrackMouse(MouseEventArgs e) 
{ 
    //Creates the drawing matrix with the right zoom; 
    Matrix mx=new Matrix(_zoom, 0, 0, _zoom, 0, 0); 
    //pans it according to the scroll bars 
    mx.Translate(this.AutoScrollPosition.X * (1.0f/_zoom), this.AutoScrollPosition.Y * (1.0f/_zoom)); 
    //inverts it 
    mx.Invert(); 
    //uses it to transform the current mouse position 
    Point[] pa=new Point[]{new Point(e.X,e.Y)}; 
    mx.TransformPoints(pa); 
    return pa[0]; 
} 
+0

非常感謝。我差不多完成了,完成後會在這裏發佈完整的代碼。 – xsl 2012-01-31 15:23:47