ATL project pushbutton color change - c++

We have a requirement to change the color of pushbutton/control. I have controls defined in resource file.
We have tried multiple ways
1)Using CMFCButton Object I got defination ambiguity errors
2)
Using CBUtton CDC* pdcWindow1 = m_Button.GetWindowDC(); CRect rect1; GetClientRect(&rect1); pdcWindow1->FillSolidRect(&rect1, (RGB(0, 0, 255)));
No effect on Button color no error as well.
Inputs which i have got so far : we have used ATLcontrols and to color Button we need MFC Functions, here ATL and MFC libs can't coexist they are causing ambiguity errors as both have same functional definitions.
Is it even possible to color ATL controls without MFC functions.?
only solution is --https://jeffpar.github.io/kbarchive/kb/173/Q173974/??

Look of standard Windows GDI buttons is customized according to Button Color Messages:
The system sends a WM_CTLCOLORBTN message to a button's parent window before drawing a button. This message contains a handle to the button's device context and a handle to the child window. The parent window can use these handles to change the button's text and background colors. However, only owner-drawn buttons respond to the parent window processing the message.
In ATL project, you would either handle this notification message in parent window class or use more sophisticated message forwarding (reflection) to reflect this message to button class.
Either way you don't really paint (FillSolidRect), you can just update colors in the message handler. And also pay attention that only owner-drawn buttons expose this functionality.
See also: Owner-drawn button, WM_CTLCOLORBTN and WM_DRAWITEM (clearing an HDC)

