清理硬盘时发现有几篇2011年整理的博客躺在那里,删了怪可惜的,发上来保存一下~~
年代久远,已经过时,仅作参考用途~~~
学了几天MiniGUI,也该写写程序了,突发奇想做了一个可以浏览照片的小程序。
程序实现的功能:
1. 在主窗口上显示图片
2. 敲键盘上的↑、↓、←、→还有两个按钮控件能够对图片进行循环切换
3. 透明按钮,鼠标移入按钮区域时显示按钮,移出区域则自动隐藏按钮
4. 标题栏显示图片的文件名
实现的效果图:
下一步需要改进的功能:
1. 透明按钮改为更友好的自定义的图片按钮
2. 切换图片时淡入淡出的效果
3. 支持字符串的文件名
一、 设计
1. 主窗口:
大小: 480 x 320
背景色:COLOR_lightwhite
标题栏: 无关闭按钮,背景色为黑色
2. 按钮(左、右):
大小: 30 x 30
背景色: 透明
左按钮位置: 离左边框5px、底部5px
右按钮位置: 离右边框5px、底部5px
3. 文件目录结构
二、 实现
1. 设置窗口属性:
给窗口结构体MAINWINCREATE赋值
void InitCreateInfo(MAINWINCREATE* CreateInfo,const char* caption,int width, int height) { CreateInfo->dwStyle = WS_VISIBLE | WS_CAPTION; CreateInfo->dwExStyle = WS_EX_NOCLOSEBOX;// | WS_EX_TROUNDCNS | WS_EX_BROUNDCNS; CreateInfo->spCaption = caption; CreateInfo->hCursor =GetSystemCursor(0); CreateInfo->hMenu = 0; CreateInfo->hIcon = 0; CreateInfo->MainWindowProc = photoViewWinProc; CreateInfo->lx = 0; CreateInfo->ty = 0; CreateInfo->rx = width; CreateInfo->by = height; CreateInfo->iBkColor = COLOR_lightwhite; CreateInfo->dwAddData = 0; CreateInfo->hHosting = HWND_DESKTOP; } |
2. 创建窗口实例:
HWND CreateWindowInstance(MAINWINCREATE* CreateInfo) { HWND hWnd = CreateMainWindow(CreateInfo); SetWindowElementAttr(hWnd, WE_BGCA_ACTIVE_CAPTION, COLOR_black); //修改标题栏背景颜色为黑色
return hWnd; } |
这里通过CreateMainWindow(CreateInfo)将创建的窗口赋给一个窗口句柄,然后返回给系统的窗口句柄,在返回前,可以通过这个句柄对创建的窗口的一些属性进行修改。如修改标题栏的背景颜色,字体颜色等。
3. 显示窗口:
调用ShowWindow(hMainWnd,SW_SHOWNORMAL)把创建的窗口显示到屏幕上,传入hMainWnd窗口实例和SW_SHOWNORMAL是显示的方式作为参数,有SW_SHOW、SW_HIDE、SW_SHOWNORMAL三种方式。SW_SHOWNORMAL说明显示窗口并置为顶层。
4. 捕获事件,处理窗口过程:
用窗口过程函数收到并处理窗口捕获的各种消息
static int photoViewWinProc(HWND hWnd,int message, WPARAM wParam, LPARAM lParam) { switch(message) { case MSG_MOUSEMOVE: {}//鼠标移动事件 case MSG_KEYDOWN:{} //键盘按下事件 case MSG_CREATE:{} //创建窗口 case MSG_PAINT:{} //绘制窗口 case MSG_DESTROY:{} //销毁控件 case MSG_CLOSE:{} //关闭窗口,退出程序 } return DefaultMainWinProc(hWnd, message, wParam, lParam); } |
4.1 创建窗口
创建窗口的时候创建了两个按钮控件btnLeft和btnRight,设置为子窗口,并且扩展风格为透明,长和宽都为30像素,父窗口为hMainWin,并设置按钮属性和点击事件回调函数为btnClickProc。
case MSG_CREATE: { btnLeft = CreateWindowEx(CTRL_BUTTON, //控件类名 "<", //控件caption名 WS_CHILD | WS_NONE, //类风格 WS_EX_TRANSPARENT, //扩展的类风格 BTN_LEFT, //控件ID 5, HEIGHT-55, //显示位置, x和y为左上角坐标 30, 30, //长和宽 hWnd, //父窗口句柄 0); //附加数据 SetNotificationCallback(btnLeft, btnClickProc); //设定通知消息回调函数
btnRight = CreateWindowEx(CTRL_BUTTON, ">", WS_CHILD | WS_NONE, WS_EX_TRANSPARENT, BTN_RIGHT, WIDTH-35, HEIGHT-55, 30, 30, hWnd, 0); SetNotificationCallback(btnRight, btnClickProc); return 0; } |
SetNotificationCallback设置控件的通知消息回调函数,当控件有通知消息时,就调用该函数,而不是发送通知消息到父窗口。新的应用程序应该尽量用这个函数来处理控件的通知消息。
MG_EXPORT NOTIFPROC GUIAPI SetNotificationCallback (HWND hwnd, NOTIFPROC notif_proc); |
4.2 获取图片路径
添加fileIO.c文件,专门用来编写文件操作相关的属性和方法。
const char * getFile(int n) { char * path = (char *)malloc(50*sizeof(char)); sprintf(path,"res/images/%d.jpg", n); return path; } |
文件夹中的图片文件都是以数字按顺序命名的jpg文件,所以传入一个数字n,然后调用sprintf函数处理路径后将其返回。
程序中设置了一个静态的全部变量index,用来标识当前的图片。然后通过index的加减操作,传入getFile函数,产生图片的路径。但是文件夹中图片有限,当index超过图片总数时就无法获得正确的图片路径,所以通过index对图片总数取余,保证每次都能取到图片,并且能够循环获取图片。
4.3 绘制窗口
从文件夹中读取图片,然后填充到主窗口中。填充的大小为窗口大小。
case MSG_PAINT: { if(LoadBitmap(HDC_SCREEN, &bmp, getFile(index%8))) return -1; hdc = BeginPaint(hWnd); FillBoxWithBitmap(hdc, 0, 0, WIDTH, HEIGHT, &bmp); EndPaint(hWnd, hdc); SetWindowCaption(hWnd, getFileName(index%8)); return 0; } |
MSG_PAINT消息在需要窗口重绘时发送到窗口过程。在MiniGUI中,所有绘图相关的函数都需要有一个设备上下文DC。当程序需要绘图时,它必须首先获取设备上下文句柄。绘制完毕后,它必须释放设备上下文句柄。程序必须在处理单个消息期间获取和释放设备上下文句柄,也就是说,如果程序在处理一条消息时获取了设备上下文句柄,它必须在处理完该消息退出窗口过程函数之前释放该设备上下文句柄。BeginPaint和EndPaint这两个函数只能在处理MSG_PAINT的消息中调用。
HDC GUIAPI BeginPaint(HWND hWnd); //获取设备上下文句柄 void GUIAPI EndPaint(HWND hWnd, HDC hdc); //释放设备上下文句柄 |
一般来说,MiniGUI程序的窗口过程会在如下情况收到一个MSG_PAINT消息:
l 用户移动窗口或显示窗口时,MiniGUI会向先前被隐藏的窗口发送MSG_PAINT消息
l 程序使用InvalidateRect函数来更新窗口的无效区域,将产生一个MSG_PAINT消息
l 程序调用UpdateWindow函数来重绘窗口
l 覆盖程序窗口的对话框或消息框被消除
l 下拉或弹出菜单被消除
通过MiniGUI的LoadBitmap函数组,可以将位图文件装载为MiniGUI设备相关的位图对象,即BITMAP对象。
MG_EXPORT int GUIAPI LoadBitmapFromFile (HDC hdc, PBITMAP pBitmap, const char* spFileName); #define LoadBitmap LoadBitmapFromFile |
LoadBitmap通过文件路径将位图文件装载进入BITMAP对象,成功返回0,失败返回失败代码。
#define ERR_BMP_OK 0 #define ERR_BMP_IMAGE_TYPE -1 #define ERR_BMP_UNKNOWN_TYPE -2 #define ERR_BMP_CANT_READ -3 #define ERR_BMP_CANT_SAVE -4 #define ERR_BMP_NOT_SUPPORTED -5 #define ERR_BMP_MEM -6 #define ERR_BMP_LOAD -7 #define ERR_BMP_FILEIO -8 #define ERR_BMP_OTHER -9 #define ERR_BMP_ERROR_SOURCE -10 |
FillBoxWithBitmap用设备相关位图对象填充矩形框。
FillBoxWithBitmapPart用设备相关位图对象的部分填充矩形框,也可以扩大或缩小位图。
MG_EXPORT BOOL GUIAPI FillBoxWithBitmap (HDC hdc, int x,int y, int w,int h, const BITMAP *bmp); MG_EXPORT BOOL GUIAPI FillBoxWithBitmapPart (HDC hdc, int x,int y, int w,int h, int bw,int bh, const BITMAP* bmp,int xo, int yo); |
4.4 关闭窗口退出程序
卸载位图,销毁主窗口,向主窗口发送退出消息。
case MSG_CLOSE: { UnloadBitmap(&bmp); DestroyMainWindow(hWnd); PostQuitMessage(hWnd); return 0; } |
应用程序调用DestroyMainWindow会发送一个MSG_DESTROY消息到窗口过程中,通知应用程序销毁主窗口,销毁主窗口前,应先销毁被托管主窗口的位图、字体、控件等资源。
case MSG_DESTROY: { DestroyAllControls(hWnd); return 0; } |
4.5 鼠标移动事件
先获取鼠标的坐标,如果移入按钮控件的区域,则显示按钮,移出则隐藏按钮。
case MSG_MOUSEMOVE: { int x = LOSWORD(lParam); int y = HISWORD(lParam); DWORD dwStyle; ScreenToClient(hdc, &x, &y); if(x >= 5 && x <= 35 && y > HEIGHT-55 && y < HEIGHT-25) { dwStyle = GetWindowStyle(btnLeft); if(!(dwStyle & WS_VISIBLE)) { IncludeWindowStyle(btnLeft, WS_CHILD | WS_VISIBLE | WS_BORDER); UpdateWindow(hWnd, TRUE); } } else { dwStyle = GetWindowStyle(btnLeft); if(dwStyle & WS_VISIBLE) { ExcludeWindowStyle(btnLeft, WS_VISIBLE); UpdateWindow(hWnd, TRUE); } } if(x >= WIDTH-35 && x <= WIDTH-5 && y> HEIGHT - 55 && y < HEIGHT-25) { dwStyle = GetWindowStyle(btnRight); if(!(dwStyle & WS_VISIBLE)) { IncludeWindowStyle(btnRight, WS_CHILD | WS_VISIBLE | WS_BORDER); UpdateWindow(hWnd, TRUE); } } else { dwStyle = GetWindowStyle(btnRight); if(dwStyle & WS_VISIBLE) { ExcludeWindowStyle(btnRight, WS_VISIBLE); UpdateWindow(hWnd, TRUE); } } return 0; } |
MiniGUI向窗口过程发送MSG_MOVE消息,把击中检测码放在wParam参数中,把光标坐标放在lParam参数中。lParam是个DWORD类型,低位存放鼠标的x坐标,高位存放鼠标的y坐标。通过LOSWORD(lParam)和HISWORD(lParam)函数提取。由于此时取出的坐标是屏幕坐标系上的坐标,需要转换成为客户区的坐标才能通过与客户区的相对位置进行判断是否进入按钮的区域。
当鼠标移入按钮的区域,通过GetWindowStyle函数取得按钮当前的风格,如果此时按钮没有WS_VISIBLE属性时,就用ExcludeWindowStyle函数添加WS_VISIBLE属性。鼠标移出按钮的区域也是这个思路,但是不是用ExcludeWindowStyle函数,而是用ExcludeWindowStyle函数,去除WS_VISIBLE属性。修改完按钮的属性后,需要重绘窗口才能生效,调用UpdateWindow函数重绘窗口。
4.6 键盘按键处理
监听键盘,如果按下“↑”或“←”时,切换到前一张图片,如果按下“↓”或“→”,切换到下一张图片。
case MSG_KEYDOWN: { keyCode = LOWORD(wParam); //keyCode = (int)wParam;一样 if(keyCode == 103 || keyCode == 105) index--; if(keyCode == 108 || keyCode == 106) index++; UpdateWindow(hWnd, TRUE); return 0; } |
当MiniGUI向窗口过程传送一个键盘消息时,击键消息的wParam参数就是代表该键的扫描码,lParam参数含有SHIFT、ALT、CTRL等特殊键的状态标志。“↑”和“←”的扫描码分别为103和105,“↓”和“→”的扫描码分别为108和106,index增减后,用UpdateWindow产生MSG_PAINT消息重绘窗口。