Clipping Region of Child Window Controls
-
I cannot figure out how to persistently change the clipping region of a child window control. I have a rich edit control (non-mfc... created with CreateWindowEx), and I want to change the visible area of the control dynamically. There seem to be three approaches, and I cannot seem to figure out how to get any of them to work: 1. Call SetWindowRgn() on the child window control: this weirdly enough seems to make the specified region invisible, rather than visible, and is not persistent (the next InvalidateRect call re-draws the region. 2. Call SelectClipRgn(): this isn't persistent after the display context is released. The following code for example completely ignores my call to SelectClipRgn(): hdc = GetDC(hwnd); rgn1 = CreateRectRgn(0, 0, 50, 50); (void)SelectClipRgn(hdc, rgn1); (void)ReleaseDC(hwnd, hdc); hdc = GetDC(hwnd); (void)GetClipBox(hdc, &WinRect); rgn1 = CreateRectRgnIndirect((const RECT *)(&WinRect)); hbr = (HBRUSH) GetStockObject(WHITE_BRUSH); (void)FillRgn(hdc, rgn1, hbr); ... 3. Intercept the WM_PAINT message of the control, and change the clipping region before the control is painted. The problem here is that I don't know how to intercept the child window WM_PAINT message without completely re-writing the control, and that doesn't seem to be the right solution. Anyway, I love your site... first time I've logged onto CodeProject.com. I would appreciate any insight into how to clip a child control (a rich edit control in particular), but I have some buttons that I also want to clip in the same region. Thank you, Brian Brian Rosenthal Chief Technology Officer MyBestHealth, Inc. brosenthal@mybesthealth.com
-
I cannot figure out how to persistently change the clipping region of a child window control. I have a rich edit control (non-mfc... created with CreateWindowEx), and I want to change the visible area of the control dynamically. There seem to be three approaches, and I cannot seem to figure out how to get any of them to work: 1. Call SetWindowRgn() on the child window control: this weirdly enough seems to make the specified region invisible, rather than visible, and is not persistent (the next InvalidateRect call re-draws the region. 2. Call SelectClipRgn(): this isn't persistent after the display context is released. The following code for example completely ignores my call to SelectClipRgn(): hdc = GetDC(hwnd); rgn1 = CreateRectRgn(0, 0, 50, 50); (void)SelectClipRgn(hdc, rgn1); (void)ReleaseDC(hwnd, hdc); hdc = GetDC(hwnd); (void)GetClipBox(hdc, &WinRect); rgn1 = CreateRectRgnIndirect((const RECT *)(&WinRect)); hbr = (HBRUSH) GetStockObject(WHITE_BRUSH); (void)FillRgn(hdc, rgn1, hbr); ... 3. Intercept the WM_PAINT message of the control, and change the clipping region before the control is painted. The problem here is that I don't know how to intercept the child window WM_PAINT message without completely re-writing the control, and that doesn't seem to be the right solution. Anyway, I love your site... first time I've logged onto CodeProject.com. I would appreciate any insight into how to clip a child control (a rich edit control in particular), but I have some buttons that I also want to clip in the same region. Thank you, Brian Brian Rosenthal Chief Technology Officer MyBestHealth, Inc. brosenthal@mybesthealth.com
Being completely confused, I wrote this test application I built to illustrate exactly what is happening to my code. It's a simple application that compiles by itself in VC6.0. A quick glance, and then running it, is meant to illustrate perhaps more in detail my confusion with setting the window region of a child control. I have marked three points in the code that can be either commented or not to produce results that to me were surprising and erratic. So, I guess my real question is to explain the results and tell me what I can do to get the richedit control in this example to have its region set properly. Here is a quick description of the code, and then the actual source follows (you can just cut and paste into VC6.0, and it works). Sorry, I know the tabs didn't copy into the message, but I cannot figure out what to do to get them in there. Simple WinProc: I. WM_CREATE: // Create a RichEdit Control // Create a button // Call ShowWindow() on each. II. WM_COMMAND: // If the button is clicked: // 1. call SetWindowRgn(richedit) to set the region // to only the top left. // 2. (C. Call InvalidateRgn(...) on the window) // 3. (D. Call InvalidateRgn(...) on the richedit control) III. WM_PAINT: // B. SendMessage(...WM_PAINT...) to both children, then return 0 // A. break; As you can see, I note four places in the code which each produce a different behavior that confuses me. It's very easy to uncomment the proper lines and see the result. Anyway, thank you very much in advance. If you would like me to clarify something, please send me e-mail at brosenthal@mybesthealth.com. Sorry if this is a stupid question. I feel like a dunce. Have been beating my head against the wall about this for a while. Anyway, here is the sourcecode that works in VC6.0: I will post the sourcecode immediately following this on this thread (too long for one message)
-
Being completely confused, I wrote this test application I built to illustrate exactly what is happening to my code. It's a simple application that compiles by itself in VC6.0. A quick glance, and then running it, is meant to illustrate perhaps more in detail my confusion with setting the window region of a child control. I have marked three points in the code that can be either commented or not to produce results that to me were surprising and erratic. So, I guess my real question is to explain the results and tell me what I can do to get the richedit control in this example to have its region set properly. Here is a quick description of the code, and then the actual source follows (you can just cut and paste into VC6.0, and it works). Sorry, I know the tabs didn't copy into the message, but I cannot figure out what to do to get them in there. Simple WinProc: I. WM_CREATE: // Create a RichEdit Control // Create a button // Call ShowWindow() on each. II. WM_COMMAND: // If the button is clicked: // 1. call SetWindowRgn(richedit) to set the region // to only the top left. // 2. (C. Call InvalidateRgn(...) on the window) // 3. (D. Call InvalidateRgn(...) on the richedit control) III. WM_PAINT: // B. SendMessage(...WM_PAINT...) to both children, then return 0 // A. break; As you can see, I note four places in the code which each produce a different behavior that confuses me. It's very easy to uncomment the proper lines and see the result. Anyway, thank you very much in advance. If you would like me to clarify something, please send me e-mail at brosenthal@mybesthealth.com. Sorry if this is a stupid question. I feel like a dunce. Have been beating my head against the wall about this for a while. Anyway, here is the sourcecode that works in VC6.0: I will post the sourcecode immediately following this on this thread (too long for one message)
#ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #endif #include #include #include #include "richedit.h" /* * Define a block of globally accessible information */ typedef struct tagAPPBLOCK { /* Windows types */ HINSTANCE hInstance; /* application instance */ HWND hMainWnd; /* handle to main window */ HWND hwndRichEditChatWindow; /* Host rich edit chat window */ HRGN hwndRichEditChatRegion; /* The region of the rich edit control */ HWND hwndGoButton; } APPBLOCK, *LPAPPBLOCK; // Define the WndProc function LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); APPBLOCK appblock; /* * Two child windows: A Rich edit control and a button */ #define ID_RICHEDITCHATWINDOW 1 #define ID_GOBUTTON 2 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("RichEdit Region Clipping Test"); // name HWND hwnd; // this window MSG msg; // for the message loop WNDCLASS wndclass; // I will define a class for this window char tmpStr[128]; // for debugging information appblock.hInstance = hInstance; // Store the instance value in a global variable. wndclass.style = 0; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) GetStockObject(TRANSPARENT); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; // LoadLibrary("Riched20.dll"); // Necessary to use the rich edit control if (!LoadLibrary("Riched20.dll")) { (void)MessageBox(NULL, "Error! Load Riched20.dll.", "Fatal Error.", MB_OK | MB_ICONASTERISK); sprintf(tmpStr, "Error Information: %d", GetLastError()); return false; } // Register the class if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("Error registering the class"), szAppName, MB_ICONERROR); return 0; } // Create a main window to hold the button and the rich edit control hwnd = CreateWindow( szAppName, TEXT("Richedit Clipping Test"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } /* * Contain
-
I cannot figure out how to persistently change the clipping region of a child window control. I have a rich edit control (non-mfc... created with CreateWindowEx), and I want to change the visible area of the control dynamically. There seem to be three approaches, and I cannot seem to figure out how to get any of them to work: 1. Call SetWindowRgn() on the child window control: this weirdly enough seems to make the specified region invisible, rather than visible, and is not persistent (the next InvalidateRect call re-draws the region. 2. Call SelectClipRgn(): this isn't persistent after the display context is released. The following code for example completely ignores my call to SelectClipRgn(): hdc = GetDC(hwnd); rgn1 = CreateRectRgn(0, 0, 50, 50); (void)SelectClipRgn(hdc, rgn1); (void)ReleaseDC(hwnd, hdc); hdc = GetDC(hwnd); (void)GetClipBox(hdc, &WinRect); rgn1 = CreateRectRgnIndirect((const RECT *)(&WinRect)); hbr = (HBRUSH) GetStockObject(WHITE_BRUSH); (void)FillRgn(hdc, rgn1, hbr); ... 3. Intercept the WM_PAINT message of the control, and change the clipping region before the control is painted. The problem here is that I don't know how to intercept the child window WM_PAINT message without completely re-writing the control, and that doesn't seem to be the right solution. Anyway, I love your site... first time I've logged onto CodeProject.com. I would appreciate any insight into how to clip a child control (a rich edit control in particular), but I have some buttons that I also want to clip in the same region. Thank you, Brian Brian Rosenthal Chief Technology Officer MyBestHealth, Inc. brosenthal@mybesthealth.com
It says in the documentation for SetWindowRgn: "After a successful call to SetWindowRgn, the system owns the region specified by the region handle hRgn. The system does not make a copy of the region. Thus, you should not make any further function calls with this region handle. In particular, do not delete this region handle. The system deletes the region handle when it no longer needed." In case 'A' below, if you comment out the break statement then you destroy the window region after the first WM_PAINT message, thus putting your display into an undefined state. Jim // WEIRD OUTCOME A. If this line is uncommented, the child window is resized, // but only temporarily. Alt-tabbing between applications seems to lose the region. break; //if this is commented out, then lpab->hwndRichEditChatRegion is destroyed, violating rules for SetWindowRgn // END WEIRD OUTCOME A case WM_DESTROY: DeleteObject(lpab->hwndRichEditChatRegion); PostQuitMessage(0); return 0; }
-
It says in the documentation for SetWindowRgn: "After a successful call to SetWindowRgn, the system owns the region specified by the region handle hRgn. The system does not make a copy of the region. Thus, you should not make any further function calls with this region handle. In particular, do not delete this region handle. The system deletes the region handle when it no longer needed." In case 'A' below, if you comment out the break statement then you destroy the window region after the first WM_PAINT message, thus putting your display into an undefined state. Jim // WEIRD OUTCOME A. If this line is uncommented, the child window is resized, // but only temporarily. Alt-tabbing between applications seems to lose the region. break; //if this is commented out, then lpab->hwndRichEditChatRegion is destroyed, violating rules for SetWindowRgn // END WEIRD OUTCOME A case WM_DESTROY: DeleteObject(lpab->hwndRichEditChatRegion); PostQuitMessage(0); return 0; }
I concur - once you have used setwindowrgn the HRGN handle is now owned by the system Holy Handgrenade of Antioch instructions