WinCE应用程序部分学习总结
到新公司来4个多月了,最近做的基于WinCE的程序也算是基本完成了,所以想对于最近学习的一些东西做一个归纳总结。其实对于WinCE的应用来说,大多数情况都是界面开发,所以对于想学底层的人,对于这一部分作为一个了解即可,至少要会写一个简单的对话框程序来测试你的底层驱动。想做一个比较炫的界面是非常复杂的,所以以下的方法都是一些简单的界面程序设计。
首先,需要理解的一点是,在WinCE中,不管是对话框,按键,以及各种对话框,他们其实都是一个窗口。理解这一点,对于初学者来说还是很重要的,这也就意味着我们可以像绘制普通窗口那样去绘制按键,或者ListBox的背景,无非你要做的就是获得这些控件的HDC。
Ok,还有需要提醒的一点就是,在程序中也许你会用到MessageBox,或者模态的对话框,这两者的使用都会使你主窗口的消息得不到处理,所以你的主窗口的界面就会得不到刷新。解决办法就是换成非模态的对话框即可。下面就对一些我用到的界面设计中的方法作一个总结(嘿嘿,对了,我都是用Win32写的,不会MFC,我想其实道理也都差不多):
一.绘制界面背景:
呵呵,也许眨一看,觉得无非绘制背景嘛,加载一张图片,然后Bitbit上去就可以了。但是当我们需要在一个背景上添加各种图片,或者需要移动的线条,or你要自己实现一个漂亮的滚动条。那么问题就出现了,每当你去刷新界面的时候,屏幕会闪烁的非常厉害,也许你可以在刷新的时候,只刷新你需要更新的那一部分,但是当你需要刷新整个界面的时候,那么这个办法就不行了。So,双缓冲是解决这个问题最好的方法。双缓冲的原理,大家可以Google一下(抵制baidu,支持Google)。直接贴代码:
/***********************************************************************************************
* 函数介绍:双缓冲绘制按键背景函数,由于要频繁调用该方法,所以独立出来该函数
* 输入参数:hWnd:按键句柄, hBtnmap:按键背景图, x, y:按键大小, iOffsetX iOffsetY:偏移量
* 输出参数:没得!
* 返回值 : 也没得!
***********************************************************************************************/
void HelloDrawBk(HWND hWnd, HBITMAP hBtnMap, int x, int y, int iOffsetX, int iOffsetY)
{
HDC hdc; //一个DC
HDC hdcMem; //两个DC
HDC hdcMemBuf; //三个DC,呀!怎么成三缓冲了。
RECT rect;
HBITMAP hBitmap;
HBITMAP hOldBmp1;
HBITMAP hOldBmp2;
hdc = GetDC(hWnd); //嘿嘿。。这里是我绘制按键背景的函数,所以我要取得他的HDC
//创建第一个内存DC
hdcMem = CreateCompatibleDC(NULL);
hBitmap = CreateCompatibleBitmap( hdc, x, y); //创建缓冲位图,先把图画到这张空白位图上
//创建第二个内存DC
hdcMemBuf = CreateCompatibleDC(NULL );
//将空白位图选进第一缓冲区
hOldBmp1 = ( HBITMAP )SelectObject( hdcMem, hBitmap);
//用背景色刷一下
GetClientRect(hWnd, &rect);
FillRect( hdcMem, &rect, NULL );
//将背景图贴上去,选择背景的起始位置,这里的背景坐标是由按键在背景图上的位置所决定的!
hOldBmp2 = ( HBITMAP )SelectObject( hdcMemBuf, hBackBitMap);
BitBlt( hdcMem, 0, 0, x, y, hdcMemBuf, iOffsetX, iOffsetY, SRCCOPY);
SelectObject( hdcMemBuf, hOldBmp2 );
//把按键图贴上去
hOldBmp2 = ( HBITMAP )SelectObject( hdcMemBuf, hBtnMap);
//该函数可以实现透明背景
TransparentBlt(hdcMem, 0, 0, x, y, hdcMemBuf, 0, 0, x, y, RGB(255,0,0));
//最后画到目的DC 上 这个HDC应该不叫缓冲吧,那么我们还是叫他双缓冲。
BitBlt( hdc, 0, 0, x, y, hdcMem, 0, 0,SRCCOPY);
//结束操作
SelectObject( hdcMem, hOldBmp1 ); //友情提示:不选出来要内存泄漏的哦!
SelectObject( hdcMemBuf, hOldBmp2 ); //友情提示:要全部选出来哦!
DeleteDC( hdcMem );
DeleteDC( hdcMemBuf );
DeleteObject( hBitmap );
ReleaseDC (hWnd, hdc);
}
Notice:
由于是我们自己绘制窗口背景,所以在窗口消息中应该加上
case WM_ERASEBKGND:
break;
这样窗口就不会自己去刷新背景了。
恩恩......!这样你的界面就不会闪了。俗话说:有好处就有坏处(这真的是谚语?),双缓冲对资源的耗费还是比较大的,如果你用一个Timer去刷新背景,那我就不保证你的程序一定正常咯。So,在写程序的时候,我们尽量不用Timer去刷新背景,在程序真正需要刷新的时候我们再去刷新屏幕,这个就需要由你的程序来决定怎么刷新咯。但是,用双缓冲的时候,一定,确定,务必,肯定,要记着这一点(咱是吃过亏的!)。
二.绘制按键背景。
既然我们会画背景咯,恩...恩....那么我们怎么绘制按键背景了,还要按下去和弹起来的时候不一样。MFC好像很简单就实现了,但是Win32就比较复杂一点咯。首先,按键消息,WM_LBUTTONDOWN,WM_LBUTTONUP,我们获取这个消息就可以了,但是如果主窗口有很多按键了?那么,我们就需要SetWindowsLong这个函数了(没用过的,查MSDN去,没有MSDN的baidu去!错了!Google去,抵制baidu支持Google!)
1、 创建按键:
//创建按键,创建的时候必须加上BS_OWNERDRAW消息,表示我们自绘按键
hPreBTN = CreateWindow (TEXT ("BUTTON"), NULL,
BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_OWNERDRAW ,
12, 35, 48, 40, hMainWnd, (HMENU)IDC_PreBTN, hMainInst, NULL);
2、 创建好按键以后我们需要:
SetWindowLong (hXxBtnWnd, GWL_WNDPROC, (long)XxBtnProc);
该函数把XxBtn(XX按键)的消息传递给了我们自定义的函数XxBtnProc来处理.
3、 实现XxBtnProc函数:
/***********************************************************************************************
* 函数介绍:Xx按键消息处理函数,绘制Xx按键背景
* 输入参数:自己看!
* 输出参数:没得!
* 返回值 : 自己看!
***********************************************************************************************/
LRESULT WINAPI XxBtnProc (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
switch (wMsg)
{
case WM_LBUTTONDOWN: //按下
//这个函数就在上面,绘制背景的,莫要说不认识我!
HelloDrawBk (hWnd, hXxBitMap1, 40, 40, 10, 5);
break;
case WM_LBUTTONUP: //起来
HelloDrawBk (hWnd, hXxBitMap2, 40, 40, 10, 5);
break;
}
//返回主窗口函数,不然主窗口得不到处理 不返回我试试!郁闷死你娃!5555555555555.....
return DefWindowProc(hWnd, wMsg, wParam, lParam);
}
4、 最后,你还需要在WM_PATINT消息里面绘制XxBtn的背景,不然按键没有初始背景。
如果需要实现长按这些效果,那么可以设定一个Timer来实现(理论上是这样的,没做过!)。
三.自绘ListBox :
这部分,我也只是实现了简单的背景颜色,和字体颜色的改变,想绘制ListBox的背景还没有找到很好的方法。直接贴代码:
LRESULT CALLBACK ListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
int wmId, wmEvent;
PAINTSTRUCT ps;
int i = 0;
int iCurSlec;
TCHAR tmpBuffer[256];
TCHAR szOut [256];
HBRUSH hbrBack;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmEvent)
{
//这个是ListBox窗口的双击消息
case LBN_DBLCLK:
iPlaySlec = SendDlgItemMessage (hWnd, IDC_LISTBOX, LB_GETCURSEL, 0, 0);
//重新更新一下ListBox
InvalidateRect( hListboxWnd, NULL, FALSE);
break;
//这个是ListBox窗口的单击消息
case LBN_SELCHANGE:
//重新更新一下ListBox
InvalidateRect( hListboxWnd, NULL, FALSE);
break;
}
break;
case WM_CREATE:
//创建一个ListBox窗口,LBS_OWNERDRAWVARIABLE指明是我们自绘ListBox,必须有该风格
hListboxWnd = CreateWindow (TEXT ("LISTBOX"), NULL,
WS_VSCROLL | WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CHILD
| LBS_NOTIFY | LBS_HASSTRINGS | LBS_OWNERDRAWVARIABLE ,
0, 0, 260, 325, hWnd, (HMENU)IDC_LISTBOX, hMainInst, NULL);
//添加一些选项进去,作为测试用
while ( i < 50)
{
i ++ ;
wsprintf (szOut, TEXT ("%d%d%d"), i, 0, 0);
// LB_ADDSTRING,添加一个选项
SendDlgItemMessage (hWnd, IDC_LISTBOX, LB_ADDSTRING, 0, (LPARAM)szOut);
}
break;
case WM_PAINT:
RETAILMSG(TRUE, (TEXT("ListWindow: WM_PAINT!/r/n")));
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_ERASEBKGND:
break;
//控制ListBox选项的大小!
case WM_MEASUREITEM:
LPMEASUREITEMSTRUCT lpmis;
lpmis=(LPMEASUREITEMSTRUCT)lParam;
lpmis->itemHeight=20;
break;
//绘制ListBox消息,在这里实现ListBox单个选项的绘制
case WM_DRAWITEM:
LPDRAWITEMSTRUCT lpdis;
lpdis=(LPDRAWITEMSTRUCT)lParam;
iCurSlec = SendDlgItemMessage (hWnd, IDC_LISTBOX, LB_GETCURSEL, 0, 0);
SendMessage(lpdis->hwndItem,LB_GETTEXT,lpdis->itemID,(LPARAM)tmpBuffer);
SelectObject(lpdis->hDC, hFont);
//改变单双数选项的背景色
if (lpdis->itemID & 0x01)
hbrBack = CreateSolidBrush (RGB (198, 233, 253));
else
hbrBack = CreateSolidBrush (RGB (200, 230, 250));
FillRect (lpdis->hDC, &lpdis->rcItem, hbrBack);
//实现选择,选中和未选的选项字体的不同颜色
if (lpdis->itemID == iCurSlec)
{
hbrBack = CreateSolidBrush (RGB (0, 51, 204));
FillRect (lpdis->hDC, &lpdis->rcItem, hbrBack);
SetBkMode(lpdis->hDC, TRANSPARENT);
SetTextColor(lpdis->hDC, RGB(0xff, 0xff, 0x00));
}
else if (lpdis->itemID == iPlaySlec)
{
SetBkMode(lpdis->hDC, TRANSPARENT);
SetTextColor(lpdis->hDC, RGB(0xff, 0x00, 0xff));
}
else
{
SetBkMode(lpdis->hDC, TRANSPARENT);
SetTextColor(lpdis->hDC, RGB(0x00, 0x00, 0xaa));
}
//显示
ExtTextOut(lpdis->hDC,0,((lpdis->rcItem.top + lpdis->rcItem.bottom)/2 - 10),
0, NULL, tmpBuffer,wcslen(tmpBuffer), NULL);
DeleteObject(hbrBack);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
杂七杂八:
1、如果你需要在窗口中相应双击按键,那么你需要在注册窗口的时候加上 CS_DBLCLKS风格。
2、如果你需要改变窗口的大小,你可以用SetWindowPos来实现,下面的方法可以实现全屏:
LONG nFullWidth=GetSystemMetrics(SM_CXSCREEN);
LONG nFullHeight=GetSystemMetrics(SM_CYSCREEN);
SetWindowPos(hDisplayWnd, HWND_TOPMOST, 0, 0, nFullWidth, nFullHeight, SWP_SHOWWINDOW);
3、如果你想实现字体的背景透明:
SetBkMode(hDC, TRANSPARENT);
SetTextColor(hDC, RGB(0xff, 0x00, 0xff));
4、如果你想实现一个特定形状的图形,比如圆形,方形,那么你可以设计一个方形的图片,然后在周围填充上一种特定的颜色作为背景色,比如255的全红色,然后调用函数:
TransparentBlt(hdcMem, 0, 0, x, y, hDc, 0, 0, x, y, RGB(255,0,0));//bmp的图片,其他图片不知道!
5、如果你要使用非模态对话框,记得在消息处理过程中加上IsDialogMessage:
while (GetMessage(&msg, NULL, 0, 0))
{
//使用非模式对话框时候,需要添加该消息处理过程
if ((hDisplayDlg == 0) || (!IsDialogMessage (hDisplayDlg, &msg)))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
差不多了,下面准备学习底层的一些东西,希望能坚持下来。本人菜鸟,有什么问题和建议大家可以联系:
luocan1358@126.com。或者群里的兄弟,可以直接QQ我。