Home made MDI windows list in Window menu
Why we need it.
Windows list is a part of standard MDI windows interface. There are some bug(s) admitted by Microsoft. See MS KB article Q125435 for example. It is (almost) impossible to modify it. It is much easier to reproduce Windows menu behavior. This project can serve as a start point for window menu modification. Add something to menu items, graphics for example, or create a different MoreWindows dialog. To include invisible MDIChilds to activate them from a menu etc.
How to do this thing.
First of all this example based on MFC application generated by AppWizard. Modifications are mostly made in MainFrm.cpp,.h module, using wizards when possible.
1. Remove standard windows list.
Modify all standard Window menu IDs generated by AppWizard. MDI interface relays on this IDs. So just rename ID_WINDOW_NEW to ID_WINDOWS_NEW ID_WINDOW_CASCADE to ID_WINDOWS_CASCADE etc. for all menu items under Window menu. AppStudio creates new IDs for existing items. Build and run project to be sure the windows list does not exist any more and standard MDI commands (New Window, Cascade, Tile...) don't work. Add the fifth item - separator to the menu. Add #define NUM_FIXED_ITEMS 5 to MainFrm.cpp2. Using ClassWizard add message handlers for new menu IDs.
Implement them however you want. For Example mimic a usual behavior.
void CMainFrame::OnWindowsTileHorz()
{
// TODO: Add your command handler code here
MDITile(MDITILE_HORIZONTAL);
}
Now we have reproduced a fixed part of Window menu. Build and try it.
3. Add a protected member function to return CMenu object for Window menu to CMainFrame
CMenu* CMainFrame::GetWindowMenu()
{
// MENU_POSITION is not reliable because it's different for different views
CMenu* pWindowMenu = NULL;
CMenu* pTopMenu = GetMenu();
CString strMenu;
for (int iPos = pTopMenu->GetMenuItemCount()-1; iPos >= 0; iPos--)
{
pTopMenu->GetMenuString( iPos, strMenu, MF_BYPOSITION );
if (strMenu == "&Window")
{
pWindowMenu = pTopMenu->GetSubMenu(iPos);
break;
}
}
ASSERT(pWindowMenu != NULL);
return pWindowMenu;
}
Now we are ready to modify the menu.
4. Add new ID to resource.h manually
#define ID_WINDOWS_FIRST 32001 #define ID_WINDOWS_2 32002 #define ID_WINDOWS_3 32003 #define ID_WINDOWS_4 32004 #define ID_WINDOWS_5 32005 #define ID_WINDOWS_6 32006 #define ID_WINDOWS_7 32007 #define ID_WINDOWS_8 32008 #define ID_WINDOWS_LAST 32009 #define ID_WINDOWS_MORE 32010Actually we need the first and two last. But just in case reserve all of them Also define a max number of windows in windows list in a menu. Add #define NUM_WINDOWS_ITEMS 9 to MainFrm.cpp
5. Add a protected member function looping through all visible MDI childs in CMainFrame to add them to Window menu.
void CMainFrame::UpdateWindowMenu()
{
CMenu* pWindowMenu = GetWindowMenu();
//Remove all entries after separator
for ( int i = pWindowMenu->GetMenuItemCount()- 1 ; i > NUM_FIXED_ITEMS - 1;
pWindowMenu->DeleteMenu(i--, MF_BYPOSITION) );
//m_hWndMDIClient - undocumented
CWnd* pWnd = CWnd::FromHandle(m_hWndMDIClient)->GetWindow(GW_CHILD);
CString strTitle;
i = 0;
char buff[3];
while(pWnd)
{
if(pWnd)
if(pWnd->IsWindowVisible())
{
if (i == NUM_WINDOWS_ITEMS) //Critical number ow windows (9) reached
{
pWindowMenu->AppendMenu( MF_STRING, ID_WINDOWS_MORE, "&Windows...");
return;
}
pWnd->GetWindowText(strTitle);
strTitle = CString("&") + itoa(i + 1,buff,10) + " " + strTitle;
pWindowMenu->AppendMenu( MF_STRING, ID_WINDOWS_FIRST + i++,strTitle);
}
pWnd = pWnd->GetWindow(GW_HWNDNEXT);
}
}
OK. Well done with implementation. Now we need to call this function from somewhere.
Not the best but possible place to do it is one of OnUpdateUI for a fixed menu item.
void CMainFrame::OnUpdateWindowsCascade(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
//It is a challenge to find a better place to call it from
UpdateWindowMenu();
}
7. Add a member function to activate a particular MDI child
void CMainFrame::ActivateFrameFromMenu(int nItem)
{
CWnd* pWnd = CWnd::FromHandle(m_hWndMDIClient)->GetWindow(GW_CHILD);
int i = ID_WINDOWS_FIRST;
BOOL bMaximized;
while(pWnd)
{
if(pWnd)
if(pWnd->IsWindowVisible())
if ( i++ == nItem)
{
MDIGetActive( &bMaximized );
if (!bMaximized)
MDIRestore(pWnd);
MDIActivate(pWnd);
return;
}
pWnd = pWnd->GetWindow(GW_HWNDNEXT);
}
}
6. Add message handler for all new menu items.
First we need to handle WM_COMMAND. Using ClassWizard add virtual OnCmdMsg and implement it like:
BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
// If pHandlerInfo is NULL, then handle the message
if (pHandlerInfo == NULL)
{
// Filter the commands sent to Window menu
if (nID >= ID_WINDOWS_FIRST && nID <= ID_WINDOWS_LAST)
{
if (nCode == CN_COMMAND)
// Handle WM_COMMAND message
ActivateFrameFromMenu(nID);
else
if (nCode == CN_UPDATE_COMMAND_UI)
// Update UI element state
DoUpdateWindowMenu(nID, (CCmdUI*)pExtra);
return TRUE;
}
}
// If we didn't process the command, call the base class
// version of OnCmdMsg so the message-map can handle the message
return CMDIFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
We also need a function to update UI for windows list items
void CMainFrame::DoUpdateWindowMenu(int nID, CCmdUI* pCCmdUI)
{
pCCmdUI->Enable(TRUE);
if (nID == ID_WINDOWS_FIRST)
pCCmdUI->SetCheck();
}
7. Add a windows list dialog.
CWinList is a CDialog derived class based on template with a non-sorted listbox and a couple of buttons. On Init Dialog we call a CMainFrame public function to fill a list box like we did for a menu.
((CMainFrame*) AfxGetMainWnd())->FillWinList(&m_lbWinList);
void CMainFrame::FillWinList(CListBox* pListBox)
{
CWnd* pWnd = m_wndMdiClient.GetWindow(GW_CHILD);
CString strTitle;
while(pWnd)
{
if(pWnd)
if(pWnd->IsWindowVisible())
{
pWnd->GetWindowText(strTitle);
pListBox->AddString(strTitle);
}
pWnd = pWnd->GetWindow(GW_HWNDNEXT);
}
pListBox->SetCurSel(0);
}
OnOk the dialog returns item selected:
void CWinList::OnOK()
{
// TODO: Add extra validation here
EndDialog(m_lbWinList.GetCurSel() + ID_WINDOWS_FIRST);
//CDialog::OnOK(); Do not call default implementation
}
8. Handle ID_WINDOWS_MORE command
The only thing left is to handle ID_WINDOWS_MORE command. There is a little problem here. ClassWizard doesn't see ID_WINDOWS_MORE command. So either add a handler manually or temporary add this extra item to any menu and use ClassWizard, then remove it.
void CMainFrame::OnWindowsMore()
{
int nItem = CWinList().DoModal();
if (nItem != IDCANCEL)
ActivateFrameFromMenu(nItem);
}
9. Build. Run. Enjoy!
Last Updated: January 15, 1999

