MFC: How do you get CMFCToolBarComboBoxButton to show on a CMFCToolBar? - winapi

Something I thought would be easy I can't get to work. How do I get a ComboBox to show on the CMFCToolBar? Here's what I have done that doesn't work (meaning it just shows the original placeholder button with the circle (grayed out)).
1 - I added a button to the toolbar in resource editor giving it the id ID_EDIT_FIND_COMBO (I also put a circle in it just to know it's in use).
2- in CMainFrame I added ON_REGISTERED_MESSAGE(AFX_WM_RESETTOOLBAR, OnToolbarReset) to message map and the OnToolbarReset() function below:
afx_msg LRESULT CMainFrame::OnToolbarReset(WPARAM wparm, LPARAM )
{
UINT uitoolbarid = (UINT) wparm;
if (uitoolbarid == IDR_MAINFRAME) {
CMFCToolBarComboBoxButton btncombo(ID_EDIT_FIND_COMBO, GetCmdMgr()->GetCmdImage(ID_EDIT_FIND));
m_wndToolBar.ReplaceButton(ID_EDIT_FIND_COMBO, btncombo);
}
return 0;
}
The ReplaceButton() returns 1 saying it replaced it.
What am I missing?
To be clear, here's what MS says to do which is what is done above:
1 - Reserve a dummy resource ID for the button in the parent toolbar resource. For more information about how to create buttons by using the Toolbar Editor in Visual Studio, see the Toolbar Editor article.
2 - Reserve a toolbar image (button icon) for the button in all bitmaps of the parent toolbar.
3 - In the message handler that processes the AFX_WM_RESETTOOLBAR message, do the following steps:
a. Construct the button control by using a CMFCToolbarButton-derived class.
b. Replace the dummy button with the new control by using CMFCToolBar::ReplaceButton. You can construct the button object on the stack, because ReplaceButton copies the button object and maintains the copy.
TIA!!
Here's a sample project where it doesn't work. Just the button on the toolbar, not a combobox. Even took code sample from MS sample.
Sample Project

The answer is that MFC caches the toolbar so if you ever run it without the combo, once you replace the button to use the combo it still doesn't use it. You can make it pick up the change by either going to customize for the toolbars (if you have that option enabled) and choose Reset All, otherwise, you'll find under Computer\HKEY_CURRENT_USER\Software\{NameAsUsedInSetRegistryCall}\{appname}\Workspace all the cache items, of which the various MFCToolBar entries. Delete the key and run app again, then it works. Why they wouldn't make it smart and have a timestamp to know if to automactially update, not sure?

Related

WIN32: Duplicate Standard Button Control (disabled Icon / hotkey underline) in Owner Draw Button?

So for the simple feat of wanting to put an icon on the right side of button text instead of the left resulted in having to use owner draw buttons (but someone here said Custom Draw is actually available if using visual themes). Okay, fine, but now I'm finding you can't really duplicate what Windows standard buttons do when it's not in owner draw mode.
For a normal enabled button I can get the look correct by checking if visual styles are available or not and then using either the DrawThemeBackground() / DrawThemeText() or DrawFrameControl() / DrawText(). However the hot key underline character is shown even when alt key is not pressed, the default buttons don't show it until alt pressed.
For a disabled button, I can't duplicate the disabled look of the icon placed on the button. I tried DrawState() over DrawIconEx() but that looks like the old Windows 3.1 type grey graphic not the visual style dimmed graphic. I see there is a DrawThemeIcon() for an image list, I guess I could try that (I'd have to test non visual style mode to see if DrawState() matches when not using visual styles).
Also, as you hover over the button, the state doesn't change, I understand that if using owner draw, that doesn't occur, maybe it would still work with Custom Draw?
So the two main questions are:
1 - Is there something built-in to the button / owner draw to handle the underlined hotkey only when alt was pressed?
Update to Question 1: I found DT_HIDEPREFIX in DrawText() and using Custom Draw there is the CDIS_SHOWKEYBOARDCUES flag. However with Owner Draw I'm not sure if there is a flag someplace?
2 - How do you draw an icon for a button that is disabled to match what default buttons do?
TIA!!
For shortcut underline you can use WM_QUERYUISTATE to ask if it should be hidden or visible
DWORD draw_text_flags = ...;
if ( SendMessage( control_hwnd, WM_QUERYUISTATE, 0, 0 ) & UISF_HIDEACCEL ) != 0 )
{
// hide prefix
draw_text_flags |= DT_HIDEPREFIX;
}
// some combination of PBS_DEFAULTED, PBS_DISABLED, PBS_HOT, PBS_NORMAL, PBS_PRESSED;
int state = ...;
DrawThemeText( theme, hdc, BP_PUSHBUTTON, state, text, text_len, draw_text_flags, 0, rect );
Answer to Q2: If you create an HIMAGELIST using ILC_COLOR32 | ILC_MASK, and use ILD_NORMAL|ILD_BLEND25 on ImageList_Draw() it gives the same look as windows default buttons for a disabled button.
Based on responses from #Remy-Lebeau and #Daniel-Sęk and reviewing various projects on CodeProject, I create an easy to use class to handle this. It can be found at CodeProject. Thanks Guys.

Place cursor in edit control box in visual studio

I am trying to make a password dialog box in a visual studio software. As soon as my password dialog box opens, I want the cursor to be placed in the edit control box so I can type password on it.
How can I put or place cursor in a text edit box in visual studio MFC without a mouse click?
Please suggest me how to do this.
static CEdit *ptrCurrentEditWindow;
BOOL GET_PASSWORD::OnInitDialog()
{
CDialog::OnInitDialog();
SetDlgItemText(IDC_PASSWORD, L"");
ptrCurrentEditWindow = &m_wnd_password;
return TRUE;
}
The documentation for CDialog::OnInitDialog explains, how to do this. The return value section has this information:
Specifies whether the application has set the input focus to one of the controls in the dialog box. If OnInitDialog returns nonzero, Windows sets the input focus to the default location, the first control in the dialog box. The application can return 0 only if it has explicitly set the input focus to one of the controls in the dialog box.
That leaves you with 2 options:
Make your edit control the first control, and have Windows handle everything for you by returning TRUE.
Move input to your edit control manually (CDialog::GotoDlgCtrl), and return FALSE.
Addressing the updated question: The implementation already does, what you need, as long as you make IDC_PASSWORD the first control in your dialog template. If you do not want to or cannot arrange for this, you'll have to manually move input focus, like this:
BOOL GET_PASSWORD::OnInitDialog()
{
CDialog::OnInitDialog();
// Not needed; an edit control is initially empty
SetDlgItemText(IDC_PASSWORD, L"");
// Set input focus to the desired control
GotoDlgCtrl(GetDlgItem(IDC_PASSWORD));
// Let the framework know, that you already set input focus
return FALSE;
}

VB6 + how to switch between windows/frame in form by buttons

