CMFCMenuButton with a programmatically constructed menu? - c++

I'm new to the CMFCMenuButton control; here's my code in OnInitDialog():
// Load application list into menu button
m_ApplicationMenu = CreateMenu();
m_MenuInfoSize = 2;
m_MenuInfo = new MENUITEMINFO[m_MenuInfoSize];
memset(m_MenuInfo, 0, sizeof(MENUITEMINFO) * m_MenuInfoSize);
UINT menuIndex = 0;
BOOL b;
// 1st menu item
memset(m_MenuInfo + menuIndex, 0, sizeof(MENUITEMINFO));
m_MenuInfo[menuIndex].cbSize = sizeof(MENUITEMINFO);
m_MenuInfo[menuIndex].fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
m_MenuInfo[menuIndex].wID = menuIndex;
m_MenuInfo[menuIndex].dwTypeData = new WCHAR[10];
swprintf_s(m_MenuInfo[menuIndex].dwTypeData, 10, L"%s", L"A1");
m_MenuInfo[menuIndex].cch = wcslen(m_MenuInfo[menuIndex].dwTypeData) + 1;
b = InsertMenuItem(m_ApplicationMenu, menuIndex, TRUE, &(m_MenuInfo[menuIndex]));
menuIndex++;
// 2nd menu item
memset(&m_MenuInfo[menuIndex], 0, sizeof(MENUITEMINFO));
m_MenuInfo[menuIndex].cbSize = sizeof(MENUITEMINFO);
m_MenuInfo[menuIndex].fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
m_MenuInfo[menuIndex].wID = menuIndex;
m_MenuInfo[menuIndex].dwTypeData = new WCHAR[10];
swprintf_s(m_MenuInfo[menuIndex].dwTypeData, 10, L"%s", L"B2");
m_MenuInfo[menuIndex].cch = wcslen(m_MenuInfo[menuIndex].dwTypeData) + 1;
b = InsertMenuItem(m_ApplicationMenu, menuIndex, TRUE, &(m_MenuInfo[menuIndex]));
menuIndex++;
// Attach menu to CMFCMenuButton
m_ApplicationList.m_bOSMenu = TRUE;
m_ApplicationList.m_bRightArrow = FALSE;
m_ApplicationList.m_bStayPressed = TRUE;
m_ApplicationList.m_bDefaultClick = FALSE;
m_ApplicationList.m_hMenu = m_ApplicationMenu;
// Testing the constructed menu with the dialog's menu bar
::SetMenu(this->m_hWnd, m_ApplicationMenu);
When I ran the application, the CMFCMenuButton displayed a dropdown menu when I clicked on it... but those two items were empty, no text and no image.
I added the last line to test my constructed menu; and the two items showed up in the menu bar properly.
I also tried using a menu created from the resource editor. It showed up fine in menu bar, but in the CMFCMenuButton, there were again empty spaces.
What did I miss?

Try
m_ApplicationMenu = CreatePopupMenu();
Also, you'd better use 1 as the first menuIndex if you want to get the m_nMenuResult of CMFCMenuButton in click event. Because:
CMFCMenuButton::m_nMenuResult
An integer that indicates which item the user selects from the pop-up menu.
The value of this member variable is zero if the user cancels the menu
without making a selection or if an error occurs.

Related

CTabView syncronize after removeview

