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);
Related
I have an C++ MFC application created in Visual Studio 2015.
I want to add a new tab to the application and created this function in the mainFrame class:
void CMainFrame::OnCustomerNewcustomer()
{
const CObList &tabGroups = GetMDITabGroups();
CMFCTabCtrl *wndTab = (CMFCTabCtrl*)tabGroups.GetHead();
CCustomerList *customer = (CCustomerList*)RUNTIME_CLASS(CCustomerList)->CreateObject();
((CWnd*)customer)->Create(NULL, NULL, WS_VISIBLE | WS_CHILD, CRect(0, 0, 20, 20), this, IDD_FORMVIEW_NEW_CUSTOMER);
wndTab->AddTab(customer, _T("New Customer"), -1, 1);
}
The new tab is showed in the tab controller but if I selecte the tab it does not show the frame in IDD_FORMVIEW_NEW_CUSTOMER it only show the last selected tab's frame. Does anyone know how to fix this?
You are mixing two concepts you should not: MDI Child Windows and the CMFCTabCtrl tabs.
I presume your CMainFrame class is descendant from CMDIFrameWndEx or anything similar. If you want to have a MDI application, you should read more about Document/View architecture.
You will need to have at least one CMultiDocTemplate (or a derived class) object, which will have an association of {Document, Child Frame, View}
Your code on OnInitInstance will look something like:
CYourDocument* pDoc = /*WriteFunctionToGetApplicationDocument*/();
if (!pDoc)
{
ASSERT(FALSE);
return FALSE;
}
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_YOUR_DOCUMENT_TYPE,
RUNTIME_CLASS(CYourDocument),
RUNTIME_CLASS(CYourChildFrame),
RUNTIME_CLASS(CYourView));
if (!pDocTemplate)
{
CoUninitialize(); // if you did CoInitailize before
return FALSE;
}
AddDocTemplate(pDocTemplate);
In the procedure you want to add a new tab, refer to that template and instruct to create the respective frame:
CYourChildFrame* pFrame = NULL;
// add code to see pFrame is already open
if (!pFrame)
{
CWaitCursor wc;
CDocTemplate* pDocTemplate = /*WriteFunctionToGetApplicationMultiDocTemplate*/();
if (!pDocTemplate)
{
ASSERT(FALSE);
return;
}
pFrame = dynamic_cast<CYourFrame*>(pDocTemplate->CreateNewFrame(pDoc, NULL));
if (!pFrame)
{
ASSERT(FALSE);
return;
}
pDocTemplate->InitialUpdateFrame(pFrame, pDoc);
}
if (pFrame)
MDIMaximize(pFrame);
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);
}
}
I' m new with MFC. I needed to create a floating toolbar (CToolBar) with no option of docking and save and restore its last pos.
The toolbar also should be active all the time, but its NOT.
When I'm openning a new child window (dialog for instance) from the mainframe, the floating tool bar become not active (I can not click on its buttons, or drag it etc..).
In the past I've used CDiaolog with Overlapped style and it was floating and always active as I needed. Is it possible to do the same with my Floating Toolbar? Thanks
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
toolbarIconSize.cx = toolbarIconSize.cy = TOOLBAR_MAIN_ICON_SIZE;
if ( !m_wndMyFloatingToolbar.Create(this,m_wndMyFloatingToolbar.GetBarStyle() |WS_EX_PALETTEWINDOW | WS_EX_TOPMOST |CBRS_FLOATING | WS_VISIBLE) ||
!m_wndMyFloatingToolbar.LoadToolBar(IDR_GENERAL_TOOLBAR, toolbarIconSize))
{
TRACE0("Failed to create My Floating Toolbar\n");
return -1; // fail to create
}
m_wndMyFloatingToolbar.EnableDocking(0);
EnableDocking(0);
if (!CreateCtrlBar())
{
TRACE0("Failed to create ctrl toolbar\n");
return -1; // fail to create
}
// ...
//...
return 0;
}
void CMainFrame::OnViewToolBar()
{
// ...
//...
CPoint Pos = MyFloatingToolbarGetLastPosition(); \\Get last pos
FloatControlBar( &m_wndMyFloatingToolbar, Pos, CBRS_ALIGN_LEFT );
MyFloatingToolbarSetIsVisible();
FloatControlBar( &m_wndMyFloatingToolbar, Pos, CBRS_ALIGN_LEFT );
}
void CMainFrame::MyFloatingToolbarSetIsVisible()
{
WINDOWPLACEMENT wp;
m_wndMyFloatingToolbar.GetParent()->GetParent()->GetWindowPlacement(&wp);
wp.showCmd = SW_SHOW;
m_wndMyFloatingToolbar.GetParent()->GetParent()->SetWindowPlacement(&wp);
m_wndMyFloatingToolbar.GetParent()->GetWindowPlacement(&wp);
wp.showCmd = SW_SHOW;
m_wndMyFloatingToolbar.GetParent()->SetWindowPlacement(&wp);
m_wndMyFloatingToolbar.GetWindowPlacement(&wp);
wp.showCmd = SW_SHOW;
m_wndMyFloatingToolbar.SetWindowPlacement(&wp);
}
void CWJToolBar::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
{
CToolBar::OnWindowPosChanging(lpwndpos);
if ( GetBarStyle() & CBRS_FLOATING )
{
if((lpwndpos->flags & SWP_HIDEWINDOW) && ((this->GetParentFrame())->m_hWnd !=(this->GetTopLevelFrame())->m_hWnd))
{
CMainFrame* mf = (CMainFrame*)(AfxGetApp()->GetMainWnd());
mf->MyFloatingToolbarSavePosition();
}
}
}
You may need to debug to view its coordinates if they are correctly set. Be independent. :p
Based on your current posted code, I don't see the point of your stored data, try this
hiding your toolbar
saving its position data
changing your parent windows position and
reloading your saved coordinates.
The saved data becomes incorrect values then.
I suggest you capture the position to which you want to add your toolbar live . This makes your toolbar application more generic.
So,
Save your toolbar's i.e top-left distance to its parent windows, not its coordinates
Get your parent windows coordinates
Reload your toolbar based on the saved distance
There are of course other ways to do this but I think this is more trivial to accomplish what you may be looking for.
Use CMFCToolBar (instead CToolBar), then you need only 2 commands, to achieve this.
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1)
return -1;
:
m_wndToolBar.SetPermament(TRUE); // it removes CloseButton (=always active)
CRect rect;
GetClientRect(&rect);
ClientToScreen(rect);
rect.OffsetRect(100, 20);
m_wndToolBar.FloatPane(rect); // Float and move it to your wished coordinates
:
}
I'm having the following code:
CMainFrame* pFrame = new CMainFrame;
if (!pFrame)
return FALSE;
m_pMainWnd = pFrame;
// create and load the frame with its resources
pFrame->LoadFrame(IDR_APP_MAINFRAME,
WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,
NULL);
// The one and only window has been initialized, so show and update it
pFrame->ShowWindow(SW_SHOWMAXIMIZED);
The problem is, when I press <ALT>, the menu(IDR_APP_MAINFRAME) will popup.
How can I always hide the menu and do not response to presss?
I had heard this is due to an accelerator control in MFC, but I couldn't see the control in my project solution which is using VS2008..
In your CMainFrame override PreCreateWindow and destroy the menu. Try something like this:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if(cs.hMenu!=NULL)
{
::DestroyMenu(cs.hMenu);
cs.hMenu = NULL;
}
return CFrameWnd::PreCreateWindow(cs);
}
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.