Click here to Skip to main content
Click here to Skip to main content

Multiple Top Level Windows

By , 8 May 2003
 
  • Download demo project - 7 Kb
  • Download source files - 18 Kb
  • Introduction

    MFC, out of the box, supports three interface models, MDI, SDI, and dialog based. What it doesn't have is support for MTLW (ok, so MS probably has a different acronym for it, but what I mean is Multiple Top Level Windows). This is the model popularized by those web browsers we all know and love, Netscape Navigator and Internet Explorer.

    Problem

    The main problem with MTLW and MFC is the main window (aka CWinApp::m_pMainWnd).

    1. MFC expects there to be one.
    2. MFC will close the application as soon as the main window is closed.

    Solution

    So what we need is a way to keep CWinApp::m_pMainWnd pointed at a valid TLW and switch it from one window to another as needed to make sure that the app doesn't shut down except when the last TLW is closed.

    Two classes will help you do this:

    • CMultiTopApp - should be used as the base class for your app object.
    • CTopLevelFrame - should be used as the base class for CMainFrame.
    Derive from these classes and wire up the message maps properly, and that's it. You're app now supports MTLW. Of course you need to have ways of creating new TLWs and probably you want to have a command to close all the windows and shut down your app, so the demo project includes those functions.

    First, a handler for the "New Window" menu item creates new TLWs.

    void CMultiTopSample::OnFileNewwindow() 
    {
    	CMainFrame* pFrame = new CMainFrame;
    
    	// create and load the frame with its resources
    	pFrame->LoadFrame(IDR_MAINFRAME,
    		WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,
    		NULL);
    
    	// The one and only window has been initialized,<BR>	// so show and update it.
    	pFrame->ShowWindow(SW_SHOW);
    	pFrame->UpdateWindow();
    }

    Second, unless you want your users to have to close each window individually, you may want to have an "Exit" menu item which does the following:

    void CMultiTopSample::OnAppExit() 
    {
    	CloseAllFrames() ;
    }
    Is this easy, or what!

    How it works (briefly), and a BONUS member function:

    CMultiTopApp has an STL list for keeping track of TLWs...

    list< CTopLevelFrame* > m_listMainFrames ;
    ... and a few functions for manipulating them:

    void AddFrame( CTopLevelFrame* pFrame ) ;         // Adds a TLW to list
    void KillFrame( CTopLevelFrame* pFrame ) ;        // Removes TLW from list
    void ReplaceMainFrame( CTopLevelFrame* pFrame ) ; // Makes a differnt TLW the<BR>                                               // MFC main window if possible

    Plus a special bonus function. I use this to do print preview in YATLW and disable all the regular TLWs while I do so.

    void EnableFrames( BOOL bEnable ); // Enables/Disables all TLWs in list

    CTopLevelFrame uses the CMultiTopApp member functions in response to window creation and destruction messages.

    MESSAGE Handler Action
    WM_CREATE OnCreate() calls AddFrame() after default processing
    WM_CLOSE OnClose() calls ReplaceMainFrame() before default processing
    WM_DESTROY OnDestroy() calls KillFrame() after default processing

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here

    About the Author

    Sir Gras of Berger
    Web Developer
    United States United States
    Member
    No Biography provided

    Sign Up to vote   Poor Excellent
    Add a reason or comment to your vote: x
    Votes of 3 or less require a comment

    Comments and Discussions

     
    You must Sign In to use this message board.
    Search this forum  
        Spacing  Noise  Layout  Per page   
    NewsVisual Studio 2010 has it builtinmemberkiwi_4 Oct '10 - 11:08 
    New Project Wizard > Visual C++ > MFC > MFC Application > Application type: Multiple top-level documents
    GeneralLicensememberBarryHolleran9 Apr '08 - 0:25 
    Hi,
     
    Can you tell me if I am free to use this in a comercial application?
     
    Thanks
    GeneralAfxMessageBox problem and solutionmemberKHDev4u21 Apr '05 - 9:55 
    This class is wonderful. Thanks.
     
    I noticed that if the application ever pops up a message box it is possible that it will use the first frame that was created instead of the active frame for the parent of the dialog box. This activates the other frame and is annoying. This can be fixed by overriding DoMessageBox. I tried override WM_ACTIVATE in CTopLevelFrame and setting m_pMainWnd to this when a window was activated (only for WA_ACTIVE and WA_MOUSEACTIVE) but that didn't work. Anyways, here is the code for the CWinApp::DoMessageBox override.
     
    int CMultiTopApp::DoMessageBox(LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt)
    {
    	//If a different main frame is activated use that one instead.
    	CWnd* pActiveWnd = CWnd::FromHandle(GetForegroundWindow());
    	CFrameWnd* pParentFrame = NULL;
    	if (NULL != pActiveWnd){
    		if (pActiveWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))){
    			pParentFrame = (CFrameWnd*)pActiveWnd;
    		} else {
    			pParentFrame = pActiveWnd->GetParentFrame();
    		}
    	}
    	//It's important that we don't modify the main wnd permanently!
    	CWnd* pOldMainWnd = m_pMainWnd;
    	if (NULL != pParentFrame){
    		m_pMainWnd = pParentFrame;
    	}
    	int nRet = CMultiTopApp::DoMessageBox(lpszPrompt, nType, nIDPrompt);
    	m_pMainWnd = pOldMainWnd;
    	return nRet;
    }
    

     
    -Kevin Hoffman
    Generalwhen the MainForm is Minimizedmemberqiaoyun28 Feb '05 - 22:55 
    Thank you for your great sample.
    But How can I get only one icon on start bar of Windows when I minimized the MainForm of the application?
    GeneralGreat !memberRodrigo Pinho Pereira de Souza7 Jul '04 - 9:46 
    Great ! Good job !
     
    Its almost that I am looking for.
     
    I just change few lines of your code, and implemented on MDI.
     
    I am using in Doc/View Architecture.
     
    Thanks. Smile | :)
     
    Rodrigo Pinho Pereira de Souza
    GeneralQuestionmemberlequang5 Feb '04 - 19:34 
    I want write a mfc application(MDI doc/view) same as winword 2000(XP) style.
    A document appear in Taskbar
    GeneralSmall extension to this excellent class...memberBrendan Tregear26 Aug '03 - 19:01 
    I've added a small function to CMultiTopApp that allows you close frame windows only of a certain type. In my situation, I've got a screen to browse the database records, one to browse images and one to edit each individual image. If the user closes the image browsing screen, all the image editing screens need to close:
     

    /*
    * Close frames of the appropriate type, as long as it is not the main window
    */
    void CMultiTopApp::CloseFramesKindOf(const CRuntimeClass * pClass)
    {
    MSG msg ;
     
    //
    // clear out pending messages
    //
    while( ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ))
    ::DispatchMessage( &msg ) ;
     
    std::list::iterator ppFrame = m_listMainFrames.begin() ;
    while ( ppFrame != m_listMainFrames.end() )
    {
    if ( (*ppFrame) != m_pMainWnd )
    {
    if ((*ppFrame)->IsKindOf(pClass))
    {
    (*ppFrame)->PostMessage( WM_CLOSE ) ;
    }
    }
     
    ppFrame++ ;
     
    //
    // because top-level window processing is based on both WM_CLOSE
    // and WM_DESTROY being processed, we make sure that all messages
    // have been processed before continuing
    //
    while( ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ))
    ::DispatchMessage( &msg ) ;
    }
    }

     
    declared as:

    void CloseFramesKindOf(const CRuntimeClass * pClass);

     
    and to use it:
     

    CMultiTopApp * pApp = (CMultiTopApp*) AfxGetApp();
    pApp->CloseFramesKindOf(RUNTIME_CLASS(CEditFrame));

     
    I thought I'd post just in case anyone else found it useful (not that it's hard to work out for yourself! Smile | :)
    GeneralMultiple Top Level Windows in BCB 6memberbmanjones24 Feb '03 - 4:31 
    Hi, I am new to GUI development and have currently being working with Borland C++ Builder 6. I was wondering if anyone knows how to accomplish the multiple top level windows program described in the article above using the Borland libraries.
     
    Thanks
    Ryan
    GeneralNote for VC++ 7 (.NET) usersmemberTunny9 Feb '03 - 9:18 
    Just something to look out for in the future
     
    I don't remember seeing the option in VC++ 6, but the MFC class wizard in the .NET version theres an option for a "Mulitple Top Level Frame" application.
     
    Looks like they've taken note and removed the need for this idiom
    GeneralRe: Note for VC++ 7 (.NET) usersmemberBrendan Tregear7 Aug '03 - 16:35 
    Just tried this out. Their implementation is limited in that if any frame is closed the whole application is closed.
    GeneralRe: Note for VC++ 7 (.NET) userssussjames@tunny.co.uk7 Aug '03 - 20:09 
    Are you sure ?
     
    I just tried a simple test. Created a Visual C++, MFC Application, Multiple Top Level everything else default. Select "New Frame". From this point there are two distinctions dependent upon which Frame you decided to close.
    Case 1 - If you close the originating frame (parent) then you're correct all frames do close.
    Case 2 - If you close the newly created frame (child) that is the only frame closed, the parent isn't touched.
     
    I suppose it all depends on your requirements. I'd guess overriding the onClose method of the parent is one option, but I'm not sure how well this sits with killing the parent frame and having the child window living. Maybe the orginal idiom does have advantages, unfortunately I haven't the time to look into this fully now, was something I worked on 6 months ago.
     
    I just wanted to make people aware of the options.
     
    GL with your project.
     

     

    GeneralRe: Note for VC++ 7 (.NET) usersmemberBrendan Tregear11 Aug '03 - 0:29 
    Hi James,
     
    Yeh, looking at what I wrote before, I didn't make it very clear did I? What you've written is what I was trying to say.
     
    In the application I'm writing, I've got three CFrameWnd classes that perform three distinct functions - searching, browsing and editing. They all connect to the same document. I'm switching from one frame window to the next (only keeping one open at a time), as the user moves around the application. I'm finding that it's a little disconcerting for the user to see the window disappear and another one pop up. I'm going to try and change it so that the new window opens in exactly the same size as the previous window....
    QuestionHow to deal with DocumentsmemberJoe Varadi6 Feb '02 - 23:34 

    Great example, but it's seems to be missing any mention of documents.
     
    I'm asking because this code doesn't use the CDocTemplate to create a frame-view-doc triplet, but the more brute-force approach of calling CFrameWnd::LoadFrame and CView::Create.
     
    So what's the glue code to tie in documents into this architecture?
     

    GeneralIsn't the Way "Multiple Top-Level Windows" are donesussBrian Hart7 Jul '00 - 20:07 
    Multiple Top-Level Windows in Internet Explorer and Netscape are seperate threads of the same process, and it's OK; they have Taskbar entries.
     
    When you have a thread that's just a worker function, we call it a "worker thread;" an example is a function that performs some math.
     
    However, anytime a Window appears on the screen, it's part of a "user-interface thread." The way CWinApp is implemented was changed in MFC 4.2 under VC++ 6. The main window that you see is actually the main window of the only user-interface thread your program creates. Notice that CWinApp isn't derived from CCmdTarget directly anymore; it's now derived from CWinThread, the base-class for all user-interface threads.
     
    To start, look up AfxBeginThread() in the help. Smile | :) This article is a good start, but is really a kludgy hack when you consider how it's done, what with main window lists and all. Smile | :)
     
    Also, there are several MFC samples which illustrate multi-threadedness, and I'm sure there's a MultiPad version which is multi-UI-threaded. Smile | :) Let your mouse do the walking!
     
    Yours,
    Brian Har
    GeneralRe: Isn't the Way sussDon Grasberger10 Jul '00 - 3:49 
    Whether or not you have multiple UI threads (I've never seen the point myself), MFC needs to be accomodated if you're going to have multiple top level windows.
     
    Look at the MFC source code.
     
    When a CFrameWnd receives a WM_CLOSE message _AND_ it is pointed to by the CWinApp's m_pMainWnd member, it tries to end the program. The classes in the article juggle m_pMainWnd so that MFC only ends the program when the last TLW is closed
    GeneralRe: Isn't the Way sussRob Krakora31 Jul '00 - 16:03 
    Brian:
     
    I have been writing Windows applications using Stingray's MTI class for three years now. This class enables your application to have multiple top level windows within a single thread. I have the Stingray source, and while their implementation is somewhat cleaner, the core functionality is the same. By the way, Stingray does have a multi-UI-threaded Multipad example.
     
    Regards,
     
    Rob Krakor
    GeneralRe: Isn't the WaymemberVaclav7 Aug '03 - 10:12 
    Just got a taste of doing mutithreading in MFC and found that transfering data between threads is a major task in MFC.
    It appears that MFC went to lot of trouble keeping threads from stepping on each other but forgot that the purpose od many application is to process data. MFC does not do that well - for example I ended up posting a message in a new thread to MFC MainFrame and propagating this message to the view for processing. Simply put - long way around the barn process.
     
    I have not tested this application - I do need doc/view.
     
    What I need is single user interface and than I need multi document with muti view and be able to process these and show results to the user.
     

    What I have done so far - using multithreading is complicated way to do it.
    GeneralRe: Isn't the WaymemberGiles12 Jul '05 - 3:57 
    Hi Rob, di you know what the sample is called?
    QuestionHow to avoid > 1 entry in the taskbar?sussPeter Andersson19 May '00 - 11:09 
    Hello!
    I saw your article and I does the same as the MTI model in Stingray Objective Toolkit. All window are top-level and that's OK. However every window also get an entry in the taskbar.
     
    My app works like the old Visual Basic, with one top-window with menus and the N dialogs/windows with grids/formviews.
     
    Stingray also have a FDI model which I use today. However that model has the flaw that the parent is always 'behind' any of its children.
     
    So the MTI model creates too many taskbar entries (otherwise perfect for me) and the FDI model makes the 'main' window hard to find if hidden by child windows (otherwise it's fine too).
     
    Isn't there any way I can have the best of both worlds?
     
    Contact me on via e-mail if you need more info. I value all input on this topic since I have no idea how to solve this.
     
    /Peter
    AnswerRe: How to avoid > 1 entry in the taskbar?sussRob Krakora14 Jun '00 - 3:18 
    Peter:
     
    Hi, my name is Rob Krakora, and I am a Software Engineer with Thomson Consumer Electronics (RCA) in Indianapolis, Indiana. I to use the MTI from Stingray, and if you take a gander at Chris Maunder's article "Creating an application with no taskbar icon" in the "Doc/View" section and implement his code snippet, your problem is solved. I tried it in my MTI based application and the top level windows no longer show up in the task bar, but they can still be minimized on the desk top. See the code snippet below borrowed from Chris's article and modified to replace CFrameWnd with SECToplevelFrame.
     
    Happy coding,
     
    Rob Krakora
     
    class CMainFrame : public SECToplevelFrame
    {
    ...
    protected:
    CWnd m_wndInvisible;
    ...
     
    Then override CMainFrame::PreCreateWindow:
     
    BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
    {
    if (!SECToplevelFrame::PreCreateWindow(cs))
    return FALSE;
     
    // Create invisible window
    if (!::IsWindow(m_wndInvisible.m_hWnd))
    {
    LPCTSTR pstrOwnerClass = AfxRegisterWndClass(0);
    if (!m_wndInvisible.CreateEx(0, pstrOwnerClass, _T(""), WS_POPUP,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, 0))
    return FALSE;
    }
     
    cs.hwndParent = m_wndInvisible.m_hWnd;
     
    return TRUE;
    }
    GeneralRe: How to avoid > 1 entry in the taskbar?memberSam C2 Dec '01 - 8:49 
    I have seen people mention STRINGRAY Stuido. Do you or anyone else have a ballpark pricing scheme for the studio? Their website just says contact a sales rep. I made that mistake once with another company and now I get hounded constantly about when I would like to purchase the product.
     
    If they would just post a price I can say heck no, being a small time developer one who writes applications for in house development as well as some small hobby apps can't really afford the 1K-3K pricetags of many of the library and tools I would really like Smile | :)
     

     
    Sam C
    ----
    Allsys Technologies
    http://www.allsystech.com
    "Making software simpler..."
    GeneralRe: How to avoid > 1 entry in the taskbar?memberMike O'Neill9 May '03 - 12:48 
    Sounds like the menu in a fancy restaurant: If you need to ask, then you can't afford it Wink | ;)

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    Permalink | Advertise | Privacy | Mobile
    Web01 | 2.6.130516.1 | Last Updated 9 May 2003
    Article Copyright 2000 by Sir Gras of Berger
    Everything else Copyright © CodeProject, 1999-2013
    Terms of Use
    Layout: fixed | fluid