I have been struggling with a problem for weeks. I have an MDI app, explore style. In right side, I have a CTabView, which has 5 CListViews and one CFormView. Depending on what I choose in leftview (CTreeView), I should remove (or add) the FormView from CTabView.
The CTabView could be re-arranged by drag and drop (you could drag CTestFormView as first tab), and they are stored in that order.
Here is the link to have a sample project that simulates the problem:
Explore sample project
In left view, I have:
As soon as I select "Without test-form-view" item, the CTestFormView is removed from CTabView, using CTabView::RemoveView.
To reproduce that, you can do the following simple steps:
Go to "With test-form-view"
Drag CTestFormView as first tab
Select, let's say, CExploreListView4
Select "Without test-form-view" item in left view
The "CTestFormView" has disappeared from CTabView, and the first tab is selected. Right-click on this CExploreListView1, and you will see the context menu of CExploreListView4, not the context menu of CExploreListView1.
If you select another treeitem from leftview ("With-test-form-view"), which add CTestFormView, then all listviews from CTabView are not redrawn correctly. Is there a bug in CTabView ?
With this following code I remove a view in CTabView:
RemoveView(nTabIndex);
This is the code I used to add a view to CTabView dynamically:
int CExploreTabbedView::AddView(CRuntimeClass* pViewClass, const CString& strViewLabel, int iIndex /*= -1*/, CCreateContext* pContext/* = NULL*/, BOOL bAfterCreation/* = FALSE*/)
{
ASSERT_VALID(this);
ENSURE(pViewClass != NULL);
ENSURE(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CView)));
CView* pView = DYNAMIC_DOWNCAST(CView, pViewClass->CreateObject());
ASSERT_VALID(pView);
if(! pView->Create(NULL, _T(""), WS_CHILD | WS_VISIBLE, CRect(0, 0, 0, 0), &m_wndTabs, (UINT)-1, pContext))
{
TRACE1(_T("CTabView:Failed to create view '%s'\n"), pViewClass->m_lpszClassName);
return -1;
}
CDocument* pDoc = GetDocument();
if (pDoc != NULL)
{
ASSERT_VALID(pDoc);
BOOL bFound = FALSE;
for (POSITION pos = pDoc->GetFirstViewPosition();! bFound && pos != NULL;)
{
if(pDoc->GetNextView(pos) == pView)
bFound = TRUE;
}
if(! bFound)
pDoc->AddView(pView);
}
pView->SetParent(this);
if(bAfterCreation)
pView->SendMessage(WM_INITIALUPDATE, 0, 0);
m_wndTabs.InsertTab(pView, strViewLabel, iIndex);
int nTabs = m_wndTabs.GetTabsNum();
return nTabs - 1;
}
Could you help me figure out why removing/adding view to CTabView is causing this problem?
You must specify the context where you add the view and set the document there.
Like:
CCreateContext newContext;
newContext.m_pNewViewClass = NULL;
newContext.m_pNewDocTemplate = NULL;
newContext.m_pLastView = NULL;
newContext.m_pCurrentFrame = NULL;
newContext.m_pCurrentDoc = GetDocument();
CTabView *pTab = GetLayoutViewsTab();
int index=pTab->AddView(RUNTIME_CLASS(YourClass), tabName,-1,&newContext);

Having trouble getting a handle to a Dockable Pane C++