This is how I was able to achieve :
HINSTANCE hInstance = GetModuleHandle(NULL);
HANDLE hBitmap = LoadImage(hInstance, MAKEINTRESOURCE(IDB_CURCANCEL_LOGO), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
HANDLE hBitmap1 = LoadImage(hInstance, MAKEINTRESOURCE(IDB_CURLOGIN_LOGO), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
const HWND hOriginalLoginButton = GetDlgItem(IDOK);
const HWND hOriginalCancelButton = GetDlgItem(IDCANCEL);
SendMessage(hOriginalLoginButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap1);
SendMessage(hOriginalCancelButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
Now i am trying to make corners rounded.

Related

Properly handling owner drawn Win32 button hovering

I want to add multiple color themes to my Win32 application, this means that I have to manually handle all the control drawing manually by using the BS_OWNERDRAW style flag. I then handle all the drawing in the WM_DRAWITEM message through the LPDRAWITEMSTRUCT structure stored in the lParam. Here's the problem though, by introducing owner drawing I also have to handle click and hover events, which in and of itself isn't a problem but it becomes one because it turns out that the LPDRAWITEMSTRUCT has no message being sent to indicate whether or not a control is being hovered. The itemState member do have messages like ODS_SELECTED, ODS_FOCUS and so on, but no flag for hovering only.
That being said, there does exist a flag named ODS_HOTLIGHT which according to MSDN should do the job, the problem however is that this flag never occurs for buttons, only menu items. I've tried using the SetWindowSubclass function by giving each control it's separate WindowProc callback where you can track the mouse leaving and entering the control. This works, but that also means that I need to transition all drawing commands over to the subclass procedure, which seems rather stupid to me, since the WM_DRAWITEM is intended for custom drawing in the first place.
You could always send WM_DRAWITEM manually via SendMessage but that also means that I need to provide an LPDRAWITEMSTRUCT manually which hasn't worked out that great. In essence I do not know what the best approach is to react to these hover events and draw them accordingly, the LPDRAWITEMSTRUCT does not provide such a flag and thus I have no idea what other approach to use. How should I tackle this?
The way I'm currently handling the WM_DRAWITEM message (buttons only):
case WM_DRAWITEM: {
LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
HPEN borderPen = CreatePen(PS_INSIDEFRAME, 0, RGB(0, 0, 0));
HGDIOBJ oldPen = SelectObject(pDIS->hDC, borderPen);
HGDIOBJ oldBrush;
if (pDIS->itemState & ODS_SELECTED) {
oldBrush = SelectObject(pDIS->hDC, buttonHoverBrush);
}
else {
oldBrush = SelectObject(pDIS->hDC, buttonDefaultBrush);
}
// Rounded button
RoundRect(pDIS->hDC, pDIS->rcItem.left, pDIS->rcItem.top, pDIS->rcItem.right, pDIS->rcItem.bottom, 5, 5);
//Clean up
SelectObject(pDIS->hDC, oldPen);
SelectObject(pDIS->hDC, oldBrush);
DeleteObject(borderPen);
// Calculate button dimensions
int buttonWidth = pDIS->rcItem.right - pDIS->rcItem.left;
int buttonHeight = pDIS->rcItem.bottom - pDIS->rcItem.top;
WCHAR staticText[128];
int len = SendMessage(pDIS->hwndItem, WM_GETTEXT, ARRAYSIZE(staticText), (LPARAM)staticText);
HFONT buttonFont = (HFONT)SendMessage(pDIS->hwndItem, WM_GETFONT, 0, 0);
SIZE buttonDim;
HFONT oldFont = (HFONT)SelectObject(pDIS->hDC, buttonFont);
GetTextExtentPoint32(pDIS->hDC, staticText, len, &buttonDim);
SetTextColor(pDIS->hDC, RGB(255, 255, 255));
SetBkMode(pDIS->hDC, TRANSPARENT);
TextOut(pDIS->hDC, buttonWidth / 2 - buttonDim.cx / 2, buttonHeight / 2 - buttonDim.cy / 2, staticText, len);
wasHandled = TRUE;
result = TRUE;
break;
}
The code listed above creates a button with a given background color and centers text within it. It also changes color to the hover rush when clicked. What I want to happen is that the button changes its color immediately upon hover and not upon click. Thank you in advance!
I've tried using the SetWindowSubclass function by giving each control it's separate WindowProc callback where you can track the mouse leaving and entering the control. This works, but that also means that I need to transition all drawing commands over to the subclass procedure, which seems rather stupid to me, since the WM_DRAWITEM is intended for custom drawing in the first place.
Unfortunately, that is precisely what you will likely need to do.
For instance, you can have the button's subclass procedure handle the WM_MOUSEMOVE message to detect when the mouse is over the button, and handle the WM_MOUSELEAVE message to detect when the mouse moves out of the button. The message handler can use WM_MOUSEMOVE to call TrackMouseEvent() to trigger WM_MOUSELEAVE and WM_MOUSEHOVER messages. It can then use WM_MOUSEHOVER to set a flag and invalidate the button to trigger a repaint, and it can use WM_MOUSELEAVE to clear the flag and invalidate the button to trigger a repaint.
Inside the button's normal draw handler (which DOES NOT need to be moved to the subclass procedure. BTW), if the flag is set then draw the button as hovered, otherwise draw it as non-hovered.
The only gotcha with this approach is if you have multiple buttons then you will need multiple flags, one per button. But you can use (Get|Set)WindowLongPtr(GWL_USERDATA) or (Get|Set)Prop() to store/retrieve each button's flag inside/from the button's HWND directly Or, you can just maintain your own std::map (or other lookup table) of HWND-to-flag associations.
Another option would be to have the subclass procedure use the WM_MOUSE(MOVE|HOVER|LEAVE) messages to send simulated WM_DRAWITEM messages to the button, specifying/omitting the ODS_HOTLIGHT style as needed so the drawing code can look for that style. You might need the subclass procedure to intercept WM_PAINT messages to make that work, though. Not sure about that.
Otherwise, you could simply have your WM_DRAWITEM handler grab the mouse's current coordinates and see if they fall within the button's client area, and then draw accordingly.

Deselect edit control win32 c++

How would I go about deselecting the text in edit control?
After entering the input I want the user to be able to deselect the edit control.
Because even after you click out of it and press a key, it gets entered into the edit.
Here is the code for my edit control:
HFONT fontMain = CreateFont(
-16, // Height Of Font
0, // Width Of Font
0, // Angle Of Escapement
0, // Orientation Angle
0, // Font Weight
false, // Italic
false, // Underline
false, // Strikeout
ANSI_CHARSET, // Character Set Identifier
OUT_TT_PRECIS, // Output Precision
CLIP_DEFAULT_PRECIS, // Clipping Precision
ANTIALIASED_QUALITY, // Output Quality
FF_DONTCARE|DEFAULT_PITCH, // Family And Pitch
TEXT("Calibri"));
HWND editControl = CreateWindow(
TEXT("EDIT"),
TEXT("TEST TEXT"),
WS_CHILD | WS_VISIBLE | ES_LEFT | ES_MULTILINE,
x, y, width, height,
window,
(HMENU) 100,
instance,
NULL);
SendMessage(window /* parent window */, WM_SETFONT, (WPARAM)fontMain, NULL);
DeleteObject(fontMain);
I have checked MSDN docs and have not found any additional styles to add to achieve the task.
If you have any ideas on how to achieve this task could you help me out?
Thank you.
You could use the same trick that works to dismiss dropdown list (of combo box), popup menus, and the like.
You'll need to subclass the EDIT control so you receive messages first to your own window procedure.
In your textbox subclass WM_SETFOCUS handler, call SetCapture so the next click is delivered to the textbox even if it's outside.
In the textbox subclasses's handler for mouse click messages, test the location and if outside the textbox, call SetFocus(NULL) to give up the focus. (This is where a popup would dismiss itself). Also call ReleaseCapture().
Also call ReleaseCapture() in the subclass's WM_KILLFOCUS handler, since mouse clicks are not the only way to lose focus.
The above is the way to have any click outside the textbox remove its focus. If you only want clicks in the client area of your parent window to defocus the textbox, then you can certainly skip the subclassing and capture, and just handle the mouse click events in the parent window, calling SetFocus(NULL) or SetFocus(parentHWnd).
I handled the WM_LBUTTONDOWN message in the window proc.
Such that when I click the mouse button on the parent window, it will set the focus to the parent window.
static LRESULT WINAPI WndProc
(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message){
case WM_LBUTTONDOWN : {
// Passing the parent window as the parameter.
SetFocus(HWND parentWindow);
}
}
}
This method works as the WM_LBUTTONDOWN might not be triggered if you click on any other window except the parent window.
There might be some exceptions to the above statement, but overall it works with buttons and edits but not static text for some reason.
The answers were provided by:
#PaulSanders and #BenVoigt

Is focus rect drawn automatically for listbox (WC_LISTVIEW)?

I have created a listbox similar to the code provided here. In my UI, will the tab focus rect be automatically drawn by DefWindowProc() or do I need to take care of that ? I don't see the focus rect being drawn on tab focus.
Thanks.
HWND CreateListView (HWND hwndParent)
{
INITCOMMONCONTROLSEX icex; // Structure for control initialization.
icex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);
RECT rcClient; // The parent window's client area.
GetClientRect (hwndParent, &rcClient);
// Create the list-view window in report view with label editing enabled.
HWND hWndListView = CreateWindow(WC_LISTVIEW,
L"",
WS_CHILD | WS_VISIBLE | LVS_LIST,
0, 0,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top,
hwndParent,
(HMENU)IDM_CODE_SAMPLES,
g_hInst,
NULL);
return (hWndListView);
}
Normally,the default window procedure draws a focus rectangle for the list box item drawn by the owner in response to the WM_DRAWITEM message.
In MSDN
For an empty list box or combo box, itemID can be -1. This allows
the application to draw only the focus rectangle at the coordinates
specified by the rcItem member even though there are no items in the
control. This indicates to the user whether the list box or combo box
has the focus. How the bits are set in the itemAction member
determines whether the rectangle is to be drawn as though the list box
or combo box has the focus.
If you are not using owner/custom draw then all Windows common controls will draw the focus rectangle for you automatically.
Windows 2000 (and later) hide keyboard accelerators and focus rectangles by default unless you are interacting with the window by using your keyboard.
Windows enables applications to hide or show various features in its UI. These settings are known as the UI state. The UI state includes the following settings:
focus indicators (such as focus rectangles on buttons)
keyboard accelerators (indicated by underlines in control labels)

How to set background color of window after I have registered it?

I am not using a dialog, I'm using my own custom class which I have registered and then used the CreateWindow call to create it, I have preset the background color to red when registering:
WNDCLASSEX wc;
wc.hbrBackground = CreateSolidBrush(RGB(255, 0, 0));
But now I want to change the background color at runtime, by e.g. clicking a button to change it to blue.
I have tried to use SetBkColor() call in the WM_PAINT, and tried returning a brush from the WM_CTLCOLORDLG message, they don't work.
Any help?
From Window Background comes:
...The system paints the background for a
window or gives the window the
opportunity to do so by sending it a
WM_ERASEBKGND message when the
application calls BeginPaint. If an
application does not process the
message but passes it to
DefWindowProc, the system erases the
background by filling it with the
pattern in the background brush
specified by the window's class.....
...... An application can process the
WM_ERASEBKGND message even though a
class background brush is defined.
This is typical in applications that
enable the user to change the window
background color or pattern for a
specified window without affecting
other windows in the class. In such
cases, the application must not pass
the message to DefWindowProc. .....
So, use the WM_ERASEBKGND message's wParam to get the DC and paint the background.
You may try the following:
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
Short answer: Handle WM_ERASEBKGND.
Longer answer:
When you register the WNDCLASS, you're providing information about all windows of that class. So if you want to change the color of just one instance of the window, you'll need to handle it yourself.
When it's time to repaint your window, the system will send your wndproc a WM_ERASEBKGND message. If you don't handle it, the DefWindowProc will erase the client area with the color from the window class. But you can handle the message directly, painting whatever color (or background pattern) you like.

How to draw in the nonclient area?

I'd like to be able to do some drawing to the right of the menu bar, in the nonclient area of a window.
Is this possible, using C++ / MFC?
Charlie hit on the answer with WM_NCPAINT. If you're using MFC, the code would look something like this:
// in the message map
ON_WM_NCPAINT()
// ...
void CMainFrame::OnNcPaint()
{
// still want the menu to be drawn, so trigger default handler first
Default();
// get menu bar bounds
MENUBARINFO menuInfo = {sizeof(MENUBARINFO)};
if ( GetMenuBarInfo(OBJID_MENU, 0, &menuInfo) )
{
CRect windowBounds;
GetWindowRect(&windowBounds);
CRect menuBounds(menuInfo.rcBar);
menuBounds.OffsetRect(-windowBounds.TopLeft());
// horrible, horrible icon-drawing code. Don't use this. Seriously.
CWindowDC dc(this);
HICON appIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
::DrawIconEx(dc, menuBounds.right-18, menuBounds.top+2, appIcon, 0,0, 0, NULL, DI_NORMAL);
::DestroyIcon(appIcon);
}
}
In order to draw in the non-client area, you need to get the "window" DC (rather than "client" DC), and draw in the "window" DC.
You should try handling WM_NCPAINT. This is similar to a normal WM_PAINT message, but deals with the entire window, rather than just the client area. The MSDN documents on WM_NCPAINT provide the following sample code:
case WM_NCPAINT:
{
HDC hdc;
hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN);
// Paint into this DC
ReleaseDC(hwnd, hdc);
}
This code is intended to be used in the message loop of your applicaton, which is canonically organized using a large 'switch' statement.
As noted in the MFC example from Shog, make sure to call the default version, which in this example would mean a call to DefWindowProc.
If you just want something in the menu bar, maybe it is easier/cleaner to add it as a right-aligned menu item. This way it'll also work with different Windows themes, etc.