I have a property sheet with several pages. Most of the pages have one or more edit controls.
Most controls are initialized not from the page dialogs but from the dialog that created the property sheet; some however are initialized in the page dialogs and they behave the same.
Everything starts out fine. One can move between the pages. None of the controls have the input focus.
If one clicks on one of the edit controls in a property sheet page establishing input focus one can modify the control. Again all seems in order.
If one then moves to a different property page, the first edit control in that page gets the input focus AND all the text in that control gets selected! This behavior applies to all the pages except one having an edit control with read only style. After that one can move back to other pages and the initial nothing selected no input focus behavior is restored.
All of the pages handle the PSN_QUERYINITIALFOCUS notification and return zero through the SetWindowLong mechanism.
Is this expected behavior?
And why isn't some control given focus initially?
My primary interest here is to somehow kill the selection. I have tried killing the selection with EM_SETSEL in the PSN_SETACTIVE notification to no avail.
The MSDN says the following under PSN_QUERYINITIALFOCUS "Otherwise, return zero and focus will go to the default control." How do I go about setting a control as default?
I find the the actions described above bizarre! I would still like to know
if they are normal.
why no control receives the focus initially.
I was able to kill the selection by adding code to the property sheet pages to handle the WM_COMMAND/EN_SETFOCUS message for any edit controls. I do not know if other controls
send EN_SETFOCUS messages.
case EN_SETFOCUS:
{
char cn[16];
HWND H = (HWND) lParam;
GetClassName (H, cn, 15);
if (strcmp (cn,"Edit") == 0)
{
SendMessage (H, EM_SETSEL, -1, 0);
}
return true;
}
I presume it would be possible to save any selection in an EN_KILLFOCUS handler and restore it
in the EN_SETFOCUS handler but doing so for an unknown number of controls would be tedious.
Related
I'm working with 3d party application. Changing a text in Edit control using
SendMessageW(m_edit_handle,WM_SETTEXT,0,str_address);
And it works fine.. It changes visually in the window. But once I click a button (also programmatically) it works as there is a default value but not the one I set with SendMessageW.
Just wondering if after changing the text in Edit window I have to call some other method to force Windows to update the actual value in the field?
Depending on how the target app is coded, you may need to issue an EN_CHANGE notification to the Edit's parent window. Sending WM_SETTEXT will not send that notification, as it is meant to be sent when the user makes changes to the Edit's content, not when code does.
SendMessageW(m_edit_handle, WM_SETTEXT, 0, str_address);
SendMessageW(GetParent(m_edit_handle), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(m_edit_handle), EN_CHANGE), LPARAM(m_edit_handle));
Can anyone point me at an WINAPI example of embedding a property sheet in a dialog box using WINAPI (not MFC)?
For the record, here are some of the things I learned while
investigating this question. I obtained most/all of this from
asking questions here or by searching the web. Thanks to all for
your help!
I have written a class to encapsulate everything I've discovered;
if interested in getting a copy send me an email mdorl#wisc.edu.
Pass the HWND of the parent window for your property sheet in
.hwndParent of the PROPSHEETHEADER when creating the property sheet
with the PropertySheet page function.
I used a picture control on the dialog as the parent of the property
sheet so that's the HWND I used as the hwndParent.
Change the style of the property sheet using the call back
pfnCallback field in the header, don't forget to include
PSH_USECALLBACK in dwFlags. See PropSheetProc Function in
the MSDN. In the PSCB_PRECREATE handler of the callback,
adjust the window style of the property sheet by removing
WS_POPUP and adding WS_CHILD. I did not want a border or title
bar on the property sheet so I also removed WS_CAPTION, WS_SYSEMNU,
& DS_MODALFRAME
LONG L = ((LPDLGTEMPLATE)lParam)->style;
L &= ~WS_POPUP;
L &= ~WS_CAPTION; // gets rid of title bar
L &= ~WS_SYSMENU;
L &= ~DS_MODALFRAME; // gets rid of border
L |= WS_CHILD; // 40000000
((LPDLGTEMPLATE)lParam)->style = L;
Using a child window instead of a popup window neatly solves
some problems such as moving the property sheet when the parent
moves, keeping the blue/gray state of the parent window properly
colored, and clipping issues. I.e. you don't have to worry about these things.
If you stopped here, you'd face another problem. The dialog support
code in WIN32 would get in a cpu loop trying to find the property sheet and
controls in its Windows. See
What is WS_EX_CONTROLPARENT and DS_CONTROL For?
for a pretty good explanation of
this problem. The solution is to add the WS_EX_CONTROLPARENT extended
style to the property sheet. You can find information on this
on the web by searching for WS_EX_CONTROLPARENT. What you won't find
caused me much grief. If you use a control on the dialog as the
parent, you must also set its WS_EX_CONTROLPARENT.
I do not know what the difference between WS_EX_CONTROLPARENT and DS_CONTROL is nor do I know if you could substitute DS_CONTROL for WS_EX_CONTROLPARENT. From the web I gather that DS_CONTROL has something to do with tabs. My test app works the same with respect to tabs with or without DS_CONTROL.
I took care of the WS_EX_CONTROLPARENT business after calling the
PropertySheet function to create the property sheet. I do not know
if you could do this in the call back routine or not. I suppose
you could use GetParent in the call back to get the HWND of the
control.
LONG S;
S = GetWindowLong (hwndPS, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS, GWL_EXSTYLE, S);
S = GetWindowLong (hwndPS_Area, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS_Area, GWL_EXSTYLE, S);
After creating the property sheet with the PropertySheet function,
you have to position it properly:
SetWindowPos(hwndPS, HWND_TOP, 2, 2, -1, -1,
SWP_NOSIZE | SWP_NOACTIVATE);
I allowed a couple of pixels for the border.
If you want to get rid of all the property sheet buttons and the space they take:
RECT rectWnd;
RECT rectButton;
GetWindowRect(hwndPS, &rectWnd);
HWND hWnd = ::GetDlgItem(hwndPS, IDOK);
if(!hWnd)
{
DebugBreak();
}
GetWindowRect(hWnd, &rectButton);
SetWindowPos (hwndPS, NULL, 0, 0,
rectWnd.right - rectWnd.left, rectButton.top - rectWnd.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
Individual buttons can be eliminated:
hwndOk = GetDlgItem (hwndPS,IDOK); // Hide the OK button
ShowWindow (hwndOk, SW_HIDE);
EnableWindow (hwndOk, FALSE);
You can get rid of the APPLY button using the PSH_NOAPPLYNOW
dwFlags in the PROPERTYSHEETHEADER when you create the property sheet.
The property sheet pages will NOT be created until the user first
activates them. I wanted them all to be created when the property
sheet was created.
unsigned int iP;
for (iP=0; iP<Header.nPages; iP++)
{
if (iP != Header.nStartPage)
SendMessage (hwndPS, PSM_SETCURSEL,iP,NULL);
}
SendMessage (hwndPS, PSM_SETCURSEL,Header.nStartPage,NULL);
There's a PROPSHEETPAGE flag that does the same thing (from the MSDN)
PSP_PREMATURE Version 4.71. Causes the page to be created when the
property sheet is created. If this flag is not specified, the page
will not be created until it is selected the first time.
But I was worried about the Version 4.71 business so I did it myself.
You can change the text on any of the buttons:
SetDlgItemText (hwndPS,ButtonID,Text);
where ButtonID is one of IDOK IDCANCEL IDHELP IDAPPLYNOW
IDAPPLYNOW is undefined on my system so
#define IDAPPLYNOW 0x3021
Here's how I intend to use my property sheet. I'm going to put all the initialization and
termination code in the dialog containing the property sheet and eliminate all the property sheet buttons. I can set all the initial values there and retrieve all the final values there when the user pushes some DO IT button. I notice that the act of setting, for example, a text box causes the containing page dialog to get a WM_COMMAND/EN_CHANGE. If you use this message to enable the APPLY button, you may need to disable the change flag for the page after setting any list boxes. I solved this problem by clearing all the changed flags after setting the initial value. (I suspect the property sheet clears the changed flag after all the INITDIALOG messages have been sent. In any case APPLY is not enabled if the text boxes are set in the page INITDIALOG handlers.) If I find an error I will select that property sheet page and put some red text in the parent dialog describing the error. The only thing the property sheet dialog procedures need to do is manipulate their controls.
One of the things that puzzles me is how to determine if all pages have returned PSNRET_NOERROR from their WS_NOTIFY/PSN_APPLY messages. I've had some success counting the
number of successive PSNRET_NOERROR replies setting the count to zero when a PSNRET_INVALID is reported or a PSN_KILLACTIVE is received. If the count reaches the number of pages in the property sheet header, I assume all pages have returned PSNRET_NOERROR. But I worry about things like disabled and invisible pages.
Is there a method such that a user can click on the form itself, and in doing so remove focus from whatever object (textbox, combobox, etc) currently has it? Basically, can focus be uniformly removed from everything at once?
Setting the focus to the form itself does not work.
I thought about doing the old "hide a placeholder button behind another object" trick, but I'm really not a fan of that.
Thanks!
In VB6 a PictureBox can get focus, even if it does not contain any control.
In your case you can put a PictureBox with TabStop false, BorderStyle set to 0, TabIndex set to 0 behind every other control but not containing any focusable control and stretch it to ScaleWidth by ScaleHeight at run-time.
You have to put the labels and any windowless control in this background PictureBox too.
This way when the user clicks "on the form" the focus will "go away". With "no focus" Tab key will focus first control (the one with TabIndex set to 1).
When a form is active, something generally HAS to have focus. It sounds like you're just wanting to not "show" that a particular control has focus.
If that's the case, it's going to depend on the controls. Some have properties that control whether or not the specific control "indicates" its focus in some way.
But the built in Windows controls will always show their focus state unless you subclass them
Given this problem. I'd probably put a button on the form , then move it offscreen when the form loads. Make sure it's not a tab stop, but then when you want to hide focus, set focus specifically to the button, make sure the button is STILL in the tab order, even though it's not a tab stop, so the user can press tab while on the button and end up somewhere logical.
Don't have VB handy, but could you simply remove TabStop?
for x = 1 to me.Controls.count
me.Controls(x).TabStop = 0
next
I have a picturebox and a control on a form.
Private Sub cmdButton_Click
PictureBox.setFocus
Exit sub
End sub
The control doesn't change its appearance, nor does the picturebox.
Of course you'll need to add an If-Then clause if you want the control to respond normally sometimes.
I'm working on a Windows project with a simple dialog created with CreateWindowEx() it contains multiple panes loaded with CreateDialog() to load the layout from a resource file. On the child panes there are a number of controls including text boxes and buttons which I would like to use TAB to navigate around but all I get is the Windows 'bing' telling me that the key does not do anything. My message loop looks like this:
while( PeekMessage(&msg, 0, 0, 0, PM_REMOVE) )
{
if( !IsDialogMessage(0, &msg) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
And each control window has WS_TABSTOP set in the style as well as the owner pane having WS_EX_CONTROLPARENT set.
Is there anything else I need to do to make the tab key work?
Thanks,
J
Try this:
http://support.microsoft.com/kb/71450 (How To Use One IsDialogMessage() Call for Many Modeless Dialogs)
You panes are modeless dialogs, and IsDialogMessage is responsible for handling Tab keys. I hope that this article exactly matches your case.
The WS_TABSTOP Style
The WS_TABSTOP style specifies the controls to which the user can move by pressing the TAB key or SHIFT+TAB keys.
When the user presses TAB or SHIFT+TAB, the system first determines whether these keys are processed by the control that currently has the input focus. It sends the control a WM_GETDLGCODE message, and if the control returns DLGC_WANTTAB, the system passes the keys to the control. Otherwise, the system uses the GetNextDlgTabItem function to locate the next control that is visible, not disabled, and that has the WS_TABSTOP style. The search starts with the control currently having the input focus and proceeds in the order in which the controls were createdthat is, the order in which they are defined in the dialog box template. When the system locates a control having the required characteristics, the system moves the input focus to it.
If the search for the next control with the WS_TABSTOP style encounters a window with the WS_EX_CONTROLPARENT style, the system recursively searches the window's children.
An application can also use GetNextDlgTabItem to locate controls having the WS_TABSTOP style. The function retrieves the window handle of the next or previous control having the WS_TABSTOP style without moving the input focus.
Source: MSDN.
if( !IsDialogMessage(0, &msg) )
The first argument should not be NULL, it must be the handle of a dialog. Painful here of course.
i need to implement the functionality of EM_SETCUEBANNER, where a text hint appears inside an Edit control:
The catch is that i cannot use version 6 of the Common Controls, which is what is required to get the Microsoft supplied implementation of a cue banner.
i've looked into simply changing the text of the edit control, and the font format to
Dark Gray Italic Text
but it will throw Change events (component wrapper provided by higher component library) that i can't find a way to avoid.
So i was instead going to custom draw the text, drawing the Cue Banner text when the control is unfocused and empty, and rely on default painting otherwise.
The Edit control doesn't nicely expose a custom drawing mechanism, like ListView, TreeView and others provide.
Other people have looked into it, but it seems to be an nearly impossible task:
From the way things are looking, I'll
have to handle the following
messages:
WM_ERASEBKGND, WM_PAINT (for obvious reasons)
WM_SETFOCUS, WM_KILLFOCUS (to prevent
the white bar from displaying --
described above)
WM_CHAR (to process and update the
text in the control)
And I also need to find a way to
display the caret in the control,
since I haven't found a way to allow
Windows to do that for me without also
painting the white bar I mentioned.
This is going to be fun. :rolleyes:
Given that the Windows Edit control was never meant to be custom drawn: does anyone know how to custom draw a Windows Edit control?
Note: i will also accept answers that solve my problem, rather than answering my question. But anyone else wanting to custom draw an Edit control, coming across this question, would probably like an answer.
Custom drawing an Edit control is essentially impossible. There are a few specialized cases were you are doing so little that can get away with it, but you risk breaking badly in the next revision of windows (or when someone runs your app on an older version, or via terminal services, etc).
Just taking over WM_PAINT and WM_ERASEBKGROUND aren't good enough, because the control will sometimes paint on other messages as well.
You are better off just writing your own edit control. I know that's a huge amount of work, but in the long run it will be less work than trying to hack your way into taking over all of the Edit controls' drawing code.
I remember back in the good old days when everyone use to subclass the button control to add color and graphics, etc. The thing is, one day I sat down and just wrote my own button window class. and it was LESS CODE than what we had in our source tree to subclass and custom draw the Windows button.
Create a window class of your own that looks like and empty edit control, that draws the cue text and shows a caret and has focus. Create the edit control also, but position it behind your window. (or leave it hidden)
Then when you get the first WM_CHAR message (or WM_KEYDOWN?). You put your window behind the edit conrol, give focus to the edit, and pass the WM_CHAR message on. From then on the edit control will take over.
You can listen to EN_CHANGE notifications from the edit control if you need to go back to showing your cue text when the edit gets empty. But I'd think that it would be fine to go back to the cue text only when the edit looses focus AND is empty.
Subclassing the EDIT control worked well for me - needed to display some formatting information to the user when editing object attributes (and some attributes could be multiple lines). The important thing, like Adrian said in his answer too, is to call the EDIT control's procedure before your own drawing. Calling it afterward or issuing your own BeginPaint/EndPaint (with return 0 or DefWindowProc) caused issues for me from the text not displaying at all, to it displaying only when resizing but not after editing, to leaving screen litter of the leftover caret. With that, I haven't had any issues regardless of the EDIT control's other repaint times.
Some setup:
SetWindowSubclass(attributeValuesEdit, &AttributeValueEditProcedure, 0, reinterpret_cast<DWORD_PTR>(this));
// Not only do multiline edit controls fail to display the cue banner text,
// but they also ignore the Edit_SetCueBannerText call, meaning we can't
// just call GetCueBannerText in the subclassed function. So store it as
// a window property instead.
SetProp(attributeValuesEdit, L"CueBannerText", L"<attribute value>");
The callback:
LRESULT CALLBACK AttributeValueEditProcedure(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam,
UINT_PTR subclassId,
DWORD_PTR data
)
{
...
case WM_PRINTCLIENT:
case WM_PAINT:
{
auto textLength = GetWindowTextLength(hwnd);
if (textLength == 0 && GetFocus() != hwnd)
{
// Get the needed DC with DCX_INTERSECTUPDATE before the EDIT
// control's WM_PAINT handler calls BeginPaint/EndPaint, which
// validates the update rect and would otherwise lead to drawing
// nothing later because the region is empty. Also, grab it from
// the cache so we don't mess with the EDIT's DC.
HDC hdc = (message == WM_PRINTCLIENT)
? reinterpret_cast<HDC>(wParam)
: GetDCEx(hwnd, nullptr, DCX_INTERSECTUPDATE|DCX_CACHE|DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS);
// Call the EDIT control so that the caret is properly handled,
// no caret litter left on the screen after tabbing away.
auto result = DefSubclassProc(hwnd, message, wParam, lParam);
// Get the font and margin so the cue banner text has a
// consistent appearance and placement with existing text.
HFONT font = GetWindowFont(hwnd);
RECT editRect;
Edit_GetRect(hwnd, OUT &editRect);
// Ideally we would call Edit_GetCueBannerText, but since that message
// returns nothing when ES_MULTILINE, use a window property instead.
auto* cueBannerText = reinterpret_cast<wchar_t*>(GetProp(hwnd, L"CueBannerText"));
HFONT previousFont = SelectFont(hdc, font);
SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
SetBkMode(hdc, TRANSPARENT);
DrawText(hdc, cueBannerText, int(wcslen(cueBannerText)), &editRect, DT_TOP|DT_LEFT|DT_NOPREFIX|DT_NOCLIP);
SelectFont(hdc, previousFont);
ReleaseDC(hwnd, hdc);
// Return the EDIT's result (could probably safely just return zero here,
// but seems safer to relay whatever value came from the edit).
return result;
}
}
break;
Writing your own EDIT control (which I've actually done more than once, to partial degrees of completeness compared to the built-in one) is not much work if you do the bare minimum (maybe English only with basic caret support), but it's a LOT of work to get correct if you want caret navigation over complex scripts with variable sized clusters, selection over ranges, IME support, context menus with copy and paste, high contrast modes, and accessibility features such as text to speech. So unlike so many other answers, I recommend not implementing your own EDIT control merely for cue banner text.
Subclass the edit control. Handle WM_PAINT by first calling the original window procedure and then, if it's empty and not in focus, draw the cue text. Pass every other message to the original window procedure.
I've done this--it works. The problem the CodeGuru person had doesn't seem to apply to your situation. I believe he's trying to do more to the appearance. For performance, it looks like the edit control is doing some updates outside of WM_PAINT processing (probably for performance). That's going to make it nearly impossible to take complete control of the appearance. But you CAN draw the cue prompt.
And I also need to find a way to display the caret in the control,
since I haven't found a way to allow Windows to do that for me without
also painting the white bar I mentioned.
If you want to handle WM_PAINT by yourself without forwarding the message to the original windowproc of your superclass, you should not forget to call DefWindowProc. So that the caret will be drawn.
To avoid the white bar you should remove class brush with SetClassLongPtr.
And somehow keep your DC's clipping region to clip Edit controt's ExtTextOut outputs.
The white bar may be the result of OPAQUE option passed to ExtTextOut by Edit control.
Conclusion: Write your own control. No pain, no gain.