I created a multi doc ribbon based MFC application through the MFC Wizard. Im trying to get a handle to m_wndFileView to update its view. I know there are several ways to do it but Im not understanding why the method Im using is not working. Soooo to start
class CMainFrame : public CMDIFrameWndEx
{
...
CFileView m_wndFileView;
CPropertiesWnd m_wndProperties;
...
}
class CFileView : public CDockablePane
{
...
protected:
CViewTree m_wndFileView;
...
};
class CPropertiesWnd : public CDockablePane
{
...
protected:
CMFCPropertyGridCtrl m_wndPropList;
...
};
The main frame is created from MAINAPPLICATION.cpp
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
{
delete pMainFrame;
return FALSE;
}
m_pMainWnd = pMainFrame;
// call DragAcceptFiles only if there's a suffix
// In an MDI app, this should occur immediately after setting m_pMainWnd
// Enable drag/drop open
m_pMainWnd->DragAcceptFiles();
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Enable DDE Execute open
EnableShellOpen();
RegisterShellFileTypes(TRUE);
// Dispatch commands specified on the command line. Will return FALSE if
// app was launched with /RegServer, /Register, /Unregserver or /Unregister.
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The main window has been initialized, so show and update it
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
MainFrm.cpp creates these two panes:
// Create file view
CString strFileView;
bNameValid = strFileView.LoadString(IDS_FILE_VIEW);
ASSERT(bNameValid);
if (!m_wndFileView.Create(strFileView, this, CRect(0, 0, 200, 200), TRUE, ID_VIEW_FILEVIEW, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT| CBRS_FLOAT_MULTI))
{
TRACE0("Failed to create File View window\n");
return FALSE; // failed to create
}
// Create properties window
CString strPropertiesWnd;
bNameValid = strPropertiesWnd.LoadString(IDS_PROPERTIES_WND);
ASSERT(bNameValid);
if (!m_wndProperties.Create(strPropertiesWnd, this, CRect(0, 0, 200, 200), TRUE, ID_VIEW_PROPERTIESWND, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_RIGHT | CBRS_FLOAT_MULTI))
{
TRACE0("Failed to create Properties window\n");
return FALSE; // failed to create
}
From MAINAPPLICATION.cpp, I can access the properties pane through
CWnd * pwnd = ((CWnd*)(AfxGetApp()->m_pMainWnd));
CPropertiesWnd * pPropertiesWnd = (CPropertiesWnd*)pwnd->GetDlgItem(ID_VIEW_PROPERTIESWND);
CMFCPropertyGridCtrl * m_wndPropList = (CMFCPropertyGridCtrl *)pPropertiesWnd->GetDlgItem(2);
but for some reason I cannot access the fileview pane with
CWnd * pwnd = ((CWnd*)(AfxGetApp()->m_pMainWnd));
CFileView * pFileViewWnd = (CFileView*)pwnd->GetDlgItem(ID_VIEW_FILEVIEW);
CViewTree * m_wndFileView= (CViewTree*)pFileViewWnd ->GetDlgItem(4);
the (CFileView*)pwnd->GetDlgItem(ID_VIEW_FILEVIEW); returns NULL
please help. This is driving me crazy. In the end I can modify the m_wndPropList but not the m_wndFileView because I cannot get a handle to pFileViewWnd. Two panes created in the same way cannot be accessed in the same way. Why? If more code is needed, let me know. Thanks.
You should create a inline getters in CMainFrame class like this:
CFileView& GetFileViewPane()
{
return m_wndFileView;
}
CPropertiesWnd& GetPropsPane()
{
return m_wndProperties;
}
After that you can access those windows just like this:
CMainFrame* pMainFrame = DYNAMIC_DOWNCAST(CMainFrame, AfxGetMainWnd());
if (pMainFrame && pMainFrame->GetSafeHwnd()) // sanity check
{
pMainFrame->GetFileViewPane().DoStuff();
}
So #MarkRansom really helped out with the spy++ idea. To get a handle, I had to do the following:
// get CWnd to main window
CWnd * pwnd = ((CWnd*)(AfxGetApp()->m_pMainWnd));
// from spy++ i found that the tabbed panes were in a window called
// "File View" so i found a child window with that title
HWND h = FindWindowExW(pwnd->GetSafeHwnd(), NULL, NULL, L"File View");
// casted it to a tabbedpane pointer
CTabbedPane * pFileViewWn = (CTabbedPane *)CWnd::FromHandle(h);
// mfc wizard did what seems to me as weird naming. to find the docking
// panes i did the same as above but there was no title to this window
HWND hh = FindWindowExW(pFileViewWn->GetSafeHwnd(), NULL, NULL, L"");
// casted that
CDockablePane* pTabbedBar = (CDockablePane*)CWnd::FromHandle(hh);
// was able to find my specific docking pane using a resource id
CFileView * pFileViewWnd = (CFileView*)pTabbedBar->GetDlgItem(ID_VIEW_FILEVIEW);
// was able to find the control i wanted to use using a resource id
CViewTree * m_wndFileView = (CViewTree *)pFileViewWnd->GetDlgItem(4);
HTREEITEM hRoot = m_wndFileView->GetRootItem();
m_wndFileView->InsertItem(name, 2, 2, hRoot);
Going through the code i would think that the logic would be
tabbed pane
File View Dockable Pane
File View controls
Class View Dockable Pane
Class View controls
but somehow another window slide its way in to make
tabbed pane
MYSTERY DOCKABLE WINDOW
File View Dockable Pane
File View controls
Class View Dockable Pane
Class View controls

How to set listcontrol item to be highlighted?

