您的位置:首页 > 其它

VC实现区域选择以及不规则窗口示例

2016-09-08 15:11 281 查看
在一些应用场合,比如屏幕选定区域录制,需要选定一个区域,然后画出一个矩形边框以便界定录制区域的边界。如下图:



图中的虚边框是可以随鼠标移动的,并且实时显示坐标信息。

一旦确定位置,再次单击鼠标,则可以画出如下矩形区域:



绿色的矩形就是最终选定的区域,该选择区域位于所有窗口最顶层,所以不会被其他窗口所覆盖。

实现该方法其实也不难,需要创建2个特殊的窗口,一个是用于移动选择区域的,另一个是指示最后选定的区域的。

第一个窗口是个全屏窗口,根据鼠标移动绘制选择边框。

第二个窗口是个不规则窗口,采用CombineRgn等方法对窗口形状进行裁剪。

这些都是一些特殊场合需要用到的一些技巧,这里共享出来,希望对有这方面需求的朋友提供一点帮助。

关于第一个窗口,鼠标移动绘制部分代码如下:

void CShiftRegionWindow2::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
POINT pt;
RECT rcnew_rect;
int dx,dy;
GetCursorPos(&pt);

dx = (rcClip.right - rcClip.left)/2;
dy = (rcClip.bottom - rcClip.top)/2;

rcnew_rect.left = pt.x - dx; // Update rect with new mouse info
rcnew_rect.top = pt.y - dy;
rcnew_rect.right = pt.x + dx;
rcnew_rect.bottom = pt.y + dy;

if (rcnew_rect.left<0) {

rcnew_rect.left=0;
rcnew_rect.right= dx+dx;

}
if (rcnew_rect.top<0) {

rcnew_rect.top=0;
rcnew_rect.bottom= dy+dy;

}
if (rcnew_rect.right>maxxScreen-1) {

rcnew_rect.right=maxxScreen-1;
rcnew_rect.left=maxxScreen-1- dx-dx;

}
if (rcnew_rect.bottom>maxyScreen-1) {

rcnew_rect.bottom=maxyScreen-1;
rcnew_rect.top=maxyScreen-1- dy-dy;

}

//if (memcmp(&rcClip, &rcnew_rect, sizeof(rcClip)) != 0)
if(!EqualRect(&rcClip, &rcnew_rect))
{
HDC hScreenDC = ::GetDC(GetSafeHwnd());
DrawSelect(hScreenDC, FALSE, &rcClip); // erase old rubber-band
DrawSelect(hScreenDC, TRUE, &rcnew_rect); // new rubber-band
::ReleaseDC(GetSafeHwnd(),hScreenDC);

rcClip=rcnew_rect;
}// if old

CWnd::OnMouseMove(nFlags, point);
}矩形框和提示框的绘制如下:
void CShiftRegionWindow2::DrawSelect(HDC hdc, BOOL fDraw, LPRECT lprClip)
{
wchar_t sz[80];
DWORD dw;
int x, y, len, dx, dy;
HDC hdcBits;
RECT rectDraw;
SIZE sExtent;

rectDraw = *lprClip;
if (!IsRectEmpty(&rectDraw))
{

// If a rectangular clip region has been selected, draw it
HBRUSH newbrush = (HBRUSH) CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,100));
HBRUSH oldbrush = (HBRUSH) SelectObject(hdc,newbrush);

// 画4条反色的线组成矩形框。PATINVERT:使用布尔XOR(异或)操作符将指定模式的颜色与目标矩形的颜色进行组合。
//PatBlt SRCINVERT regardless fDraw is TRUE or FALSE
PatBlt(hdc, rectDraw.left, rectDraw.top, rectDraw.right-rectDraw.left, DINV, PATINVERT);
PatBlt(hdc, rectDraw.left, rectDraw.bottom-DINV, DINV, -(rectDraw.bottom-rectDraw.top-2*DINV),  PATINVERT);
PatBlt(hdc, rectDraw.right-DINV, rectDraw.top+DINV, DINV, rectDraw.bottom-rectDraw.top-2*DINV,   PATINVERT);
PatBlt(hdc, rectDraw.right, rectDraw.bottom-DINV, -(rectDraw.right-rectDraw.left), DINV,  PATINVERT);

SelectObject(hdc,oldbrush);
DeleteObject(newbrush);

hdcBits = CreateCompatibleDC(hdc);
HFONT newfont = (HFONT) GetStockObject(ANSI_VAR_FONT);
HFONT oldfont = (HFONT) SelectObject(hdc, newfont);
//HFONT oldfont = (HFONT) SelectObject(hdcBits, newfont);

wsprintf(sz, L"Left : %d  Top : %d  Width : %d  Height : %d", rectDraw.left, rectDraw.top, rectDraw.right - rectDraw.left+1, rectDraw.bottom -  rectDraw.top+1);
len = lstrlen(sz);
dw = GetTextExtentPoint(hdc, sz, len, &sExtent);
//dw = GetTextExtentPoint(hdcBits, sz, len, &sExtent);