Comments
Nike Air Max 1 FB publicity, father a strong color texture, the new shoes
Posted by Geozyoceada on 04/25/2013 03:47pmIn the summer in a tumbler guts the cool sprite seems to be a good preferred, but if the sprite "feet"? Will also give you a frisk, bring a sustenance! This summer, Nike and Sprite [url=http://markwarren.org.uk/property-waet.cfm]air max 90[/url] and his sneakers to a blend of outstanding example snow spread of non-professional, white and dejected color schematic in the time-honoured Nike Feeling Max 1 shoes let slip a refreshing impertinent scent.[url=http://markwarren.org.uk/goodbuy.cfm]nike free run uk[/url] Summer is the time to select a clean shoe, shoes should be a good choice. Qualifying series Nike Freshen Max HomeTurf borough recently definitely comes up, this series in the masterpiece Freshen Max shoes to London, Paris and Milan the three paid glorification to the iconic metropolis of Europe, combined with the characteristics of the three cities, Like Max 1 HYP,Air Max 90 HYP,Air Max 1 and shoes such as Quality Max 95, combined [url=http://northernroofing.co.uk/roofins.cfm]nike free run 3[/url] with the Hyperfuse, as kind-heartedly as a heterogeneity of materials, such as suede, Whether you want functional or retro-everything.
ReplyMore concessions with herveleger, more surprise!
Posted by wolemrfvtu on 04/04/2013 09:05amherve leger gown herve leger herve leger herve leger outlet
ReplySmall bug fix
Posted by blamond on 05/01/2008 03:15pmHi Sergey, Thanks for the code - it does exactly what it's supposed to and saved me some time. One small bug in your html code of
ReplyFillWinList(): instead ofCWnd* pWnd = m_wndMdiClient.GetWindow(GW_CHILD);it should be:CWnd* pWnd = CWnd::FromHandle(m_hWndMDIClient)->GetWindow(GW_CHILD);At least that's what I had to do to use it. Cheers, Bruce LamondHome made MDI windows list in Window menu
Posted by Legacy on 08/09/1999 12:00amOriginally posted by: Steve Sun
ReplyWindowMenu
Posted by Legacy on 04/22/1999 12:00amOriginally posted by: Suja
I tried this out but the list of open windows remained disabled.
Reply