Is there an api to disable all controls in a rect area?
I am trying to write something like that:
GetClientRect(hWnd, rect);
DisableControls(rect);
GetClientRect gets the client area (left and top are always 0), you need to use GetWindowRect to figure out where a child window is.
All child controls are in the client area and you can just disable the parent window and all children will also stop accepting input.
It does seem a little contrived to disable based on a rectangle instead of a list of known controls but I suppose it might be useful in some cases.
static BOOL CALLBACK DisableChildrenInRectProc(HWND hWnd, LPARAM Param)
{
RECT *pParentRect = (RECT*) Param, r, ir;
if (GetWindowRect(hWnd, &r) && IntersectRect(&ir, &r, pParentRect))
{
EnableWindow(hWnd, FALSE);
}
return TRUE;
}
HWND hWnd = ...
RECT r;
GetWindowRect(hWnd, &r);
r.bottom = r.top + (r.bottom - r.top) / 2; // In this example, only disable controls in the top half.
EnumChildWindows(hWnd, DisableChildrenInRectProc, (LPARAM) &r);
I don't believe there is one. Use EnumChildWindows, GetWindowRect and some coordinate mapping via ScreenToClient. For overlap testing you could use IntersectRect.
Related
i want to have a border and title less window so i do SetWindowLongPtrW( window_handle, GWL_STYLE, 0 );
After that i can't move my Window so in my WndProc i do
if( message == WM_NCHITTEST ) {
RECT rc;
GetClientRect( hwnd, &rc );
MapWindowPoints( hwnd, GetParent( hwnd ), (LPPOINT)&rc, 2 );
int mouseX = LOWORD( lParam ) - rc.left;
int mouseY = HIWORD( lParam ) - rc.top;
POINT p;
p.x = mouseX;
p.y = mouseY;
return PtInRect( &rc, p ) ? HTCAPTION : DefWindowProc( hwnd, message, wParam, lParam );
}
It works, the first time i move the window. After i once stop clicking with the mouse it won't move again :/
SetWindowLongPtrW( window_handle, GWL_STYLE, 0 ); will hide the window, assuming it doesn't cause more serious problems. Use GetWindowLongPtr and combine that with valid window styles, or hide the window using ShowWindow
The error you have described is unrelated. You are attempting to find the screen coordinates of the window using GetClientRect and MapWindowPoints. The result will not be exact because the window may have borders and title bar.
Use GetWindowRect instead. This will give you screen coordinates of the window.
You can compare that with the mouse position LOWORD(lParam) and HIWORD(lParam). This is already screen coordinates. This code will move the screen every where the mouse lands in window:
RECT rc;
GetWindowRect(hwnd, &rc);
int mouseX = LOWORD(lparam);
int mouseY = HIWORD(lparam);
Don't subtract rc.left and rc.top from mouse position. That would convert the coordinates in client coordinates (roughly). Your code may work when window is on top-left corner of the screen, but it won't work later when window is moved.
Use ScreenToClient if you wish to work in client window coordinates.
As MSDN said
BOOL ScreenToClient(
_In_ HWND hWnd,
LPPOINT lpPoint
);
the ScreenToClient's second para is a pointer to POINT,
and PINT said by MSDN is
typedef struct tagPOINT {
LONG x;
LONG y;
} POINT, *PPOINT;
it has only x and y. It's NOT like MFC ScreenToClient function, the para is a rect, and rect has width and height.
I am confused how to use win32 ScreenToClient function.
You can use MapWindowPoints() to convert a RECT in a single operation:
RECT r = ...;
MapWindowPoints(NULL, hWnd, (LPPOINT)&r, 2);
MFC actually has two methods, they're overloaded. One accepts a POINT structure, just like the Win32 function, the other accepts a RECT structure, both work the same way: it maps each point from screen-to-client.
If you have a RECT that you want to get client coordinates of without using MFC then just do it manually, like so:
RECT rect = GetMyRect();
POINT rectTL;
rectTL.x = rect.left;
rectTL.y = rect.top;
ScreenToClient( hWnd, &rectTL );
POINT rectBR;
rectBR.x = rect.right
rectBR.y = rect.bottom;
ScreenToClient( hWnd, &rectBR );
rect.left = rectTL.x;
rect.top = rectTL.y;
rect.right = rectBR.x;
rect.bottom = rectBR.y;
Note that RECT is
typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT;
and looks like two consecutive POINTs in memory. Therefore you can do what the MFC source code does, which is approx. the following (don't have the MFC source in front of me right now):
::ScreenToClient(hWnd, (POINT*)&rect->left);
::ScreenToClient(hWnd, (POINT*)&rect->right);
which is not the cleanest thing from a C point of view, but those structures are bound to remain binary compatible.
I have a Layered Window (WS_EX_LAYERED) that implements a custom NCHITTEST and NCCALCSIZE to make the client rect of my window the same as the window rect. My window sizes and paints correctly; I can return HTBOTTOM from WM_NCHITTEST when the cursor is near the bottom edge of my window to cause a vertical-resize-window-from-the-bottom type action. However, I am not getting the vertical resize cursor. Is there way a do fix this without having to implement WM_SETCURSOR and test the pointer's position vs the edges of the windows?
Here's a snippet of my code:
case WM_NCCALCSIZE:
// Bypass DefWindowProc, so the Window Rect == Client Rect
return 0;
case WM_NCHITTEST: {
RECT w;
::GetWindowRect(hwnd, &w);
// Compare the mouse X/Y vs the rect of the window to detect
// resizing from the bottom edges
int r = HTCLIENT;
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
if (w.bottom - y < 10) {
// If I was not using NCHITTEST, I should get a verticle resize pointer here
if (x - w.left < 10)
r = HTBOTTOMLEFT;
else if (w.right - x < 10)
r = HTBOTTOMRIGHT;
else
r = HTBOTTOM;
}
return r;
}
You need to handle the WM_SETCURSOR message - the low-order word of lParam specifies the hit-test code.
For instance,
case WM_SETCURSOR:
switch (LOWORD(lParam))
{
case HTBOTTOM:
SetCursor(LoadCursor(0, IDC_SIZENS));
return 0;
}
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
I want to draw a lot of lines in the WM_PAINT message handler with the following code.
//DrawLine with double buffering
LRESULT CALLBACK CMyDoc::OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
std::vector<Gdiplus::Point> points;
std::vector<Gdiplus::Point>::iterator iter1, iter2;
HDC hdc, hdcMem;
HBITMAP hbmScreen, hbmOldBitmap;
PAINTSTRUCT ps;
RECT rect;
hdc = BeginPaint(hWnd, &ps);
//Create memory dc
hdcMem = CreateCompatibleDC(hdc);
GetClientRect(hWnd, &rect);
hbmScreen = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
hbmOldBitmap = (HBITMAP)SelectObject(hdcMem, hbmScreen);
//Fill the rect with white
FillRect(hdcMem, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
//Draw the lines
Gdiplus::Graphics graphics(hdcMem);
Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0));
points = m_pPolyLine->GetPoints();
for (iter1 = points.begin(); iter1 != points.end(); iter1++) {
for (iter2 = iter1 + 1; iter2 != points.end(); iter2++)
graphics.DrawLine(&blackPen, *iter1, *iter2);
}
//Copy the bitmap from memory dc to the real dc
BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
//Clean up
SelectObject(hdcMem, hbmOldBitmap);
DeleteObject(hbmScreen);
DeleteDC(hdcMem);
EndPaint(hWnd, &ps);
return 0;
}
However, if the size of points exceed 20, the client rect just flicker. I think the reason is that Gdiplus::DrawLines is too slow.
Is there any method to solve the flicker problem?
Thanks.
The flickering may be caused by slow painting as well as by other things. In general, try/ensure the following:
Try to not rely on WM_ERASEBKGND message, i.e. return non-zero, or specify NULL in WNDCLASS::hbrBackground if possible. Often the paint method paints all the background of the dirty region, then there is no need to do the erasing.
If you need the erasing, it can often be optimized so that WM_ERASEBKGND returns non-zero, and the paint method then ensures the "erasing" by also painting areas not covered by the regular painted contents, if PAINTSTRUCT::fErase is set.
If reasonably possible, write the paint method so that it does not repaint the same pixels in one call. E.g. to make blue rect with red border, do not FillRect(red), then repainting inner part of it with FillRect(blue). Try to paint each pixel once as much as reasonably possible.
For complex controls/windows, the paint method may often be optimized to easily skip a lot of painting outside the dirty rect (PAINTSTRUCT::rcPaint) by proper organizing the control data.
When changing the control state, invalidate only the minimal required region of the control.
If it is not top-level window, consider using CS_PARENTDC. If your paint method does not rely on system setting clipping rectangle to the client rect of the control, this class style will lead to a somewhat better performance.
If you see the flickering on control/window resizing, consider to not using CS_HREDRAW and CS_VREDRAW. Instead invalidate the relevant parts of the control in WM_SIZE manually. This often allows to invalidate only smaller parts of the control.
If you see the flickering on control scrolling, do not invalidate whole client, but use ScrollWindow() and invalidate only small area which exposes the new (scrolled-in) content.
If everything above fails, then use double buffering.
Use double buffer. It is an offen problem with Win32 C++ application and specifically with OnPaint function and DC facilities.
Here is a few links to help you check if everything is fine with YOUR implementation of double buffer: Flicker Free Drawing In MFC and SO question "Reduce flicker with GDI+ and C++"
If your lines happen to extend outside the bounds of the DC (Graphics), Win32/GDI+ is painfully slow at clipping. Like, up to two orders of magnitude slower than rolling your own clipping function. Here is some C# code that implements Liang/Barsky - I scrounged this up from an old library that was originally in C++ 20 years ago. Should be easy enough to port back.
If your lines can extend beyond the client rectangle, call ClipLine(rect, ...) on your points before handing them off to Graphics::DrawLine.
private static bool clipTest(double dp, double dq, ref double du1, ref double du2)
{
double dr;
if (dp < 0.0)
{
dr = dq / dp;
if (dr > du2)
{
return false;
}
else if (dr > du1)
{
du1 = dr;
}
}
else
{
if (dp > 0.0)
{
dr = dq / dp;
if (dr < du1)
{
return false;
}
else if (dr < du2)
{
du2 = dr;
}
}
else
{
if (dq < 0.0)
{
return false;
}
}
}
return true;
}
public static bool ClipLine(Rectangle clipRect, ref int x1, ref int y1, ref int x2, ref int y2)
{
double dx1 = (double)x1;
double dx2 = (double)x2;
double dy1 = (double)y1;
double dy2 = (double)y2;
double du1 = 0;
double du2 = 1;
double deltaX = dx2 - dx1;
double deltaY;
if (clipTest(-deltaX, dx1 - clipRect.Left, ref du1, ref du2))
{
if (clipTest(deltaX, clipRect.Right - dx1, ref du1, ref du2))
{
deltaY = dy2 - dy1;
if (clipTest(-deltaY, dy1 - clipRect.Top, ref du1, ref du2))
{
if (clipTest(deltaY, clipRect.Bottom - dy1, ref du1, ref du2))
{
if (du2 < 1.0)
{
x2 = DoubleRoundToInt(dx1 + du2 * deltaX);
y2 = DoubleRoundToInt(dy1 + du2 * deltaY);
}
if (du1 > 0.0)
{
x1 = DoubleRoundToInt(dx1 + du1 * deltaX);
y1 = DoubleRoundToInt(dy1 + du1 * deltaY);
}
return x1 != x2 || y1 != y2;
}
}
}
}
return false;
}
The problem is I have not handle WM_ERASEBKGND message by myself.
I have a dialog picture control of type = frame that I've used as a parent for something else.
When the child window is destroyed, the remnants are left in the control. What can I do to either clear the control or cause the demise of the child window to clear the control?
winapi c++
I thought there might be a simpler method but the following does the trick and allows you to color it however you like.
int s;
HDC dc;
RECT R;
z = GetDlgItem (hDlg, IDC_PS_AREA); // clear the containing control
dc = GetWindowDC (z);
s = GetClientRect (z,&R);
FillRect (dc, &R, (HBRUSH) GetStockObject (LTGRAY_BRUSH));
ReleaseDC (z, dc);
And even better
int s;
HDC dc;
RECT R;
HBRUSH hB;
z = GetDlgItem (hDlg, IDC_PS_AREA); // clear the parent containing control
dc = GetWindowDC (z);
s = GetClientRect (z,&R);
hB = GetSysColorBrush (COLOR_3DFACE);
FillRect (dc, &R, hB);
ReleaseDC (z, dc);