Why is my edit control ignoring the applied visual styles? - c++

I have a window with several controls on it, but only the edit control is ignoring the visual styles. I've tried to track down the cause but have had no luck so far. Here is what it looks like:
As seen, the button and the listbox controls have the proper visual style. The edit control, however, does not. I had the proper style applied once a few days ago after changing a bunch of code unrelated to it so I know it's possible.
Things to note:
I have tried calls to InitCommonControls and InitCommonControlsEx with no success.
I have the WS_EX_CLIENTEDGE applied to the edit control and have always had that extended style turned on, even when it was working prior.
I have included as much code as I can to help track down this issue.
From my TextBox class (which inherits Component):
protected:
virtual void OnRegistering(CreationParameters& createParams)
{
// this is passed in as the lpClassName parameter in
// CreateWindowEx() and the lpszClassName parameter in WNDCLASSEX
createParams.BaseClassName = WC_EDIT;
// passed as the dwStyle parameter in CreateWindowEx()
createParams.WindowStyles |= ES_AUTOHSCROLL | ES_AUTOVSCROLL;
// passed as the dwExStyle parameter in CreateWindowEx()
createParams.WindowExStyles |= WS_EX_CLIENTEDGE;
}
Would an issue with handling the WM_CTLCOLOREDIT or WM_ERASEBKGND messages have anything to do with this? Or an issue with handling fonts? Or something else? Because I am 100% out of ideas at this point.

The issue was that the HBRUSH I was using to color the edit control was, somehow, inexplicably never initialized and was NULL. Since I know other people were having issues with this, here is my solution.
The problem manifested itself when WM_CTLCOLOREDIT arrived in the message queue. If an improper/incorrect HBRUSH is returned when that message arrives, the edit control reverts back to old visual styles. Since my HBRUSH was NULL, the old visual style was applied to the edit control.

Related

Scrolling text gets negated in static control (C++ WinAPI)

I have some weird control,
CreateWindowEx(!tset&USE?WS_EX_OVERLAPPEDWINDOW:0,
tset&USE?"static":"edit",0,WS_CHILD|WS_VISIBLE|
(!tset&USE?ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|WS_VSCROLL //when it's edit
:SS_NOTIFY|SS_EDITCONTROL),5,60,390,474,hwnd,HMENU(10),0,0); //when it's static
SetWindowSubclass(GetDlgItem(hwnd,10),tset&USE?BOR:NoMenu,0,0);
whose everything depends on whether themes are active. Here tset is an instance of enum type for bit flags. Everything is fine except scrolling when the control is in static mode. It wasn't receiving WM_MOUSEWHEEL messages, I made it receive them in the following way:
LRESULT CALLBACK BOR(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp,UINT_PTR,DWORD_PTR)
{
if(msg==WM_MOUSEMOVE&&GetDlgCtrlID(hwnd)==10)
{
POINT x{LOWORD(lp),HIWORD(lp)}; ClientToScreen(hwnd,&x);
RECT rc; GetWindowRect(hwnd,&rc); if(PtInRect(&rc,x)&&GetCapture()!=hwnd){SetCapture(hwnd); hwnd3=SetFocus(hwnd);}
if(!PtInRect(&rc,x)&&GetCapture()==hwnd){ReleaseCapture(); SetFocus(hwnd3);} //hwnd3 is an instance of HWND defined in global scope, for giving focus back
}
if(msg==WM_MOUSEWHEEL)
{
RECT rc; GetWindowRect(GetParent(hwnd),&rc);
HRGN x; GetWindowRgn(GetParent(hwnd),x);
ScrollWindowEx(hwnd,0,short(HIWORD(wp)),0,0,x,&rc,SW_INVALIDATE|SW_ERASE);
ShowWindow(GetParent(hwnd),SW_HIDE); ShowWindow(GetParent(hwnd),SW_SHOW);
SetFocus(hwnd);
return 1;
}
return DefSubclassProc(hwnd,msg,wp,lp);
}
Now when mouse enters client area of my control, if it's in static mode, it captures mouse and gets focus. So it intercepts WM_MOUSEWHEEL messages in its subclass callback function and I can scroll my control in the static mode. Here's the exact problem: few milliseconds later, right after its text is scrolled, the window somehow updates itself to its initial unscrolled state. And my efforts get negated. Is it clear why it behaves like this, and how to fix that?
#Edit: Why don't I simply add WS_VSCROLL to its static version? Because it has SS_NOTIFY style and I need it to respond to WM_COMMAND messages somehow else. This is why I tried scrolling it manually.
#Update:
Here in both screenshots the text length is the same. When the control is in edit state, its vertical scrollbar is proper. But when it's in static state the vertical scrollbar doesn't match the actual text length. Besides, the scrollbar is always at the same size and same position independent from text length and it's also unscrollable. Why may be the reason of it behaving like this?
#Update: Thanks to #enhzflep's comment, problem is easily solved some other way.
In the interests of removing the question from the Unanswered list, I'll repeat and expand upon one of the comments.
Looking at the images, we can immediately see a clear difference in the way they're each presented. The left image has each line starting with a different character, while the right image always starts with the same character. They also have very differently sized thumbs in the scroll-bar.
Since the problems seem to be stemming from trying to use two different controls to achieve essentially the same task, I'd suggest always using an edit control. (this is the one that appears to behave nicely, and is the one that doesn't wrap the text)
Rather than use two different types of control, I suggest just toggling the read-only flag. With luck (!) the only difference now will be the way mouse and keyboard messages are handled - hopefully you'll get similar behaviour and appearance with such an approach.
The message I'm thinking of that will achieve this is EM_SETREADONLY.
Although the question has been successful answered, I can't find reference in the Microsoft docs to support my theories, thus used language like 'hopefully'.

