Upon color change, i listen to WM_CTLCOLORSTATIC and act accordingly:
LRESULT ProcessWindowMessage(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
switch (uMsg)
{
case WM_CTLCOLORSTATIC:
LRESULT lBrush = ::DefWindowProc(hWnd, uMsg, wParam, lParam); // get default brush used so far
::SetBkMode((HDC)wParam, TRANSPARENT);
::SetTextColor((HDC)wParam, RGB(m_color.red, m_color.green, m_color.blue));
return lBrush;
}
}
This works well with regular static-texts: labels and so on, but has no effect on regular radio buttons.
During my debugging attempts, I've tried listening to:
WM_DRAWITEM - doesn't receive any events
WM_CTLCOLORBTN - receive events only for regular push buttons( OK / CANCEL)
WM_CTLCOLOREDIT - doesn't receive any events.
I'm subclassing to another window not generated / created by me, but constructed by my process.
#igal k said SetWindowTheme does not work. Since the comment is not enough for the sample code. I post it as an answer.
First the result.
Code:
OnInitDialog:
::SetWindowTheme(GetDlgItem(IDC_RADIO1)->GetSafeHwnd(), L"wstr", L"wstr");
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
// Call the base class implementation first! Otherwise, it may
// undo what we're trying to accomplish here.
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// Are we painting the IDC_MYSTATIC control? We can use
// CWnd::GetDlgCtrlID() to perform the most efficient test.
if (pWnd->GetDlgCtrlID() == IDC_RADIO1)
{
// Set the text color to red
pDC->SetTextColor(RGB(255, 0, 0));
// Set the background mode for text to transparent
// so background will show thru.
pDC->SetBkMode(TRANSPARENT);
// Return handle to our CBrush object
hbr = m_brush;
}
return hbr;
}
If you are interested to draw the full radio button, what you have to do is set a custom window proc for the radio button.In that proc you get the WM_PAINT and WM_ERASEBKGND messages.
In the erase background you can just fill the background color for the control.
In WM_PAINT you do the drawing of the control , get the window text of the control and do a DrawText with color set to whatever you need using SetTextColor
Draw an image of radio (circle and a dot inside).You can get all the state variables of the control and draw according to it, like whether its selected it or not. Whenever you need a repaint, ie new text is set, just call invalidateRect to force a repaint..
Related
I want to know if it's possible to change the color of the text (and of the small arrow) and the background of the static in a CCombobox with the Drop List style.
My class is derived from CComboBox and I've tried with the function CtlColor and OnCtlColor, but nothing seems to change the color of the ComboBox.
Here's a picture of the control with the Drop List style :
I would like the text and the arrow to change to RGB(0, 255, 255) and the background to RGB(255,255,0).
Here's my function CtlColor() :
HBRUSH CColoredComboBox::CtlColor(CDC *pDC, UINT nCtlColor)
{
if (nCtlColor == CTLCOLOR_STATIC || nCtlColor == CTLCOLOR_EDIT)
{
pDC->SetBkColor(RGB(255,255,0));
pDC->SetTextColor(RGB(0, 255, 255));
}
return m_brBkgnd;
}
It works for the style Dropdown, but not for the Drop List.
Thank you.
There are two ways - easy and hard. The hard way is to complete DrawItem with ownerdraw to handle all the cases. The easy way is to put two combos on top of each other in your dialog resource and hide the one you're not going to use.
Maybe this can give you some ideas.
Don't know arrow color can be changed or not but, color of combobox can be changed. With help of OnChildNotify() function, you can retrieve child HDC and then particular child HDC can be changed.
/////////////////////////////////////////////////////////////////////////////
// CMyComboBox message handlers
BOOL CMyComboBox::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)
{
// TODO: Add your specialized code here and/or call the base class
if(WM_CTLCOLOREDIT != message)
return CComboBox::OnChildNotify(message, wParam, lParam, pLResult);
HDC hdcChild = (HDC)wParam;
if(NULL != hdcChild)
{
SetBkMode(hdcChild, TRANSPARENT);
SetTextColor(hdcChild, RGB(255, 255, 0));
SetBkColor(hdcChild, RGB(255, 0, 0));
*pLResult = (LRESULT)(m_Brush.GetSafeHandle());
}
return TRUE;
// return CComboBox::OnChildNotify(message, wParam, lParam, pLResult);
}
Result:
I created the dialog form for MFC class by editing .rc file with form designer as follows,
It is taken as LTEXT. I want to change the color of that text using WM_CTLCOLORSTATIC message.
I used the subclass as follows,
HWND name_message = ::GetDlgItem(hwnd_, IDC_EDIT_OUTPUT_STRING);
g_EditTxtViewWndProc = (WNDPROC)(LONG_PTR)GetWindowLongPtr(name_message, GWLP_WNDPROC);
SetWindowLongPtr(name_message, GWLP_WNDPROC, (LONG_PTR)(EditTxtViewProc));
LRESULT CALLBACK
EditTxtViewProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
static HBRUSH hBrush = CreateSolidBrush(RGB(255, 0, 0));
switch( msg )
{
case WM_CTLCOLORSTATIC:
{
HDC hdC = (HDC)wp;
SetTextColor(hdC, RGB(255, 0, 0));
SetBkColor(hdC, RGB(255, 255, 255));
return (INT_PTR)hBrush;
}
}
}
In above case it does not call to the WM_CTLCOLORSTATIC message. I am beginner to the Window Programming.
Can anyone help me that how to subclass as above?
Remy Lebeau's comment is correct. You need to handle this message from within the parent's window procedure.
In MFC, you could also directly use CWnd::OnCtlColor. Most controls send this message to their parent (usually a dialog box) to prepare the pDC for drawing the control using the correct colors.
For more information, refer to MSDN: https://learn.microsoft.com/en-us/cpp/mfc/reference/cwnd-class#onctlcolor
I have a main window in a process that is not owned by the program I'm creating. I'm using a Windows Hook to inject a DLL into this process for the purpose of adding a child window to this main window.
My end goal was to create a WS_EX_LAYERED window that allows me to create an internal colored border but allow the center portion to be transparent and allow mouse clicks through. This part works perfectly.
WNDCLASS wndClass = {};
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = OverlayProc;
wndClass.hInstance = g_TargetInstance;
wndClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 255, 255));
wndClass.lpszClassName = "OVERLAY";
RegisterClass(&wndClass);
g_Window = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, "OVERLAY", nullptr,
WS_CHILDWINDOW, rect.left, rect.top, rect.right+1, rect.bottom+1, data->hwnd, nullptr, g_TargetInstance, nullptr);
SetLayeredWindowAttributes(g_Window, RGB(0, 255, 255), 0, LWA_COLORKEY);
ShowWindow(g_Window, SW_SHOW);
UpdateWindow(g_Window);
The 2nd part to this is a I wanted to conditionally block all mouse input to the parent window. I couldn't do this with the transparent portion of the WS_EX_LAYERED window so I tried creating a 2nd transparent STATIC control as a child of the main window but this doesn't block mouse input either.
I'm also sending simulated mouse clicks to the parent window through calls to PostMessage, passing WM_LBUTTONDOWN and WM_LBUTTONUP. How could I block all mouse input to the parent window via a transparent window?
It appears this is not possible to do with a simple transparent window drawn over sibling controls. What I ended up doing was using SetWindowHookEx to add a WH_GETMESSAGE hook into the process from which I use to replace the main window's WndProc function and intercept mouse messages. I tag my simulated mouse messages with a specific value in the wParam argument so the proc will now it was simulated and removes that value, passing it along to the parent window.
If it does not detect my "tag" value in the click message, it will swallow the mouse message and not pass it along to the original WndProc function.
Injected WndProc replacement
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
wParam -= 11141008;
if (wParam != MK_LBUTTON && !g_Paused)
return 0;
break;
case WM_LBUTTONUP:
wParam -= 11141008;
if (wParam != 0 && !g_Paused)
return 0;
break;
case WM_MOUSEHOVER:
case WM_MOUSEMOVE:
if (!g_Paused)
return 0;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Snippet from Windows Hook function
//...
switch (data->message)
{
case (WM_USER + 1):
{
g_Paused = FALSE;
//...
SetWindowSubclass(data->hwnd, WndProc, 1, 0);
break;
}
case (WM_USER + 2):
{
RemoveWindowSubclass(data->hwnd, WndProc, 1);
//...
break;
}
}
//...
The code inside the window hook function is used to subclass the main process window and inject my own WndProc function which in turn processes mouse input the way I want.
This is the code used to "simulate" mouse clicks without physically clicking in the window. Note the added value to wParam to identify this click as simulated and not generated by the user.
void Window::LeftClick(DWORD x, DWORD y, DWORD delayMillis)
{
LPARAM lparam = MAKELPARAM(x, y);
lock_guard<mutex> lock(this->m_ClickMutex);
PostMessage(this->m_Window, WM_LBUTTONDOWN, 11141008 + MK_LBUTTON, lparam);
this_thread::sleep_for(std::chrono::milliseconds(delayMillis));
PostMessage(this->m_Window, WM_LBUTTONUP, 11141008, lparam);
}
Also, just for the person in the comments who was ridiculing my choice of the word simulated and the added criticism for using PostMessage to simulate keyboard input, here is my keyboard input test method which (for my purposes) works flawlessly and very reliably
void GameWindow::KeyPress(UINT vkCode) const
{
UINT scanCode = MapVirtualKey(vkCode, MAPVK_VK_TO_VSC);
LPARAM lparam1 = MAKELPARAM(1, scanCode);
LPARAM lparam2 = MAKELPARAM(1, 0xC000 | scanCode);
PostMessage(this->m_Window, WM_KEYDOWN, vkCode, lparam1);
this_thread::sleep_for(chrono::milliseconds(25));
PostMessage(this->m_Window, WM_KEYUP, vkCode, lparam2);
}
Question is pretty straight forward, I'm using SetDlgItemText to display text, but I want to change the text color from black to grey so it appears grey on the screen. I tried settextcolor, knowing that was a long shot and it didn't work.
EDIT: Here is the code I have that creates the box
SetDlgItemText(hDlg, IDC_EDIT2, password_string);
You don't mention whether the control is a static or an edit control.Use code like the following in your dialog proc;
For Edit controls: under case WM_CTLCOLOREDIT: and for Static controls under case WM_CTLCOLORSTATIC:
case WM_CTLCOLOREDIT:
if (::GetDlgCtrlID((HWND) lParam) == IDC_MY_CONTROL)
{ HBRUSH hbr = (HBRUSH) DefWindowProc(hDlg, iMessage, wParam, lParam);
SetTextColor((HDC) wParam, RGB(192, 192, 192));
return (BOOL) hbr;
}
return FALSE;
Cannot do that with Stock dialogs.
Option #1 - use WM_CTLCOLORSTATIC
http://www.wischik.com/lu/programmer/setdlgitemurl.html
https://groups.google.com/forum/#!topic/comp.os.ms-windows.programmer.win32/GLUZu-zH0TQ
Option #2 - USE RTF by swapping/switching to a Rich Edit Control
https://msdn.microsoft.com/en-us/library/bb787877(v=VS.85).aspx
http://www.cplusplus.com/forum/general/49319/
Can you change the background of text in area of edit control that would stay static?
In the parent of the edit control, handle the WM_CTLCOLORSTATIC message, the wParam of this message is the HDC that the Edit control is about to draw with,
for most CTLCOLOR messages, if you set text and background colors into this DC, the control will use the colors you set.
You can also return an HBRUSH and the contol will use that for any brush painting that it wil do, but many controls don't use brushes much, so that will have limited effect for some
CTLCOLOR messages. Your best bet here is to return the DC brush, and set the DC Brush color to match the BkColor of the DC.
LRESULT lRet = 0; // return value for our WindowProc.
COLORREF crBk = RGB(255,0,0); // use RED for Background.
...
case WM_CTLCOLORSTATIC:
{
HDC hdc = (HDC)wParam;
HWND hwnd = (HWND)lParam;
// if multiple edits and only one should be colored, use
// the control id to tell them apart.
//
if (GetDlgCtrlId(hwnd) == IDC_EDIT_RECOLOR)
{
SetBkColor(hdc, crBk); // Set to red
SetDCBrushColor(hdc, crBk);
lRet = (LRESULT) GetStockObject(DC_BRUSH); // return a DC brush.
}
else
{
lRet = DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
break;
WM_CTLCOLORSTATIC is for static text control.
To be simple, you can do this in your winproc:
...
case WM_CTLCOLOREDIT:
{
HDC hdc = (HDC)wParam;
SetTextColor(hdc, yourColor); // yourColor is a WORD and it's format is 0x00BBGGRR
return (LRESULT) GetStockObject(DC_BRUSH); // return a DC brush.
}
...
If you have more than 1 edit control, you can use the item id and lParam to check which one need to be change.
WM_CTLCOLOREDIT allows you to set text and background color(+brush), if you want more control than that, you have to subclass and paint yourself
you could do something like this:
CBrush bkBrush;
RECT ctrlRect;
COLORREF crBk = RGB(255,0,0); // Red color
bkBrush.CreateSolidBrush(crBk);
CWnd* pDlg = CWnd::GetDlgItem(IDC_EDIT);
pDlg->GetClientRect(&ctrlRect);
pDlg->GetWindowDC()->FillRect(&ctrlRec, &bkBrush);
pDlg->GetWindowDC()->SetBkColor(crBk);
This should change the background color of the edit control
All you need is to set the required color in control's device context and pass an HBRUSH with same color in WM_CTLCOLOREDIT message. If you want to change both foreground & background colors, use SetTextColor t0 change the text color. But you must pass the background color HBRUSH. But if you want to change the text color only, then you must pass a DC_BRUSH with GetStockObject function.