I want to highlight listview item by default.I mean the by default first item should be highlighted .
Actually I did a sample but it is not working :(
Here is the code snippet I am using for Inserting Items to the list and setting the first row to get highlighted.
BOOL OnInitDialog()
{
CDialog::OnInitDialog();
LVCOLUMN pColumn;
pColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
pColumn.fmt = LVCFMT_LEFT;
pColumn.pszText = L"Product Name";
pColumn.cx = 150;
pColumn.iSubItem = 1;
m_ListCtrl.InsertColumn(2, &pColumn);
::ZeroMemory(&pColumn, sizeof(LVCOLUMN));
pColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
pColumn.fmt = LVCFMT_LEFT;
pColumn.pszText = L"Country";
pColumn.cx = 150;
pColumn.iSubItem = 2;
m_ListCtrl.InsertColumn(3, &pColumn);
LVITEM lvItem;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.iSubItem = 0;
lvItem.pszText = L"Himami";
m_ListCtrl.InsertItem(&lvItem);
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 1;
lvItem.iSubItem = 0;
lvItem.pszText = L"Shampoo";
m_ListCtrl.InsertItem(&lvItem);
//Trying highlight first item in the list.
m_ListCtrl.SetItemState(0, LVIS_SELECTED ,LVIS_SELECTED);
m_ListCtrl.SetExtendedStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT);
return TRUE;
}
void CListControlFocusDlg::OnListViewItemchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if ((pNMListView->uChanged & LVIF_STATE) && (pNMListView->uNewState & LVNI_SELECTED))
{
m_ListCtrl.SetCheck(m_ListCtrl.GetSelectionMark(), TRUE);
m_ListCtrl.SetSelectionMark(0);
}
}
After using SetItemState the list control is as follows:
List item is not highlighted.
But I want the item to be highlighted as in the below image.
After Implementing tab-order I am able to get the first item in the list highlighted.
But I am not able to uncheck the first item until unless I press down arrow.when I press down arrow a rectangular selection is coming on to the first item of the list and now I am able to check or uncheck the highlighted item by pressing space bar.This is how the list looks like after I pressed down arrow.
Can anyone please let me know how can I check or uncheck the first item by pressing space bar without pressing down arrow for the rectangular selection.
The cancel button looks like it has the focus. The default behavior of Windows is to set the focus to the first item in the tab order. I would suggest you use the resource editor to set the tab order. The easiest way is to set the tab stop order of the items in the dialog and make the list control the first item. The standard keystroke to edit the tab order is to hit Ctrl+D.
Use LVN_ITEMCHANGED notification
void OnItemchangedList2(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if ((pNMListView->uChanged & LVIF_STATE) && (pNMListView->uNewState & LVNI_SELECTED))
{
m_ListCtrl.SetCheck(m_ListCtrl.GetSelectionMark(), TRUE);
}
}

Dynamic menu using mfc

