在处理大型图像时,如何避免将所有信息加载到内存中,是软件开发中的一个重要问题。类似于文档程序的工作方式,例如Excel,它不会在内存中创建一个1,000,000 x 20,000单元格的图像。程序会根据需要从文件中构建屏幕图像。在本项目中,需要维护两个版本(ATL和.NET),并决定使用C++/CLI来实现.NET版本。
在.NET中创建用户控件涉及到两种类型的类:原生类和托管类。托管类通过"ref"关键字定义。例如:
private ref class CImgManager {
// 类体
};
托管类使用"^"符号引用,使用"gcnew"创建新实例,并且不能在类声明中包含原生类变量。在本项目中,定义了三个托管类:
用户控件可以触发两个事件:
在CLI_JPGViewerControl中声明和使用事件:
public delegate void _ImgOnMoveCoords(int X, int Y);
public delegate void _ImgOnScroll();
event _ImgOnMoveCoords^ ImgOnMoveCoords;
event _ImgOnScroll^ ImgOnScroll;
并在必要时触发它们。
通常不需要重写用户控件的默认消息处理方法,但在本项目中,需要获取滚动事件:
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 );
}
其他处理鼠标、调整大小或绘制方法必须在InitializeComponent()中通过相应的处理程序添加到事件:
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);
处理图像和绘图的托管类是CImgManager:
private ref class CImgManager {
Image^ ImgOrig;
Bitmap^ BmpRect;
int ImgLeft, ImgTop;
Graphics^ grp;
System::Drawing::Rectangle^ rect;
CImgParams^ imgparams;
Rectangle^ rdrawzoom;
bool ShowRect;
};
CImgManager类初始化ImgOrig和BmpRect为null,创建唯一的rdrawzoom实例。它包含加载图像、绘制、制作比例图像、刷新大小、获取图像点、绘制矩形和获取因子的方法。
处理图像缩放的托管类是CImgParams。所有变量和方法都引用位图(比例)图像,而不是原始图像。
private ref class CImgParams {
double Zoom;
int ImgHeight, ImgWidth;
int ImgLeft, ImgTop;
int MinZoomX, MaxZoomX, MinZoomY, MaxZoomY;
Point^ GetZoomMiddlePoint();
Size^ GetZoomSize();
Rectangle^ GetImageZoomRectangle();
void DoZoom();
void DoZoomFromPoint();
void Init(int ImgWidth, int ImgHeight);
};
CImgParams类包含缩放变量、图像高度和宽度、图像顶部和左侧、缩放“正方形”的坐标、获取缩放中间点、获取缩放大小、获取图像缩放矩形、执行缩放和初始化的方法。
private ref class CScrollBarsMan {
UserControl^ usrctl;
CImgParams^ imgparams;
bool UseScrollBars;
void SetScrollBars();
void SetScrollBarsPos;
void PosScroll(int horzvert, int pos);
Point^ GetZoomRelToTotal();
void RefreshParamsFromScroll();
int GetScrollPosition(int horzvert);
void UpdateScrollsFromControl();
bool MsgFromScroll(Message% m);
template
void ProcMsg( int PosInfo, int horzvert, UINT nSBCode);
int PosX, PosY;
Rectangle^ rdrawzoom;
void UpdateRectZoom();
template void SetScrollUsrCtrl(T^ scroll, int Total, int Change);
void UpdateScrolls(int horzvert, int posmiddle, int PosInfo);
};