dx = sExtent.cx;
dy = sExtent.cy;
x=  rectDraw.left +10;

if (rectDraw.top < (dy + DINV + 2))
y=  rectDraw.bottom + DINV + 2;
else
y=  rectDraw.top - dy - DINV - 2;

if (fDraw)	{

//Save Original Picture
SaveBitmapCopy(hdc,hdcBits,  x-4, y-4, dx+8, dy+8); //保存文字将要覆盖的区域

//Text 输出文字
COLORREF oldtextcolor = SetTextColor(hdc,RGB(0,0,0));
COLORREF oldbkcolor = SetBkColor(hdc,RGB(255,255,255));
SetBkMode(hdc,TRANSPARENT);

//Rectangle(hdc,x-1,y-1,x+dx, y+dy);
RoundRect(hdc,x-4,y-4,x+dx+4, y+dy+4,10,10); // 包围文字的圆角矩形

SetBkMode(hdc,OPAQUE);

ExtTextOut(hdc, x, y, 0, NULL, sz, len, NULL);
SetBkColor(hdc,oldbkcolor);
SetTextColor(hdc,oldtextcolor);
SelectObject(hdc, oldfont);
}
else
{
RestoreBitmapCopy(hdc,hdcBits,  x-4, y-4, dx+8, dy+8);//// 恢复文字覆盖的区域
// 其他区域因为是异或操作,不需要恢复
}

//Icon
if ((rectDraw.right-rectDraw.left-10 >  35) &&  (rectDraw.bottom-rectDraw.top-10 > dy + 40)) {

HBITMAP hbv = LoadBitmap( AfxGetInstanceHandle(),  MAKEINTRESOURCE(IDB_BITMAP1));
HBITMAP old_bitmap = (HBITMAP) SelectObject(hdcBits, hbv);
//  SRCINVERT:这个光栅操作码代表“异或”操作
BitBlt(hdc, rectDraw.left+10, rectDraw.bottom-42, 30, 32,hdcBits, 0,0, SRCINVERT);
SelectObject(hdcBits,old_bitmap);
DeleteObject(hbv);

}

DeleteDC(hdcBits);
}

}


关键部分都有注释,还是比较容易看懂的。

关于第二个窗口的绘制,主要用到的不规则窗口绘制技巧,关键代码如下:

void CFlashingWnd::SetUpRegion(int x, int y, int width, int height, int type)
{

CRgn wndRgn, rgnTemp, rgnTemp2,rgnTemp3;

cRect.left= x;
cRect.top= y;
cRect.right = cRect.left + width -1;
cRect.bottom = cRect.top + height -1;
TRACE("SetUpRegion, (%d,%d,%d,%d)\n", cRect.left,cRect.top,cRect.right,cRect.bottom);

if (type == 0) {
// 四个角边框
wndRgn.CreateRectRgn(0,0, cRect.Width()+THICKNESS+THICKNESS, cRect.Height()+THICKNESS+THICKNESS);
rgnTemp.CreateRectRgn(THICKNESS, THICKNESS, cRect.Width()+THICKNESS+1, cRect.Height()+THICKNESS+1);
rgnTemp2.CreateRectRgn(0, SIDELEN2, cRect.Width()+THICKNESS+THICKNESS, cRect.Height()-SIDELEN+1);
rgnTemp3.CreateRectRgn(SIDELEN2,0, cRect.Width()-SIDELEN+1, cRect.Height()+THICKNESS+THICKNESS);

wndRgn.CombineRgn(&wndRgn,&rgnTemp,RGN_DIFF);
wndRgn.CombineRgn(&wndRgn,&rgnTemp2,RGN_DIFF);
wndRgn.CombineRgn(&wndRgn,&rgnTemp3,RGN_DIFF);

wndRgn.OffsetRgn( cRect.left-THICKNESS, cRect.top-THICKNESS );

}
else {

// 矩形边框, 通过区域裁剪获得, RGN_DIFF-2个区域相减
wndRgn.CreateRectRgn(0,0, cRect.Width()+SMALLTHICKNESS+SMALLTHICKNESS, cRect.Height()+SMALLTHICKNESS+SMALLTHICKNESS);
rgnTemp.CreateRectRgn(SMALLTHICKNESS, SMALLTHICKNESS, cRect.Width()+SMALLTHICKNESS+1, cRect.Height()+SMALLTHICKNESS+1);

wndRgn.CombineRgn(&wndRgn,&rgnTemp,RGN_DIFF);

wndRgn.OffsetRgn( cRect.left-SMALLTHICKNESS, cRect.top-SMALLTHICKNESS );

}

HRGN newregion = (HRGN) wndRgn.Detach();
SetWindowRgn((HRGN) newregion, TRUE);

if (oldregion) DeleteObject(oldregion);
oldregion = newregion;

}这里提供了2种图案选择,一个是4角边框,一个是矩形边框,用户可以自己体验。
该工程用VS2008编译通过。

工程完整代码下载地址如下:

点击这里下载完整源码

这里我对这2个窗口的使用进行了一个封装,可以单独拿出来移植到其他工程直接使用,无其他版权要求。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: