Disabling (Greying out) multiple GUI Items in C++ - c++

Question:
I am looking for an effective way to disable(grey) mutiple items in a MFC C++ application. Depending on if the user is signed into a SQL Server or Oracle account, I wish to disable a section of GUI items.
What I have tried:
I have used the following code to disable one of my two "Create User" buttons.
if(checkIsSQLServer())
{
CWnd *oraCreateUser = GetDlgItem(BTN_ORA_CREATE);
oraCreateUser->EnableWindow(false); //Disable Oracle "Create User" button
}
else
{
CWnd *sqlCreateLogin = GetDlgItem(BTN_SQL_CREATE);
sqlCreateLogin->EnableWindow(false); //Disable SQL Server "Create User" button.
}
.
This code works perfectly, however it only disables the button. I wish to disable all items within either groupbox.
Do I need to create a CWnd* object for every item I wish to disable? Is there a more effective way, such as a way to disable all items that are contained within a group box?

I use this:
void EnableDlgItem (CWnd *dlg, int items[], BOOL bEnable)
{
int i = 0, item ;
while ((item = items[i++]) != 0)
{
CWnd *pControl = dlg->GetDlgItem(item) ;
if (pControl != NULL)
pControl->EnableWindow(bEnable) ;
}
}
...
And in some CYourDialog::OnSomethingFunction()
static int ids[] = {IDOK, IDC_EDIT1, IDC_EDIT2, 0};
EnableDlgItem(this, ids, FALSE);

As others have said, MFC does not expose a method that allows you to enable/disable a group of controls. You'll need to craft your own code to do that.
I was faced with the very same situation and decided to handle it by deriving my own groupbox class. In my situation, the groupbox enable/disable functioning was tied to the state of a checkbox as seen below.
(I've redacted some information from it).
Clicking on the checkbox will toggle the enable/disable of all controls within the groupbox. There's nothing stopping you from deriving your own class for the groupbox and exposing a method that can be called (rather than a checkbox) to enable/disable controls. The advantage to this approach is that if you create the class in a generic manner, you can re-use it in other situations where a groupbox is the "parent" of other controls.

Related

Call button click function from grandchild

I'm creating my first C++ wxWidgets application. I'm trying to create some kind of split button where the options are displayed in a grid. I have a custom button class which, when right-clicked on, opens a custom wxPopupTransientWindow that contains other buttons.
When I click on the buttons in the popup, I want to simulate a left click on the main button. I'm trying to achieve this through events, but I'm kinda confused.
void expandButton::mouseReleased(wxMouseEvent& evt)
{
if (pressed) {
pressed = false;
paintNow();
wxWindow* mBtn = this->GetGrandParent();
mBtn->SetLabel(this->GetLabel());
mBtn->Refresh();
wxCommandEvent event(wxEVT_BUTTON);
event.SetId(GetId());
event.SetEventObject(mBtn);
mBtn-> //make it process the event somehow?
wxPopupTransientWindow* popup = wxDynamicCast(this->GetParent(), wxPopupTransientWindow);
popup->Dismiss();
}
}
What is the best way to do this?
You should do mBtn->ProcessWindowEvent() which is a shorter synonym for mBtn->GetEventHandler()->ProcessEvent() already mentioned in the comments.
Note that, generally speaking, you're not supposed to create wxEVT_BUTTON events from your own code. In this particular case and with current (and all past) version(s) of wxWidgets it will work, but a cleaner, and guaranteed to also work with the future versions, solution would be define your own custom event and generate it instead.

Disable some context menu items if no items are checked

I have a tree view (CTreeView) that will show me a pop-up menu after I right-click my mouse on it.
In my context menu there are only 3 items (i.e A, B, C) for selection and my tree view displays a long list of ordered foods designed with check-boxes. I would like to disable menu items A and B if no ordered foods are checked and enable them when any is.
I create CFoodView::OnUpdateItemA(CCmdUI* pCmdUI) //CFoodView inherits CTreeView
and CFoodView::OnUpdateItemB(CCmdUI* pCmdUI) to handle their states like so
CFoodView::OnUpdateItemB(CCmdUI* pCmdUI)
{
if TreeView has no items
{
pCmdUI->Enable(FALSE);
}
else
{
*Search* the tree to get selected items
if None is checked
{
pCmdUI->Enable(FALSE);
}
else there are checked items
pCmdUI->Enable(TRUE);
}
}
Method CFoodView::OnUpdateItemA(CCmdUI* pCmdUI) is the same.
I think this isn't a correct way to handle this GUI feature.
Well, you did not submit all important information. How did you create menu item handlers?
Assuming you insert handlers the proper way, still did not provide any information how you are invoking popup menu.
If all you did was properly done it is the proper way of handling update menu.
The most common mistake is designating view itself as the window that handles popup updates and commands. In order to use MFC menu update mechanism, you have to pass a pointer to the main window not to the tree view:
CWnd *pMainWnd = AfxGetMainWnd();
ASSERT(pMainWnd != nullptr);
pSubMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, pMainWnd);
If this will not work reexamine the way you create handler and/or the place you invoke the TrackPopupMenu function.

How do you close a QMenu when its QWidgetAction is triggered?