I am very new beginner with VB6 and I hope I explain the things right
I want to create form with 2 buttons (the buttons are located on the top form position )
So each button will switch to other form/window/frame
For example
The first button will show window 1 (there I can set only parameters)
The second button will show window 2 (there I can set only IP address)
Please advice if we can do that by VB6 ?
And if yes how to do that ( step by step )
Remark - Similar example but with multiple windows in the same form is the system properties ( right click on my computer and properties ) , the we can see each button will view different window
Create a form with 2 buttons, Command1 and Command2.
On this form, create 2 frames, Frame1 and Frame2. hide Frame2 and make sure to line up both framesso that they are of the same size and located right on top of each other (Top, Left, Width and Height properties must be the same)
Now put this code in:
Private Sub Command1_Click()
Frame1.Visible = True
Frame2.Visible = False
End Sub
Private Sub Command2_Click()
Frame1.Visible = False
Frame2.Visible = True
End Sub
Now each the first button shows the first frame while hiding the 2nd. The second button hides the first frame and shows the seconds. I think this is the simplest way to implement your task.
PS: don't forget to name your objects properly, it's not a good idea to have default names like Command1 or Frame2 - should be more descriptive than that.
It sounds like you are asking about the tabbed dialog control. To use a tabbed dialog control in VB6:
Click Project -> Components
Scroll down to "Microsoft Tabbed Dialog Control 6.0" and select it.
Click the Apply button.
You should notice a new control in the component tool box. If you do not see the toolbox, click View -> ToolBox. This is the same area of the IDE where you first click to add a button to a form. The tabbed dialog control looks like the top tab of several file folders. When you hover your mouse over the control in the toolbox, you will see a tool tip text of "SSTab". Click this control and then draw a rectangle on your form.
By default, this will add a tabbed dialog control with 3 tabs, but you can change this in the properties window. You can now create any control on top of a tab of the tabbed dialog control and interact with the control exactly the same way you would if the control was placed on the form itself.
What you want is called an MDI Form. It's a form that contains other forms.
You can find a full tutorial on them here, but here's the gist of what you want to do:
Set the "MDIChild" property of all your subforms you want to use to True. Disable their minimize, maximize, and resize functions as well.
Create an MDIForm. Disable its AutoShowChildren property.
Add a toolbar to the MDIForm. Add buttons to the toolbar corresponding to the forms you'll be switching between.
Implement each button's click event, to create child form as expected (or switch to an existing one).

Win32, How to access button on toolbar

Let's say I have a compiled binary program that runs and exposes some GUI on the screen.
I need from another program in Win32 to access its toolbar, find a button and click on it.
I know how to find Hwnd of the toolbar, but I don't really know how to enumerate the buttons on it.
Any ideas how to do it in Win32 calls?
Is there any tool like Spy++ that is capable of showing button handles under toolbar?
Spy++ doesn't do it.
Thanks
If it is a standard Win32 Toolbar control, then you send the toolbar a TB_BUTTONCOUNT message to determine how many buttons are on the toolbar, then send a TB_GETBUTTON message to retrieve information about a button at a given index.
The tricky part is that the TBBUTTON structure that receives the button info needs to be allocated in the same process that owns the toolbar, so you will have to:
call GetWindowThreadProcessId() to retrieve the toolbar's process ID
call OpenProcess() to get a HANDLE to that process
call VirtualAllocEx() to allocate the structure inside of that process
send the TB_GETBUTTON message(s) to the toolbar, specifying the pointer returned by VirtualAllocEx()
call ReadProcessMemory() to copy the structure data back into your own process so you can process it as needed
call VirtualFreeEx() to free the allocated memory.
When the button is clicked it sends a WM_COMMAND message to the main window. Simulating a click on a toolbar button is not very practical. A better approach is to use Spy++ to find the WM_COMMAND message and its parameters. Then in your program send this same WM_COMMAND message.
AFAIR you can get the HWND of a toolbar button by calling GetDlgItem. The first parameter is the HWND of the toolbar, the second parameter is the ID of the button (the one you set in its TBBUTTON structure). You need to have the button ID to use this approach.
=== EDIT ===
In addition to EnumChildWindows, suggested by #graham.reeds, you can try SendInput. Move the target window to the foreground, calculate the screen coordinates of the upper-left corner of the toolbar (using its HWND), add the X-Y offset of the middle of the target button, and send a mouse click to that position. (I used this approach successfully to click on Flash and Silverlight objects rendered inside the IE window.)
Off the top of my head:
Use EnumChildWindows to find the child controls of the toolbar.
Then use GetWindowText to see if it is a button.
If it is PostMessage to it to invoke it's operation.

oddity with edit controls in property sheet

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.

Resources