SetParent: Window reacts slow when getting focus

I have a problem with the SetParent function of the Windows API. I'm using it to inject one of my windows into the Window of an external application (like Notepad). My Window contains an EditWindow as Child Window and a few buttons, nothing more. I use something like this:
long style = GetWindowLong(hwnd, GWL_STYLE);
style |= WS_CHILD;
style &= ~WS_POPUP;
SetWindowLong(hwnd, GWL_STYLE, style);
SetParent(hwnd, newParent);
This works, however if I click from outside of the external application into the textbox, it takes several seconds until it gets the focus. In the meantime nothing happens, even with Spy++ I can't catch any messages within this delay.
The strange thing is also that this only seems to happen with my specific application (its some very old and special measurement software), with notepad for example the delay does not happen.
So, here is my question: What could potentialy cause this problem? Are there cases where SetParent could cause such a behaviour?
Greets,
cpp_beginner

Using a manifest causes window to remain partially blank

I am in the process of trying to make an app that has most of its development done in Visual C++ 6.0 adhere to Windows theming, or to be precise the visual styles aspect of it. The application in question is a MFC application with the MBCS characterset. Getting theming to kick in in the first place required the InitCommonControlsEx(...); trick combined with the proper manifests for the Common Controls 6.0. Nothing special thus far.
With all that done, I have tons of windows which have some semblance of drawing glitches showing - white rectangles on the background where there used to be gray like there is in the rest of the window, but they do not hinder.
The biggest troublemaker is in a CDialog descendant that implements an (archaic) 'did you know...' tip dialog. It refuses to draw anything other than the buttons, checkbox and decorative frame the moment the manifest is in place. However, if I take the manifest OUT, everything works fine.
Comparison images right after invoking the dialog
I have already stepped through the OnPaint code (which draws the icon and the 'did you know' text, and all functions return sensible and successful values. I also tried removing the entire customdrawn bits (commented out the mappings for the WM_PAINT and WM_CTLCOLOR messages), but even that caused nothing to show or act different for that matter.
Once I click the 'Next Tip' button, it will properly paint the next tip in the list. However, the customdrawn header with the bulb icon remains missing.
The code really does nothing special that I can spot, and I'm at a loss. Since I suspect the dialog definition is of most help, I'll include that, although I can post other things if people have ideas on where the problems are hiding.
IDD_TIP DIALOG DISCARDABLE 0, 0, 231, 164
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Tip of the Day"
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME,12,11,207,123
LTEXT "Some String",IDC_TIPSTRING,28,63,177,60
CONTROL "&Show Tips on StartUp",IDC_STARTUP,"Button",
BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,13,146,85,10
PUSHBUTTON "&Next Tip",IDC_NEXTTIP,109,143,50,14,WS_GROUP
DEFPUSHBUTTON "&Close",IDOK,168,143,50,14,WS_GROUP
CONTROL "",IDC_BULB,"Static",SS_BITMAP,20,17,190,111
END
The machine I am testing this on is a W7 64-bit machine running VS2010. The application in question itself is 32-bit.
Edit:
A new day provides new insights. Somehow, the issue is with the handling of the WM_CTLCOLOR message. When I set it to return a NULL_BRUSH, I finally saw things being drawn (that of course blurred on top of another after initial drawing). Nailing it down even further, it seems that when nCtlColor == CTLCOLOR_STATIC, if I return a NULL_BRUSH, stuff will display. The moment I make it a WHITE_BRUSH nothing gets drawn again, however.
Original code:
HBRUSH CTipDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if (pWnd->GetDlgCtrlID() == IDC_TIPSTRING)
return (HBRUSH)GetStockObject(WHITE_BRUSH);
else if (nCtlColor == CTLCOLOR_STATIC)
/* Visual styles enabled causes the things we draw in our WM_PAINT
* to get cleared (and not redrawn) if we do not use a hollow brush here.
* Likely the default behaviour differs when using theming. */
return (HBRUSH)GetStockObject(HOLLOW_BRUSH);
return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}
Problem solved! The lines that are in bold are what I changed to fix my problem, including the obligatory comments. :)
As far as I know, version 6 of the Common Controls only officially supports Unicode.
According to a blog entry by Raymond Chen, those controls work with the ANSI character set to a degree, but only for backwards compatibility reasons. No one should be using that intentionally.
If you're an ANSI application and you create controls from the common controls library, you may encounter strange behavior. It'll mostly work, but things may be weird at the fringe.
...
And it means that all you folks who are using version 6 of the common controls but haven't converted to Unicode are relying on a compatibility loophole. The ANSI support is there for the old programs that thought they were talking to a version 5 common control; it isn't there for you.
I've never tried doing such a thing so I cannot say for sure, but you may be encountering some of this "strange behavior". If that is the case, then the only officially supported options would be to either convert to Unicode or go back to using version 5 of the controls. Otherwise you would be left trying to find workarounds to any odd behavior you might find, and that doesn't sound like a good situation to be in.