I have a QMenu for which I've created a QColorModel action widget (It's effectively just a QStandardItemModel). My desired behavior is that when a user clicks one of the colors in the model, that the action should trigger, and the menu close. However, it doesn't seem to do that, even when I trigger the action manually.
I've tried manually hiding the menu, but it's a kludge because it won't hide parent menus which th menu may be attached to.
Here's the relevant section of code:
// color menu
m_colorMenu = new QMenu("color", this);
m_colorView = new QColorView(m_colorMenu);
m_colorViewAction = new QWidgetAction(m_colorMenu);
m_colorViewAction->setDefaultWidget(m_colorView);
m_colorView->setModel(new QStandardColorModel);
connect(m_colorView, &QColorView::clicked, [&](QModelIndex index)
{
QColor color = qvariant_cast<QColor>(index.data(Qt::DecorationRole));
if (m_pen.color() != color)
{
m_pen.setColor(color);
drawIcon();
drawColorIcon();
update();
}
//this->hide(); // kludge, didn't close all parent menus
m_colorViewAction->trigger(); // doesn't seem to cause menu closure
});
m_colorMenu->addAction(m_colorViewAction);
EDIT
I've also tried adding something to the effect of:
QMenu* menu = m_colorMenu;
do
{
menu->close();
menu = dynamic_cast<QMenu*>(menu->parent());
} while (menu);
but it also is fragile/kludgey because it assumes a) all widgets are properly parented, and b) that all the parents are actually supposed to be menus. In my case, they aren't.
If the containing menus aren't in the parentage tree, and the menu you want to close isn't the top level menu, there is no easy way to do this. That said, there is:
THE NUCLEAR OPTION
Adding this to the end of the lambda function
auto topLevelWidgets = qApp->topLevelWidgets();
for (auto widget : topLevelWidgets)
{
QMenu* menu = dynamic_cast<QMenu*>(widget);
if (menu)
{
menu->close();
}
}
will cause ALL top level menus to close once the action is triggered. This is a relatively OK way to accomplish what you want because:
one of the top level menus will contain the menu in question, and
never say never, but I can't think of a single case where you would have (or want) more than one menu open at a time, so most likely the only open menu tree you would close is the intended one.

Sharing variable among class instances

class MyApp : public CWinApp {
afx_msg OnPrefrences();
};
OnPrefrences() get called when user selects tools->Preference from the menubar.
Now in one dialog(Say DlgX) there is one button, on clicking this I need to open the Preference dialog which has in fact many panes, but here I need to open the Preference dialog by selecting one the these pane as active. Also in that particular pane I need to hide some of the controls only when It gets open through this the dialog not through menu.
So I have created one variable(Say m_varX) in MainFrm class.
void DlgX::OnButtonXClick()
{
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
if(pFrame)
{
pFrame->m_varX = TRUE;
((CMyApp*)(AfxGetApp()))->OnPrefrences();
pFrame->m_varX = FALSE;
}
}
And in button handler of DlgX I have made this m_varX TRUE and call the OnPreference() and after closing of this preference dialog I have made m_varX FALSE.
All this is working fine... But the problem is that things gets clutter in mainFrm. Also the project I am working on is legacy one so I cant make much changes.
Is there any patter available for handling such case?
Thanks
You could solve this with a custom dialog (if you don't have it already)
When you show the dialog from the main menu i.e. onPreferences() you fill and show all 'panes'. you would have to do a custom dialog where the ctor takes some arguments.
E.g.
enum { all, part };
void MainFrame::OnPreferences()
{
CMyPreferences dlg( GetDocument(), all );
dlg.DoModal();
}
but when you call it from within a dialog you only fill in the parts you need.
void YourDialog::OnPreferences()
{
CMyPreferences dlg( GetDocument(), part );
dlg.doModal();
}
The argument could be something more sophisticated for more fine tuned configuration of what to show/allow to edit.
I think for that special case, even if sometimes is no more considered a pattern, the singleton pattern would work for you.

How can I disable and gray the top level menu item using MFC

I have a dialog application in which I want to have clickable menu items at the top of the dialog. These items do not show a drop down menu but actually run the associated commands.
I did this by setting Popup=False in the dialogs properties and assigning a message-id but my problem is not having the ability to disable the item properly when it makes no sense for the item to be clickable (depending on internal state stored in the dialog)
I have already found out how to disable any popup-parent menu items from http://www.microsoft.com/msj/0299/c/c0299.aspx, but this isn't exactly what I want
I have also found out how to add menu command routing to dialogs from the msdn knowledgebase article KB242577.
This works fine for sub-menu items, but not for the top level menu.
I am currently using the following function to do the disabling
void CYourDlg::EnableMenuItem(UINT nCommand, BOOL bEnable)
{
CMenu* pMenu = GetMenu();
pMenu->EnableMenuItem(nCommand, bEnable ? 0 : MF_DISABLED | MF_GRAYED);
}
This half works, if you alt-tab away from the app it does show as disabled, otherwise it doesn't.
Is there a way to invalidate the area programmatically?
I think an non-client area message may be involved.
I have not tried but in regular window (not dialog) CWnd::DrawMenuBar should do what you want. It might work with dialog based applications as well.
void CYourDlg::EnableMenuItem(UINT nCommand, BOOL bEnable)
{
CMenu* pMenu = GetMenu();
pMenu->EnableMenuItem(nCommand, bEnable ? 0 : MF_DISABLED | MF_GRAYED);
DrawMenuBar();
}
I think you should add an ON_UPDATE handler for your menu ID. This would ensure that the menu is enabled/disabled when you want to.