I would like to add a menu item to my main menu and then populate it with items at run time. How would I do this? And besides adding items how would I have a message map entry for them since I do not know the id?
You can create a CMenu object dynamically like this:
CMenu *menu = new CMenu;
menu->CreatePopupMenu();
// Add items to the menu
menu->AppendMenu(MF_STRING, menuItemID, "Text");
...
Then add this sub-menu to your main menu:
wnd->GetMenu()->AppendMenu(MF_POPUP, (UINT_PTR)menu->m_hMenu, "Menu Name");
As for the message map, assuming all your menu item IDs are within a certain range, you can use ON_COMMAND_RANGE to map the entire range to a single function. This function will receive the ID as a parameter, and within the function, you can perform different operations based on the ID.
define the menu's using #define
#define ID_SHOW 2002
#define ID_HIDE 2004
//create a menu object for main menu
CMenu *menu = new CMenu();
menu->CreateMenu();
//another menu object for submenu
CMenu *subMenu = new CMenu();
subMenu->CreatePopupMenu();
subMenu->AppendMenu(MF_STRING, ID_HIDE, _T("four"));
subMenu->AppendMenu(MF_STRING, ID_SHOW, _T("three"));
//append submenu to menu
menu->AppendMenu(MF_POPUP|MF_STRING, (UINT)subMenu->m_hMenu, _T("Advanced") );
SetMenu(menu);
CMenu menuPopup;
menuPopup.LoadMenu(IDR_CNTXT_PLAN);
subMenu.CreatePopupMenu();
subMenu.AppendMenu(MF_STRING, MENU1,"Menu1");
subMenu.AppendMenu(MF_STRING, MENU2,"Menu2");
CMenu* pMenu = menuPopup.GetSubMenu(0);
pMenu->InsertMenu(0,MF_BYPOSITION|MF_POPUP,(UINT)subMenu.m_hMenu,"Layers");
menuPopup.GetSubMenu(0)->InsertMenu(1,MF_BYPOSITION|MF_SEPARATOR,0,"");
menuPopup.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this);
Following example if you wish to dynamically add menu item & also attach data to that menu item.
struct MyStruct
{
int abc;
};
CMenu MyMenu;
MyMenu.CreatePopupMenu();
CMenu MyMainMenu;
VERIFY(MyMainMenu.LoadMenu(IDR_MAIN_MENU_ID));
MyMainMenu.InsertMenu(0, MF_POPUP, (UINT_PTR)MyMenu.m_hMenu, _T("Main Menu"));
const int iMenuAdds = 5;
for (int i = 0; i < iMenuAdds; ++i)
{
MyStruct myStruct;
myStruct.abc = i+10001;
CString MenuDesc;
MenuDesc.Format(_T("MenuNo: %d"), i);
MENUITEMINFO tmpItem;
tmpItem.fMask = MIIM_STRING | MIIM_ID | MIIM_DATA;
tmpItem.fType = MFT_STRING;
tmpItem.fState = MFS_ENABLED;
tmpItem.wID = i + 101; //See note 1. below.
tmpItem.dwItemData = (ULONG_PTR)&myStruct; //data set.
tmpItem.dwTypeData = MenuDesc.GetBuffer(); //string description
tmpItem.cch = MenuDesc.GetLength();
tmpItem.cbSize = sizeof(tmpItem);
MyMenu.InsertMenuItem(i, (LPMENUITEMINFO)& tmpItem, FALSE);
}
To retrieve menu item & associated data:
CMenu* pPopup = &MyMenu; //or CMenu* pPopup = MyMainMenu.GetSubMenu(0); depending on parent.
ULONG_PTR lRetVal = pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, this, NULL);
//^^lRetVal should return same value as tmpItem.wID above.
MENUITEMINFO tmpItem;
tmpItem.cbSize = sizeof(MENUITEMINFO);
tmpItem.fMask = MIIM_STRING | MIIM_ID | MIIM_DATA;
tmpItem.fType = MFT_STRING;
TCHAR dwTypeData[256];
tmpItem.dwTypeData = dwTypeData;
tmpItem.cch = 256;
pPopup->GetMenuItemInfo(lRetVal, &tmpItem, FALSE);
MyStruct *myStruct = (MyStruct*)tmpItem.dwItemData; //and now we have our data.
Used as your #define & can be used for ON_COMMAND_RANGE(idFirst, idLast, Function), so would still need to have some sort of defined range if you were planning on using ON_COMMAND_RANGE. Alternatively: use command range for dynamic menu or create your own within the data set. Also need to make sure any ranges used do not conflict with any already #defined menu items on the same or parent menu.
Added above as I found this thread from googling due to an issue & I was already using the accepted answers method for adding menu items.

Get button focus - MFC

I have a VC++ MFC dialog application and in my OnTimer function I am just trying to determine which button in my dialog currently has focus.
Here is some pseudocode of what I am trying to accomplish....
CDialog::OnTimer()
{
CButton *btn = GetButtonOnFocus();
int btnID = btn->GetDlgCtrlID();
}
I haven't tried it, but this should work:
CWnd * pFocus = GetFocus();
int btnID = 0;
if (pFocus != NULL && pDialog->IsChild(pFocus))
btnID = pFocus->GetDlgCtrlID();
This won't restrict the result to buttons only - to do that, you need to use GetClassName and compare to "button".