Click here to Skip to main content
Click here to Skip to main content

C++/CLI Image Viewer with zoom and scroll

, 7 Feb 2012
Rate this:
Please Sign up or sign in to vote.
User Control with scrolling and zooming done in C++/CLI
CLI_JPGViewer_SourceCode

Introduction

This is a project where I had to manage a very, very large image without loading all the info to generate that image in memory.
It's like document programs work. In excel, you don't have an image of 1,000,000 x 20,000 cells.
The program goes to the file and "build" the screen image as you move. I had to maintain 2 versions (ATL and later .NET). I decided to use C++/CLI for the .NET version.

I adapted the code to load an image. It is difficult to work with scrollbars in .NET user controls. Specially if you want the control to work the way you want it to work.

This project includes:

Creating a .NET user control in C++/CLI

Adding classes

There are two different type of classes, native and managed classes. Managed classes are defined by "ref". For example:
private ref class CImgManager
Managed classes have references (with the ^ symbol), use the "gcnew" to create a new instance and can't have native classes as variables in the class declaration.
In this project, there are 3 managed classes:
  • CImgManager: Contains the original image, the rectangle where the image is drawn, and the paint event
  • CImgParams: Contains the zoom and image parameters (height, width, coordinates, etc)
  • CScrollBarsMan: Contains code to manage scrollbars
For some reason, even after adding using namespace System::Drawing, declaring a Rectangle requires all the path. I think it's because there is a name conflict with Rectangle (Windows).

Adding events

There are two events that the user control can fire:
  • ImgOnMoveCoords: So that when the user moves the cursor on the control, can see the image coordinates.
  • ImgOnScroll: This is used to refresh the zoom image with the rectangle. It fires when the zoom or coordinates change
The code to declare and use the events is in CLI_JPGViewerControl.
To declare events you need delegates:
public delegate void _ImgOnMoveCoords(int X, int Y);
public delegate void _ImgOnScroll();
And then define the events:
event _ImgOnMoveCoords^ ImgOnMoveCoords;
event _ImgOnScroll^     ImgOnScroll;
And finally, fire them when necessary.

Message processing

There is usually no need to override the default message processing method of the user control, but in this case I need to get the scroll events:
protected: virtual void WndProc(Message% m) override
{
    bool change = scrollman->MsgFromScroll(m);
    if (m.Msg == WM_VSCROLL || m.Msg == WM_HSCROLL)
        ImgOnScroll();
        UserControl::WndProc( m );
}
The other methods to process mouse, resize or paint methods must be added to the events at InitializeComponent() by using the corresponding handlers:
this->Paint      += gcnew System::Windows::Forms::PaintEventHandler(this, &CLI_JPGViewerControl::CLI_JPGViewerControl_Paint);
this->Resize     += gcnew System::EventHandler(this, &CLI_JPGViewerControl::CLI_JPGViewerControl_Resize);
this->MouseMove  += gcnew System::Windows::Forms::MouseEventHandler(this, &CLI_JPGViewerControl::CLI_JPGViewerControl_MouseMove);
this->MouseWheel += gcnew System::Windows::Forms::MouseEventHandler(this, &CLI_JPGViewerControl::CLI_JPGViewerControl_MouseWheel);

Images, bitmaps and drawing

The managed class that deals with the image and drawing is CImgManager:

Image^ ImgOrig This is the image object where the image file is loaded
Bitmap^ BmpRect This is the bitmap where the image is copied, adjusting width or height depending of the width or height of the user control
int ImgLeft, ImgTop; This variables represent where the image starts in the bitmap. One of them will always be zero.
Graphics^ grp; This is the graphics object of the user control for the painting event
System::Drawing::Rectangle^ rect; This is the rectangle of the usercontrol where the class draws the image
CImgParams^ imgparams; Reference to zoom functionality
Rectangle^ rdrawzoom; This is the rectangle with the coordinates of zoom to draw the blue rectangle
bool ShowRect; This is to indicate if the image is one that shows zoom (blue rectangle) or not
CImgManager() Inits ImgOrig and BmpRect as null, creates the only instance of rdrawzoom
void LoadImage(String^ strfilename); Loads the image from file and builds the proportional bitmap
void Paint(); This is called from the paint event of the user control. It paints the zoomed image in the user control rectangle
void MakeProportionalImage(); This builds a bitmap proportional to the user control rectangle in BmpRect
void RefreshSize(); Recreates the bitmap when the size of the user control changes
Point^ GetImgPointFromXY(Point^ pxy); Gets the corresponding image coordinates from mouse location in the user control. If the mouse is in the blank area it returns (0,0)
void Drawrectangle(); Draws the zoom blue rectangle, based on the coordinates of rdrawzoom
double Factor(int pWidth, int pHeight) Gets the relationship between the two parameters to compare user control area vs image proportions

