我试图在我多年前开发的Apprehend Screen Capture Component中围绕选定的屏幕对象绘制一个聚焦矩形.我可以通过Handles:= WindowFromPoint(P)获取光标下对象的句柄来绘制DrawFocusRect;但这要求我隐藏然
不幸的是,当我隐藏并显示表单时,它会在隐藏和显示表单时导致闪烁.
我可以毫无问题地获得所选对象的位图,只是绘制选定的对象让我疯狂.
有没有人有任何建议围绕选定的对象绘制一个FocusedRect,所以没有闪烁?如果表单位于屏幕顶部,是否有任何API可以获取屏幕句柄?
我尝试使用Handles:= WindowFromDC(ScreenDC),所以我不必隐藏和显示表单,但WindowFromDC仍然返回表单而不是屏幕.
TCaptureObjectForm是透明的,位于屏幕上方.我需要组件中的TCaptureObjectForm.
// FormMouseMove事件 – 已添加08/2/2011
procedure TCaptureObjectForm.FormMouseMove( Sender: TObject; Shift: TShiftState; X, Y: Integer ); const crHand = -18; var P: TPoint; Handles: HWND; Rect: TRect; ScreenDC: HDC; begin // hide the TCaptureObjectForm form so the screen is found by WindowFromPoint Self.Hide; // get the object on the screen GetCursorPos( P ); Handles := WindowFromPoint( P ); // tried this but it returns self.handle rather than the screen handle //ScreenDC := GetDC( 0 ); //Handles := WindowFromDC(ScreenDC); //ReleaseDC( 0, ScreenDC ); // restore the TCaptureObjectForm Self.Show; // get object rect GetWindowRect( Handles, Rect ); // draw a rect to show it is focused Self.Canvas.DrawFocusRect( Rect ); end;This article是Microsoft的Visual Basic中的一个示例,它执行与您所需的非常相似的操作.
他们采取以下方法:
>在Form_MouseDown中捕获鼠标.
>当鼠标移动时,在用鼠标指向的窗口周围绘制一个矩形:Form_MouseMove.
>在Form_MouseUp中释放鼠标,并使整个屏幕无效以删除最后绘制的矩形.
他们直接在他们选择的窗口中绘制.我不认为使用透明窗口方法可以避免所有闪烁.
该代码示例似乎不完整且效果不佳,因此我对其进行了修改(并转换为Delphi):
// Not global variables, but private form ones var HwndLastTracked: HWND; CapturedMouse: boolean; procedure InvertTracker(hwndWindow: HWND); var rc: TRect; dc: HDC; pen, oldPen: HPEN; oldBrush: HBRUSH; style, exStyle: longint; cx, cy: integer; begin GetWindowRect(hwndWindow, rc); // Window coordinates of the origin (top-left corner) of a window is (0, 0) OffsetRect(rc, -rc.Left, -rc.Top); // DC returned by GetWindowDC covers the full window area, but in Windows // Vista/7 it seems to be clipped excluding the nonclient region, due to // DWM handling nonclient drawing, so it doesn't allow painting over it. // Thus we need to skip this nonclient area and that is why I adjust the // window rect to match the client area. Using GetClientRect instead of // GetWindowRect is not suitable as excludes scroll bars and child // parts drawed in WM_NCPAINT, such as Windows' WS_EXEDGEs and Delphi's // bevels. style := GetWindowLong(hwndWindow, GWL_STYLE); exStyle := GetWindowLong(hwndWindow, GWL_EXSTYLE); if style and WS_CAPTION <> 0 then begin if exStyle and WS_EX_TOOLWINDOW <> 0 then cy := GetSystemMetrics(SM_CYSMCAPTION) else cy := GetSystemMetrics(SM_CYCAPTION); // discard area covered by caption Inc(rc.Top, cy); end; if style and WS_THICKFRAME <> 0 then begin cx := GetSystemMetrics(SM_CXFRAME); cy := GetSystemMetrics(SM_CYFRAME); end else if style and WS_DLGFRAME <> 0 then begin cx := GetSystemMetrics(SM_CXDLGFRAME); cy := GetSystemMetrics(SM_CYDLGFRAME); end else if style and WS_BORDER <> 0 then begin cx := GetSystemMetrics(SM_CXBORDER); cy := GetSystemMetrics(SM_CYBORDER); end else begin cx := 0; cy := 0; end; if (cx <> 0) or (cy <> 0) then begin // discard area covered by borders OffsetRect(rc, cx, cy); Dec(rc.Right, cx*2); Dec(rc.Bottom, cy*2); end; // Windows API functions don't raise exceptions, so I don't use try-finally dc := GetWindowDC(hwndWindow); // Option 1: focused rect //DrawFocusRect(dc, rc); // Option 2: inverted thick border SetROP2(dc, R2_NOT); pen := CreatePen(PS_INSIDEFRAME, 3 * GetSystemMetrics(SM_CXBORDER), 0); oldPen := SelectObject(dc, pen); oldBrush := SelectObject(dc, GetStockObject(NULL_BRUSH)); Rectangle(dc, rc.Left, rc.Top, rc.Right, rc.Bottom); SelectObject(dc, oldBrush); SelectObject(dc, oldPen); DeleteObject(pen); // End option 2 ReleaseDC(hwndWindow, dc); end; procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if SetCapture(Handle) <> 0 then begin CapturedMouse := true; HwndLastTracked := 0; Screen.Cursor := crCross; end; end; procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var hwndCaptured: HWND; begin if CapturedMouse then begin hwndCaptured := WindowFromPoint(ClientToScreen(Point(X, Y))); // Uncomment this for track root windows instead of childs //hwndCaptured := GetAncestor(hwndCaptured, GA_ROOT); if hwndCaptured <> HwndLastTracked then begin if HwndLastTracked <> 0 then InvertTracker(HwndLastTracked); InvertTracker(hwndCaptured); HwndLastTracked := hwndCaptured; end; end; end; procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if CapturedMouse then begin ReleaseCapture; CapturedMouse := false; if HwndLastTracked <> 0 then begin InvertTracker(HwndLastTracked); HwndLastTracked := 0; end; Screen.Cursor := crDefault; end; end;
以下是Microsoft在Visual Studio的Spy中如何使用此技术的屏幕截图.红色的气球和文字都是我的!