理论: 拖放,是指用鼠标拖动的方法,在不同程序的窗口之间、同 一个程序的不同窗口之间或同一程序同一窗口的不同控件之间,进行移动、复制和粘贴等操作的技术。拖放操作是在操作系统的帮助下完成的。被拖动的对象首先向 操作系统注册它使用的数据格式,并按指定的数据格式提供数据,拖放操作结束时,接收拖放的窗口按指定的数据格式提取有关数据,并根据提取的数据生成相应的 对象。 拖放有两种类型:OLE拖放和文件管理器拖放。这两种方式是完全不同的机制。文件管理器拖放只能处理文件名,通过映射目的窗口的WM_DROPFILES消息,窗口就可以收到拖放进来的文件名。OLE拖放则更加通用一些,它允许你拖放可同时被保存在剪贴板上的任何数据。本文只介绍下文件管理器拖放。 这种方式的实质就是产生一个消息WM_DROPFILES。技术上没有什么难点,主要用到下面几个API函数:DragQueryFile、DragQueryPoint、DragFinish。它们的原型和注解分别如下: UINT DragQueryFile(HDROP hDrop, UINT iFile, LPTSTR lpszFile, UINT cch) 本函数用来取得拖放的文件名。其中,hDrop是一个指向含有被拖放的文件名的结构体的句柄;iFiles是要查询的文件序号,因为一次可能同时拖动很多个文件;lpszFiles是出口缓冲区指针,保存iFiles指定序号的文件的路径名,cch指定该缓冲区的大小。有两点值得注意,第一,如果我们在调用该函数的时候,指定iFile为0xFFFFFFFF,则DragQueryFile将忽略lpszFile和cch参数,返回本次拖放操作的文件数目;第二,如果指定lpszFile为NULL,则函数将返回实际所需的缓冲区长度。 BOOL DragQueryPoint(HDROP hDrop, LPPOINT lppt); 本函数用来获取,当拖放操作正在进行时,鼠标指针的位置。第二个参数lppt是一个指向POINT结构体的指针,用来保存文件放下时,鼠标指针的位置。窗口可以调用该函数以查询文件是否落在自己的窗口矩形中。 void DragFinish(HDROP hDrop); 当拖放操作处理完毕后需调用该函数释放系统分配来传输文件名的内存。代码:见光盘DragDrop #include "windows.h" /***************************************************************/ char title[] = "This is the file you dropped into the window"; char szClassName[] = "Project_Class"; char szDisplayName [] = "Drag and Drop a file into this window"; HWND g_hWnd; HINSTANCE g_hInstance; HICON g_hIcon; DWORD TopXY(DWORD wDim, DWORD sDim); void Paint_Proc(HWND hWin,HDC hDC); LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); /***************************************************************/ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; MSG msg; DWORD Wwd; DWORD Wht; DWORD Wtx; DWORD Wty; g_hInstance = hInstance; g_hIcon = LoadIcon(g_hInstance,MAKEINTRESOURCE(500)); wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wc.lpszMenuName = NULL; wc.lpszClassName = szClassName; wc.hIcon = g_hIcon; wc.hCursor = LoadCursor(NULL,IDC_ARROW); wc.hIconSm = g_hIcon; RegisterClassEx(&wc); Wwd = 300; Wht = 300; Wtx = TopXY(Wht,GetSystemMetrics(SM_CXSCREEN)); Wty = TopXY(Wht,GetSystemMetrics(SM_CYSCREEN)); g_hWnd = CreateWindowEx(WS_EX_LEFT|WS_EX_ACCEPTFILES, szClassName,szDisplayName,WS_OVERLAPPED | WS_SYSMENU,Wtx, Wty,Wwd,Wht,NULL,NULL,hInstance,NULL); ShowWindow(g_hWnd,SW_SHOWNORMAL); UpdateWindow(g_hWnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { RECT Rct; HDC hDC; PAINTSTRUCT ps; char buffer1[128]; switch(uMsg) { case WM_DROPFILES: DragQueryFile((HDROP)wParam,0,buffer1,128); MessageBox(hwnd,buffer1,title,MB_OK); DragFinish((HDROP)wParam); break; case WM_PAINT: hDC = BeginPaint(hwnd,&ps); GetClientRect(hwnd,&Rct); DrawText(hDC,szDisplayName,lstrlen(szDisplayName),&Rct,DT_LEFT); EndPaint(hwnd,&ps); break; case WM_DESTROY: PostQuitMessage(NULL); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0; } DWORD TopXY(DWORD wDim, DWORD sDim) { sDim = (sDim - wDim)/2; return sDim; } 分析: 代码比较简单,首先在创建窗口时,加入 WS_EX_ACCEPTFILES 风格。这样窗口就可以接受拖放操作了。 g_hWnd = CreateWindowEx(WS_EX_LEFT|WS_EX_ACCEPTFILES, szClassName,szDisplayName,WS_OVERLAPPED | WS_SYSMENU,Wtx, Wty,Wwd,Wht,NULL,NULL,hInstance,NULL); 当有文件拖放到窗口时,窗口会收到消息WM_DROPFILES。我们在这个消息中,进行拖放处理就可以了。 case WM_DROPFILES: DragQueryFile((HDROP)wParam,0,buffer1,128); MessageBox(hwnd,buffer1,title,MB_OK); DragFinish((HDROP)wParam); break; 运行此程序可以看到,拖动的文件只要一进入对话框的边界,鼠标上立刻会现一个“+”,在对话框上的任意位置释放鼠标,拖放的文件都会被接受。 |
windows sdk编程系列文章 ---- 利用镂空制作特效界面
理论: 平时我们经常看到一些不规则的 窗口 界面,给人的感觉也比较好。本篇我们就做这样一个特效界面。 先贴出一个图来。 要制作这样一个不规则的窗口其实比较简单。 我们看到背景图中,粉色的底色,我们需要处理掉。关于背景颜色的rgb值,我们可以通过GetPixel函数来得到。也可以使用一些小工具得到。windows sdk中将窗口设置为不规则形状的函数只有一个,如下: int SetWindowRgn( HWND hWnd , // handle to window HRGN hRgn , // handle to region BOOL bRedraw // window redraw option ); 从这个函数参数看,想要创建一个不规则的窗口,关键是需要一个不规则的区域。 得到不规则区域的办法,比较简单。步骤是: 1)使用 CreateRectRgn函数,创建一个为0的区域。 HRGN wndRgn = ::CreateRectRgn(0,0,0,0); 2)根据平面图的长度和宽度,逐行扫描图,遇到需要非底色的颜色,则创建一个像素的区域。 //创建一个包含起点与终点间高为1像素的临时“region” rgnTemp = CreateRectRgn(iLeftX, y, iX, y+1); 3)将该区域合并1)创建的区域上。 CombineRgn(wndRgn,wndRgn,rgnTemp, RGN_OR); 4)重复2的步骤,继续扫描,扫描完成后,就得到了我们想要得区域。 上面的步骤贴个代码如下: void SetupRegion(HDC hDC, HBITMAP &cBitmap, COLORREF TransColor) { //创建与传入DC兼容的临时DC HDC memDC= ::CreateCompatibleDC(hDC); HBITMAP hOldMemBmp(0); //将位图选入临时DC hOldMemBmp = (HBITMAP)::SelectObject(memDC,cBitmap); //创建总的窗体区域,初始region为0 HRGN wndRgn = ::CreateRectRgn(0,0,0,0); BITMAP bit; GetObject(cBitmap,sizeof(BITMAP),&bit);//取得位图参数,这里要用到位图的长和宽 int y; for(y=0;y<=bit.bmHeight ;y++) { HRGN rgnTemp; //保存临时region int iX = 0; do { //跳过透明色找到下一个非透明色的点. while (iX <= bit.bmWidth && GetPixel(memDC,iX, y) == TransColor) iX++; //记住这个起始点 int iLeftX = iX; //寻找下个透明色的点 while (iX <= bit.bmWidth && GetPixel(memDC,iX, y) != TransColor) ++iX; //创建一个包含起点与终点间高为1像素的临时“region” rgnTemp = CreateRectRgn(iLeftX, y, iX, y+1); //合并到主"region". CombineRgn(wndRgn,wndRgn,rgnTemp, RGN_OR); //删除临时"region",否则下次创建时和出错 DeleteObject(rgnTemp); }while(iX <bit.bmWidth ); iX = 0; } if(hOldMemBmp) SelectObject(memDC,hOldMemBmp); HWND hWnd = WindowFromDC(hDC); SetWindowRgn(hWnd,wndRgn,TRUE); SetForegroundWindow(hWnd); } 本篇例子,我们直接有了这个区域文件,我们把这个区域文件加载到rc中。 我们使用ExtCreateRegion函数,根据rc中加载的rgn资源,来创建一个HRGN. 而我们例子怎样生成一个rgn文件呢,这里给出一个链接,里面给出了一个工具。 http://www.codeproject.com/KB/GDI/rgncreator.aspx 代码:见光盘cws #include "windows.h" /************************************************************/ #define ButtonID 1000 #define PictureW 300 #define PictureH 300 HWND g_hwndButton; HWND g_hWnd; HRSRC g_RsrcHand; HGLOBAL g_RsrcPoint; DWORD g_RsrcSize; HINSTANCE g_hInstance; char ClassName[] = "cws_class"; char DisplayName[] = "custom windows shape"; char RsrcName[] = "RANGE"; char RsrcType[] = "RGN"; char ButtonClassName[] = "button"; char ButtonText[] = "Click Me!"; char Text[] = "good bye..."; /************************************************************/ LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch(uMsg) { case WM_CREATE: g_RsrcHand = FindResource(g_hInstance,RsrcName,RsrcType); g_RsrcPoint = LoadResource(g_hInstance,g_RsrcHand); g_RsrcSize = SizeofResource(g_hInstance,g_RsrcHand); g_RsrcPoint = LockResource(g_RsrcPoint); SetWindowRgn(hwnd,ExtCreateRegion(NULL,g_RsrcSize,(const struct _RGNDATA *)g_RsrcPoint),TRUE); g_hwndButton = CreateWindowEx(NULL,ButtonClassName,ButtonText, WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON, 150,185,100,25,hwnd,(HMENU)ButtonID,g_hInstance,NULL); break; case WM_COMMAND: if(LOWORD(wParam) == ButtonID) { MessageBox(hwnd,Text,DisplayName,MB_OK); SendMessage(hwnd,WM_DESTROY,NULL,NULL); } break; case WM_LBUTTONDOWN: SendMessage(hwnd,WM_NCLBUTTONDOWN,HTCAPTION,lParam); break; case WM_DESTROY: PostQuitMessage(NULL); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; MSG msg; g_hInstance = hInstance; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW |CS_BYTEALIGNWINDOW; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hbrBackground = CreatePatternBrush(LoadBitmap(hInstance,MAKEINTRESOURCE(1000))); wc.lpszMenuName = NULL; wc.lpszClassName = ClassName; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL,IDC_CROSS); wc.hIconSm = NULL; RegisterClassEx(&wc); g_hWnd = CreateWindowEx(WS_EX_LEFT, ClassName, DisplayName, WS_POPUP, (GetSystemMetrics(SM_CXSCREEN) - PictureW)/2, (GetSystemMetrics(SM_CYSCREEN) - PictureH)/2, PictureW, PictureH, NULL, NULL, hInstance, NULL ); ShowWindow(g_hWnd,SW_SHOWNORMAL); UpdateWindow(g_hWnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } 分析: 首先,我们把要镂空处理的图作为背景画刷。 wc.hbrBackground = CreatePatternBrush(LoadBitmap(hInstance,MAKEINTRESOURCE(1000))); 然后,在创建窗口的时候,我们从资源中加载rgn,并将窗口设置为不规则形状。 case WM_CREATE: g_RsrcHand = FindResource(g_hInstance,RsrcName,RsrcType); g_RsrcPoint = LoadResource(g_hInstance,g_RsrcHand); g_RsrcSize = SizeofResource(g_hInstance,g_RsrcHand); g_RsrcPoint = LockResource(g_RsrcPoint); SetWindowRgn(hwnd,ExtCreateRegion(NULL,g_RsrcSize,(const struct _RGNDATA *)g_RsrcPoint),TRUE); 接下来,我们创建了一个按钮。 g_hwndButton = CreateWindowEx(NULL,ButtonClassName,ButtonText, WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON, 150,185,100,25,hwnd,(HMENU)ButtonID,g_hInstance,NULL); break; 为了让窗口能够拖动,我们模拟了 WM_NCLBUTTONDOWN消息。 case WM_LBUTTONDOWN: SendMessage(hwnd,WM_NCLBUTTONDOWN,HTCAPTION,lParam); break; |