Controlling Inner CTabControl Items with TAB and Arrow Keys - visual-c++

I have an issue Controlling CTabControl Inner tab items with TAB and Arrow keys.
here is my code and a few screenshots:
OnInitDialog() method of the main dialog window:
BOOL PressetsDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// AUTO GENERATED MFC DIALOGUE CODE HERE
...
// ..
// TODO: Add extra initialization here
CTabCtrl* pTabCtrl = (CTabCtrl*)GetDlgItem(IDC_TAB1);
m_one.Create(IDD_TAB_ONE, pTabCtrl);
CTabCtrl* pTabCtrl2 = (CTabCtrl*)GetDlgItem(IDC_TAB1);
m_two.Create(IDD_TAB_TWO, pTabCtrl2);
TCITEM item1, item2, item3;
item1.mask = TCIF_TEXT | TCIF_PARAM;
item1.lParam = (LPARAM)&m_one;
item1.pszText = L"Normal Presets";
pTabCtrl->InsertItem(0, &item1);
item2.mask = TCIF_TEXT | TCIF_PARAM;
item2.lParam = (LPARAM)&m_two;
item2.pszText = L"Movement Presets";
pTabCtrl2->InsertItem(1, &item2);
CRect rcItem;
pTabCtrl->GetItemRect(0, &rcItem);
m_one.SetWindowPos(NULL, rcItem.left, rcItem.bottom + 1, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
CRect rcItem2;
pTabCtrl2->GetItemRect(0, &rcItem2);
m_two.SetWindowPos(NULL, rcItem2.left, rcItem2.bottom + 1, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
m_one.ShowWindow(SW_SHOW);
m_two.ShowWindow(SW_HIDE);
return TRUE; // return TRUE unless you set the focus to a control
}
and the OnTcnSelchangeTab method:
void PressetsDlg::OnTcnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)
{
// TODO: Add your control notification handler code here
int nSelect = m_Tab.GetCurSel();
if (nSelect == 0)
{
m_one.ShowWindow(SW_SHOW);
m_two.ShowWindow(SW_HIDE);
}
else if (nSelect == 1)
{
m_two.ShowWindow(SW_SHOW);
m_one.ShowWindow(SW_HIDE);
}
else
{
m_one.ShowWindow(SW_SHOW);
m_two.ShowWindow(SW_HIDE);
}
*pResult = 0;
}
[to see the tab design click here](https://i.stack.imgur.com/Tt8c1.jpg)
I've set the tab order with Ctrl + D for each dialogue resource and set Tabstop property to either True or False and still nothing happens.
At first I thought that this feature is supposed to be supported automatically but it seems that it's not.
the dialogue window moves between tabs and buttons that placed on it but as soon as I try to move to "inner Items" of each tab, it doesn't reach them.
I suspect the reason is probably that each tab is a separate window and that's probably the reason that the inner items are unreachable..

I've made a few changes and now Moving with TAB and arrow keys functions properly.
first of all I set the Control Property of both child dialogs to True.
You go to Resource View >> (Solution name) >> (project name) >> IDD + (the id you gave to the dialog) doble click on it >> Properties >> Control.
Essentially what it did is to add the flag WS_CONTROL as mentioned here early to each of the Child windows so that they could be accessed from the main dialog window that contains them.
Of course that alone didn't do much because I also had a few bugs in the code, after days of searching for it I found an example online which helped me solve the bugs.
Then I've changed my code to this: and it started working:
// first we create two modeless dialogs and embed them as child windows
// of CTabControlTutorialDlg.
// Have a look using Spy++ to see the layout of the controls as they
// appear to windows
m_one.Create(IDD_TAB_ONE, this);
m_two.Create(IDD_TAB_TWO, this);
// next we get the captions of the dialogs and use these as the caption
// for the tab window. Of course we could just load a string from the
// resources or hard code a string for the text of the tab.
TCITEM item1, item2;
item1.mask = TCIF_TEXT | TCIF_PARAM;
item1.lParam = (LPARAM)&m_one;
item1.pszText = L"Normal Presets";
m_Tab.InsertItem(0, &item1);
item2.mask = TCIF_TEXT | TCIF_PARAM;
item2.lParam = (LPARAM)&m_two;
item2.pszText = L"Custom Presets";
m_Tab.InsertItem(1, &item2);
// finally we set the tab order correctly for the so that we can tab through the dialogs and into
// the cancel and ok buttons. If we don;t do this then the tab order is tab control, ok button, cancel
// button embedded dialogs.
CRect rcItem;
m_Tab.GetItemRect(0, &rcItem);
m_one.SetWindowPos(NULL, rcItem.left, rcItem.bottom + 5, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOZORDER);
CRect rcItem2;
m_Tab.GetItemRect(0, &rcItem2);
m_two.SetWindowPos(NULL, rcItem2.left, rcItem2.bottom + 5, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOZORDER);
m_one.ShowWindow(SW_SHOW);
I found reference in this site:https://www.codeproject.com/Articles/4408/Creating-embedded-dialogs-in-MFC but in order to get their code example you need to register to the website.
It works!

Related

How to guarantee that I get the `HTREEITEM` for the item that the user right-clicked on in a CTreeControl

I have a window which has a CTreeCtrl. A user can right-click any element and display a context menu. From there they can choose to delete the entry. Something like this:
Here is a snippet of the context menu item handler:
void CAssignHistoryDlg::OnDeleteFromAssignmentHistory()
{
CString strINI = theApp.GetAssignHistoryPath();
HTREEITEM hItem = m_treeHistory.GetSelectedItem();
CString strName, strDeletedName, strEntry;
if (m_treeHistory.GetParentItem(hItem) != nullptr)
{
// The user has picked one of the history dates.
// So the parent should be the actual name.
hItem = m_treeHistory.GetParentItem(hItem);
// Now OK to proceed
}
strName = ExtractName(hItem);
GetParent()->EnableWindow(FALSE);
strEntry.Format(IDS_TPL_SURE_DELETE_FROM_ASSIGN_HIST, strName);
if (AfxMessageBox(strEntry, MB_YESNO | MB_ICONQUESTION) == IDNO)
{
The image shows my problem. If I first click on Test, so that it is selected and bright blue and then right-click, it shows Test in the popup message. This is fine. But ...
If the first name is initially selected, and I go ahead and directly right-click Test, even though it seems to go blue (as if selected), m_treeHistory.GetSelectedItem() is returning the original, first name. I think I am describing it not very well.
In short, I want to guarantee that I get the HTREEITEM for the item that the user right-clicked on. What I have is not 100% fool-proof.
If it helps, this is how I display the context menu:
void CAssignHistoryDlg::OnNMRclickTreeHistory(NMHDR *pNMHDR, LRESULT *pResult)
{
CMenu mnuContext, *pMnuEdit = nullptr;
CPoint ptLocal;
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
GetCursorPos(&ptLocal);
mnuContext.LoadMenu( IDR_MENU_SM_ASSIGN_HIST_POPUP );
pMnuEdit = mnuContext.GetSubMenu( 0 );
if (pMnuEdit != nullptr)
{
pMnuEdit->TrackPopupMenu( TPM_LEFTALIGN|TPM_LEFTBUTTON,
ptLocal.x, ptLocal.y, this, nullptr );
}
*pResult = 0;
}
So to recap, at the moment the user must physically left click on a item in the tree to select it. Then they can right-click and it will offer to delete this person. But if they go ahead and just right-click on anyone, it will not offer to delete THAT person.
You can get the actual tree item at any given point using the HitTest() member of the CTreeCtrl class. Exactly how and where you do this will depend on your code design but, if you have a pointer to your CTreeCtrl (pwTree in the code below), then you can do something like this:
CPoint ptLocal;
GetCursorPos(&ptLocal);
pWTree->ScreenToClient(&ptLocal); // May not be required in certain handlers?
HTREEITEM hItem = pWTree->HitTest(ptLocal); // Remember to check for NULL return!
You can then either use the returned hItem directly, or use it to explicitly set the tree's selection (to that item), before doing any further processing.
The MS doc: https://learn.microsoft.com/en-us/cpp/mfc/reference/cwnd-class?view=vs-2019 doesn't have a "click" handler, it has CWnd::OnRButtonDblClk, CWnd::OnRButtonDown and CWnd::OnRButtonUp. Which one are you handling?
The reason for your defect might be that you don't let the tree control handle that right button event (to select that new item).
I would suggest to use CWnd::OnContextMenu instead.

Removing menu in MFC

In MFC how to remove a menu-item of POPUP type. RemoveMenu() either take ID or position. Since, there is no ID for POPUP menu, option left is by using position.
But I am not getting how and where I can call RemoveMenu().
File Edit Test
Test_submenu_1
Test_submenu_2
Test_submenu_3 > submenu_3_item_1
Test_submenu_4
Test_submenu_5
I want to remove Test_submenu_3? I am not getting how do find CMenu object for Test so that I can call RemoveMenu() by passing position "2" for submenu_3_item_1.
Any suggestion for doing this will be greatly appreciated.
Thanks!
You cannot use LoadMenu, since this function does just that.
After modifying loaded menu it is killed when menu object used to load it goes out of scope. You have to modify menu that is currently used.
Your menu is a popup part of the main menu, second in position. It contains 5 items and second one is another popup. To my understanding, you want to remove this item and popup of this item.
To make it work you will have to ask main window for the current menu:
CMenu* pMenu = GetMenu(); // get the main menu
CMenu* pPopupMenu = pMenu->GetSubMenu(2);//(Test menu with item....)
pPopupMenu->RemoveMenu(2, MF_BYPOSITION);
Of course, this code is from the main frame. If you want to use it elsewhere, you will have to access all using pointer to the main frame.
Try the below. You get the Test sub-menu first (index 2), then once you have that you tell it to remove its Test_submenu_3 submenu by position (also 2).
CMenu topMenu;
topMenu.LoadMenu(IDR_YOUR_MENU);
CMenu& testSubMenu = *topMenu.GetSubMenu(2);
testSubMenu.RemoveMenu(2,MF_BYPOSITION);
'Test' is the 3rd menu item (by position) on the top level menu. It's just been rendered horizontally rather than vertically. Assuming you have a handle to the top level menu use the same code you'd use to the get the sub menus as you would to get the 'Test' menu.
You can use this below code to remove the submenu, by comparing name
bool RemoveSubmenu(CMenu * pMenu) {
for (int pos = 0; pos < pMenu->GetMenuItemCount(); pos++) {
wchar_t *name = new wchar_t[mf.cch + 1];
MENUITEMINFO mf;
ZeroMemory(&mf, sizeof(mf));
mf.cbSize = sizeof(mf);
mf.fMask = MIIM_SUBMENU | MIIM_FTYPE | MIIM_STRING;
mf.fType = MIIM_STRING;
mf.dwTypeData = NULL;
if (!GetMenuItemInfo(pMenu->m_hMenu, pos, TRUE, &mf))
break;
if (mf.hSubMenu != NULL){
mf.fMask = MIIM_TYPE;
mf.fType = MFT_STRING;
++mf.cch;
mf.dwTypeData = (LPSTR)name;
if (!GetMenuItemInfo(pMenu->m_hMenu, pos, TRUE, &mf)){
bRet = false;
break;
}
//
// compare sub menu name (i.e mf.dwTypeData) here, do the required
// modifications
//
pMenu->RemoveMenu(pos, MF_BYPOSITION);
bRet = true;
break;
}
}
}

Adding Minimize box to MFC Property Sheet system menu

How could I Add Minimize and Maximize box to the system menu of CMFCPropertySheet.
I have tried modifying the style by
CMFCPropertySheet::ModifyStyle(NULL, WS_SYSMENU);
but nothing happened.
Assuming you have a class derived from CPropertySheet, let's call it MySheet:
// Capture the WM_NCREATE message
BEGIN_MESSAGE_MAP(CMySheet, CPropertySheet)
ON_WM_NCCREATE()
END_MESSAGE_MAP()
BOOL CMySheet::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
{
if (!CPropertySheet::OnNcCreate(lpCreateStruct))
return FALSE;
// Modify the window style
LONG dwStyle = ::GetWindowLong(m_hWnd, GWL_STYLE);
::SetWindowLong(m_hWnd, GWL_STYLE, dwStyle | WS_WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
return TRUE;
}
Note that you could do this in the OnInitDialog, but even though the Minimize/Maximize boxes will show, they won't do anything.
doing just this in the "OnInitDialog:" worked for me.
LONG dwStyle = ::GetWindowLong(m_hWnd, GWL_STYLE);
::SetWindowLong(m_hWnd, GWL_STYLE, dwStyle | WS_WS_MINIMIZEBOX | WS_MAXIMIZEBOX);

C++, MFC Feature Pack,Mdi childs visibility

I have an MDI MFC FEATURE PACK app in vs2008.
I do need to determine what child window(s) are visible , even if multiple tab groups are created by the user, and also what is the last activated MDI child. I have found that in my mainframe CMDIFrameWndEx class, the methods
m_wndClientArea.FindActiveTabWnd ();
m_wndClientArea.GetFirstTabWnd ();
m_wndClientArea.GetNextTabWnd ();
that could potentially let me navigate through all tab grops. Trouble is that these methods return an CMFCTabControl that does not offer any method/member to obtain an pointer to the MDI child windows in the tab. It only gives the index of the active tab.
So how do I get the CMDIChildWndEx* pointer of the "in front" window of the given tabgroup?
Because your CMDIChildWndEx instances are wrapped in a tab control wrapper you can get the active tab and then the wnd from that, e.g.
int nActive = pTabCtrl->GetActiveTab();
CWnd * pWnd = pTabCtrl->GetTabWndNoWrapper( nActive );
CMDIChildWndEx * pChild = dynamic_cast<CMDIChildWndEx*>(pWnd);
It was wonderful to find this code - exactly what I needed to redraw my active tab windows in each tab group since with multiple (split) tab groups, they were not being redrawn correctly. However, to make the loop work I had to do the following (CChildFrame is my derived frame type):
m_arrpActiveChilds.RemoveAll ();
const CObList& TabGroups =m_wndClientArea.GetMDITabGroups();
if (TabGroups.GetCount ()>0) {
POSITION crtPos = TabGroups.GetHeadPosition ();
CMFCTabCtrl* pCrtTabCtrl;
do {
pCrtTabCtrl=DYNAMIC_DOWNCAST(CMFCTabCtrl, TabGroups.GetNext(crtPos));
int nActive = pCrtTabCtrl->GetActiveTab();
CWnd * pWnd = pCrtTabCtrl->GetTabWndNoWrapper( nActive );
CChildFrame * pChild = dynamic_cast<CChildFrame*>(pWnd);
m_arrpActiveChilds.Add (pChild);
} while(crtPos != NULL);

SetWindowPos() function not moving window?

I have a dialog that I want to place within another dialog and position relative to one of the controls on the main dialog.
void CspAceDlg::DrawResultsArea()
{
CWnd* pTabCtl = GetDlgItem(IDC_BUILDTABS);
CRect rectTabCtl; // Allocate CRect for control's position.
pTabCtl->GetWindowRect(&rectTabCtl);
int resX = rectTabCtl.right + 15;
int resY = rectTabCtl.top;
//RESULTS AREA
results.Create(IDD_RESULTSDIALOG, this);
results.SetWindowPos(this, resX, resY, /*608, 19, */175, 135, SWP_SHOWWINDOW);
results.ShowWindow(SW_SHOW);
}
My problem is that my dialog resource (IDD_REULTSDIALOG) has properties called X Pos and Y Pos that seem to be overriding my SetWindowPos() (and the little property tab in the resource editor won't let me leave these blank). If I set these properties to 0, 0 my dialog appears in the top left corner of the main dialog. If I set them to a number I can guess-and-test place it roughly where I want, but then running the application on different resolutions causes the dialog to appear in different spots. What I really want to do anyway is place the dialog relative to another control on my main dialog (in this case my tab control). Why is my SetWindowPos() being ignored, and how do I fix this? Should I be using a different function?
According to the documentation for SetWindowPos, if you pass in SWP_SHOWWINDOW, the window will not be moved:
If the SWP_SHOWWINDOW or SWP_HIDEWINDOW flag is set, the window cannot be moved or sized.
Figured it out myself, largely due to this thread.
My code came out looking like this:
void CspAceDlg::DrawResultsArea()
{
CRect rectTabCtl; // CRect representing tab control's position.
POINT pResXY;
POINT pResWH;
CWnd* pTabCtl = GetDlgItem(IDC_BUILDTABS);
pTabCtl->GetWindowRect(&rectTabCtl);
pResXY.x = rectTabCtl.right + 15;
pResXY.y = rectTabCtl.top;
pResWH.x = pResXY.x + 175;
pResWH.y = pResXY.y + 135;
ScreenToClient(&pResXY);
ScreenToClient(&pResWH);
//RESULTS AREA
results.Create(IDD_RESULTSDIALOG, this);
//results.SetWindowPos(this, resX, resY, /*608, 19, */175, 135, SWP_SHOWWINDOW);
results.MoveWindow(pResXY.x, pResXY.y, pResWH.x, pResWH.y, TRUE);
results.ShowWindow(SW_SHOW);
}
What fixed this problem for me, was setting the program's compatibility properties to "run this program as an administrator".

Resources