Custom MFC control contains another control - messages not getting through

I have a custom CWnd-derived MFC control, which works like this:
the control has its own OnPaint, and a black background
clicking anywhere on the control causes an edit control to appear at that location, borderless and with black-background, so it blends in
the user types in this box and hits enter, the box disappears and the control's custom paint functionality renders the same text in the same position on the background.
So our control owns a CCustomEdit, when you click on the background the control is either created or moved, and made visible:
CCustomEdit::Show(Rect &rc,CCustomControl *pParent)
{
if ( !::IsWindow( m_hWnd ) )
{
Create( ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | ES_NOHIDESEL | ES_CENTER | ES_UPPERCASE, rc, pParent, 999 );
}
else
MoveWindow( &rc );
}
The main parts actually work OK (and we're stuck with the approach). But one thing that's not working is that CCustomEdit self-registers for EN_CHANGE events and so on. When CCustomEdit is created as a normal dialog control (CEdit on the dialog template, DDX-bound to a CCustomEdit variable) these work, but within CCustomControl they are not.
CCustomEdit::PreSubclassWindow() calls SetEventmask() and is being called. And CCustomEdit's ON_CHAR handler is also being called for key-presses in the edit box, however the handlers for edit-box messages like EN_CHANGE are not.
Are there any obvious things like changing the style flags? Otherwise, why is my custom control stopping these events reaching the contained edit control?
I'm not sure I understand the situation, but I have a number of control that work roughly in the same way as what I think is happening and they all work, it is possible.
EN_CHANGE for the edit control is send to your CWnd-derived control. Are you reflecting the messages? Have you tried if EN_CHANGE gets to the custom control? From what you are describing, you are expecting EN_CHANGE to end up in CCustomEdit's message dispatcher macro chain automatically, but it doesn't; you need the help of the containing window. Now MFC does most of that for you in a CDialog, but if you roll your own you need to do it manually, or use the message reflection macro's.
I found it... somehow, my SetEventMask() was being overriden. I don't know how or where but when I added an extra call later on to test, most event handlers started getting called.
I can only assume some part of the init code in MFC is responsible.

Windows XP Style: Why do we get dark grey background on static text widgets?

We're writing Windows desktop apps using C++ and Win32. Our dialog boxes have an ugly appearance with "Windows XP style": the background to the static text is grey. Where the dialog box background is also grey, this is not a problem, but inside a tab control, where the background is white, the grey background to the text is very noticeable.
In the past we have done a lot of our own drawing of controls, but these days we are trying to use the standard look'n'feel as much as possible, and to avoid overriding standard behaviour as much as possible.
We are using the Win32 API, which is getting a bit dated, but I think the problem occurs even with ATL. We are creating a DIALOGTEMPLATE. The text is in a "static" control (0x0082). The only flag we set for the style is "SS_LEFT". The text control is inside a tab control: "SysTabControl32" with only one flag: WS_CLIPSIBLINGS set on it. I've experimented with SS_WHITERECT and WS_EX_TRANSPARENT and other settings, to no avail.
All of this gets drawn with the standard Windows dialog box message handler. My main question is "what are we doing wrong?" rather than "how can I work around it?", although I'll settle for the latter if no-one can help me with the first.
Any ideas?
The usual way of implementing pages in a tab control is required to access MS's solution to this problem :-
Instead of creating individual controls in the tab area, create a modeless child dialog for each page and have the controls on that. Create the page dialogs with the main dialog (not the tab) as their parent and as the user switches between tabs, simply show and hide the relevant page dialog.
In the WM_INITDIALOG handler for each page, call the uxtheme API EnableThemeDialogTexture
With the ETDT_ENABLETAB flag this automatically changes the background color of the dialog and all its child controls to paint appropriately on a tab.
If you have overridden WM_ERASEBKGND or WM_CTLCOLOR* in your pages DialogProc you will need to revert to default handling (return FALSE) as these methods are where the dialog code is going to do its heavy lifting. The style bits should simply be set as though the child page dialog page was going to be created on a normal windows 9X tabbed dialog.
The reason why the background is gray is because that is the default.
To override it, you can process the WM_CTLCOLORSTATIC message in the parent window and return a custom brush.
Your problem is not with ATL or WinAPI. In MFC there is the same problem.
Set Tab control as parent window for Static controls. But I think that overriding WM_DRAWITEM is more flexible solution.
I've got Win32/MFC applications with labels inside tabs on dialogs, and the background colour looks fine on them (that is, it reflects the XP-look theme, rather than being flat grey) without any obvious special handling.
Under the hood what's supposed to happen is that the label posts a WM_CTLCOLOR message to its parent, which sets the device context up appropriately: the default handling in Windows should set the appropriate background colour, at least for dialogs and tab controls.
One possibility is that you're doing something non-standard in your handling of WM_CTLCOLOR: is it over-ridden somewhere in your application? Possibly you have some old code that is setting the label background colour in this way.
(Also, as Rob asks, are you using a manifest to get comctl32 6.0 into your application?)
We're not overriding the WM_CTLCOLORSTATIC message. There's no occurrence of this string in our source code and nothing like it in our message handlers.
We've worked around this problem by overriding the WM_DRAWITEM message for tab controls to paint their contents with the grey background (standard for dialog boxes without tab controls) rather than the white background (standard for the contents of tab controls).
brush = CreateSolidBrush(GetSysColor(COLOR_MENU));
FillRect(lpdis->hDC, &lpdis->rcItem, brush);
SetBkColor(lpdis->hDC, GetSysColor(COLOR_MENU));
wtext = ToWideStrdup(c->u.tabcontrol.Tabs[lpdis->itemID].name);
rect = lpdis->rcItem;
rect.top += DlgMarginY - 1;
rect.bottom += DlgMarginY;
DrawTextW(lpdis->hDC, wtext, -1, &rect, DT_CENTER | DT_VCENTER);
free(wtext);
DeleteObject(brush);
This is obviously a workaround, not a proper answer to my question.
Incidentally, we initialise the "common controls", of which I believe the tab control is one, using code like this...I don't suppose this is related to the issue?
#pragma comment(linker, "/manifestdependency:\"type='win32' " \
"name='Microsoft.Windows.Common-Controls' " \
"version='6.0.0.0' " \
"processorArchitecture='*' " \
"publicKeyToken='6595b64144ccf1df' " \
"language='*'\"")
...
hCommCtrl = GetModuleHandle("comctl32.dll");`
if (hCommCtrl) {
ptrInit = (TfcInit_fn) GetProcAddress(hCommCtrl, "InitCommonControlsEx");
if (ptrInit) {
data.dwSize = sizeof(INITCOMMONCONTROLSEX);
data.dwICC = ctrlClass;
if (ptrInit(&data) )
gCommCtrlsInitialized |= ICC_TAB_CLASSES | ICC_BAR_CLASSES;
}
}
Have spent an amazing amount of time to try to fix a problem so seemingly simple, tried almost every constant for hbrBackground without success.
As an amateur found that the most simple & time efficient fix was to simply create a "Static" class child window that envelops the entire window. Just another hack level fix though