Zooming

The managed class that deals with image zoom is CImgParams.
Al the variables and methods are referred to the bitmap (proportional) image, not the original image.

double Zoom; This is the variable for the class that is assigned when the user control zoom property changes
int ImgHeight, ImgWidth; These variables are the image height and width. They correspond to the altered image (proportional), that is, the bitmap
int ImgLeft, ImgTop; These variables are the top and left of the bitmap that indicate the begining of the image. They correspond to ImgLeft and ImgTop of CImgManager
int MinZoomX, MaxZoomX, MinZoomY, MaxZoomY; These are the coordinates of the zoom "square". They are related to the proportional bitmap (not the original image)
Point^ GetZoomMiddlePoint(); Returns the middle point of the zoom square. Useful to position the scrollbars
Size^ GetZoomSize(); Returns the size of the zoom square
Rectangle^ GetImageZoomRectangle(); Returns a rectangle corresponding to the square defined by the variables MinZoomX, MaxZoomX, MinZoomY, MaxZoomY
void DoZoom(); From the image coordinates, it assigns MinZoomX, MaxZoomX, MinZoomY, MaxZoomY from the zoom variable, centered
void DoZoomFromPoint(); The same as DoZoom, but after zooming, it moves the center of the square to where it was before
void Init(int ImgWidth, int ImgHeight); Initializes the variables to zoom 1 (including the square).

Scrollbars

The scrollbars are positioned and resized used mostly unmanaged code that calls windows APIs. This is mostly because the code is copied from ATL controls.
One important thing is that scrollbars don't get updated correctly when you change their size, event after the method Invalidate(), so a call to SetWindowPos must be done.
When setting the zoom in the user control:
Invalidate();
HWND pHwnd = (HWND)Handle.ToPointer();
SetWindowPos(pHwnd, 0,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
All the code to handle scrollbars is in the CScrollBarsMan:

UserControl^ usrctl; This is a reference to the user control. It is used to get or set the .NET scrollbars, and to get the handle of the window
CImgParams^ imgparams; This is a reference to the zooming functionality. In order to position scrollbars, it is necesary to know the zoom state and coordinates
bool UseScrollBars; If scrollbars appear (visible)
void SetScrollBars(); Sets the scrollbars size and visibility. It is called when the user zooms in or out (not when he moves)
void SetScrollBarsPos; Positions the scrollbars in the center. This method is not used.
void PosScroll(int horzvert, int pos); Positions the scrollbar in a point. It is only called by SetScrollBarsPos, which is not used.
Point^ GetZoomRelToTotal(); Returns the left top point of the zoom square. This method is not used
void RefreshParamsFromScroll(); Refresh the zoom rectangle based on scroll input
int GetScrollPosition(int horzvert); horzvert); Gets the absolute scroll position
void UpdateScrollsFromControl(); Updates the scrollbar positions (both) base on the central point of the zoom rectangle.
bool MsgFromScroll(Message% m); This is the call from the WndProc of the user control, and manages the message.
template<unsigned Min, unsigned Max, unsigned LineP,
unsigned LineM, unsigned PageP, unsigned PageM>
void ProcMsg( int PosInfo, int horzvert, UINT nSBCode);
This method handles the message. As vertical and horizontal movements are similar, a template is used to not repeat code.
int PosX, PosY; Hold the last values of the scrollbars to see if a change is made.
Rectangle^ rdrawzoom; This is the rectangle of the zoom. It is used to send to another control (the one with the blue box) the coordinates to draw it. The blue box "changes" when the user scrolls or zooms.
void UpdateRectZoom(); This is the method that updates the rectangle zoom.
template<typename T> void
SetScrollUsrCtrl(T^ scroll, int Total, int Change);
Method to set the scrollbars after a zoom change.
void UpdateScrolls(int horzvert, int posmiddle, int PosInfo); Updates a single scroll from zoom (called from UpdateScrollsFromControl).

To do

  • Remove bitmap and allow loading original image only.
  • Improve zoom.
  • Add pan and rectangles.

License

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

Share

About the Author

ErnestoNet
Software Developer
Argentina Argentina
System developer from Argentina.
 
Programmed in VB 5,6,.NET, C#, Java, PL-SQL, Transac-SQL, C, C++ and even some "calculator" language.
 
Love to build small, useful applications.
Usually building big and complicated apps based on solid, reliable components.
 
Hobbies: reading, photography, chess, paddle, running.

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 7 Feb 2012
Article Copyright 2012 by ErnestoNet
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid