MDI child frame without a close button

I'm new to MFC and Windows programming, making the leap from DOS (a dark screen with a C:\>, remember?)

Can I create an MDI child frame that does not have a close button. I do not want the user to be able to close the first child window opened when the program starts. I've played around with the ::LoadFrame(), ::Create(), and ::PreCreateWindow() member functions of the CChildFrame class to no avail. I've tried various combinations of the WS_XXXX bits in the frame styles, but haven't hit the correct one. Is this possible in MFC?

Note that I also have to get rid of the Close option from the Windows system menu.

Thanks,
Nelson Chenkin
nchenkinAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Tommy HuiEngineerCommented:
No, it is possible. But you can do the next best thing by trapping WM_SYSCOMMAND in the child frame and if the number of child windows is 1 (meaning it is the first), then ignore the command if the wparam has SC_CLOSE in it:

  void MyWnd::OnSysCommand(wp, lp)
  {
    BOOL ignore = FALSE;
    if (count == 1)
      if ((wp & SC_CLOSE) == SC_CLOSE)
        ignore = TRUE;
    if (!ignore)
      CChildFrame::OnSysCommand(wp, lp);
  }


   
0
t004024Commented:
GetSystemMenu, gives a handle to the system menu. Maybe U can get this and manipulate the menu using the CMenu class functions. U need to call the GetSystemMenu for the MainFrame window. SO Maybe U could do something like
CMenu *tMenu = AfxGetMainWnd()->GetSystemMenu() and then call
tMenu->RemoveMenu(SC_CLOSE, MF_BYCOMMAND); and then call maybe
CMDIChildWnd *ptWnd = GetParent() // This is only if U are in the View class otherwise, call MDIGetActive of the CMDIFrameWnd and call the SetSystemMenu and RemoveMenu for this.
0
footlooseCommented:
***** The standard code generated by MFC for CChildFrame contains this override:

BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
     // TODO: Modify the Window class or styles here by modifying
     //  the CREATESTRUCT cs

     return CMDIChildWnd::PreCreateWindow(cs);
}

***** Change it as below:

BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
     cs.style &= ~WS_SYSMENU;
     cs.style |= WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
     return CMDIChildWnd::PreCreateWindow(cs);
}

***** This gets rid of the system menu but retains the minimize/maximize buttons.
***** As THUI suggested, you need to override WM_SYSCOMMAND handler as below.
***** WM_SYSCOMMAND may not appear on the standard message list, so you will
***** have to go to the CLASSINFO tab of class wizard (for CChildFrame), change
***** MESSAGE FILTER option to WINDOW, and then go back to MESSAGE MAPS tab
***** and override WM_SYSCOMMAND handler as below:

void CChildFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
     if ( nID == SC_CLOSE )
     {
          //do some checking, as THUI suggested and/or call the default
     }
     CMDIChildWnd::OnSysCommand(nID, lParam);
}

***** Also remember to go to IDR_appTYPE menu on the resource editor and get rid of
***** FILE/CLOSE menu item.
0
Introducing Cloud Class® training courses

Tech changes fast. You can learn faster. That’s why we’re bringing professional training courses to Experts Exchange. With a subscription, you can access all the Cloud Class® courses to expand your education, prep for certifications, and get top-notch instructions.

nchenkinAuthor Commented:
Footloose,

I tried as you suggested in the ::PreCreateWindow() function. However this got rid of ALL three buttons: the minimize and maximize buttons were not displayed. I could still maximize the child by double clicking on the title bar, and then use Window/Cascade to restore it, but the buttons weren't there. This was one of the things I stumbled on in my attempts.

By the way, to ensure nothing else had changed I did this in newly generated App Wizard code.

Thanks,
nchenkin
0
nchenkinAuthor Commented:
Adjusted points to 100
0
BeatachonCommented:
Does the window still have the normal looking title bar? Make sure you have your operators right. I.E: cs.style |= and NOT cs.style =.....
0
nchenkinAuthor Commented:
The title bar looks normal execpt the system menu icon on the left is gone and there are no buttons at all on the right.

Here's the code lifted from the file. Again, this is the only change to a new App Wizard app using all defaults.

BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
   // TODO: Modify the Window class or styles here by modifying
   //  the CREATESTRUCT cs

   cs.style &= ~WS_SYSMENU;
   cs.style |= (WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
   return CMDIChildWnd::PreCreateWindow(cs);
}


0
footlooseCommented:
duh! how come I missed the missing minimize/maximize boxes!! Sorry! Will keep trying.
0
footlooseCommented:
OK, let's try again: t004024's earlier suggestion on using GetSysMenu works. I tried this out, and the close button is disabled. Thui's earlier suggestion to trap WM_SYSCOMMAND still has to be carried out if you want to prevent Ctrl-F4 from closing the child window.

Override CChildFrame::Create as below:

BOOL CChildFrame::Create(  LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
                                                     DWORD dwStyle, const RECT& rect,
                                                     CMDIFrameWnd* pParentWnd, CCreateContext* pContext )
{
   if ( CMDIChildWnd::Create( lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd,
                                                    pContext ) )
   {
      CMenu *p = GetSystemMenu( FALSE );
      p->RemoveMenu( 5, MF_BYPOSITION );
      p->RemoveMenu( 5, MF_BYPOSITION );

      return TRUE;
   }

   return FALSE;
}

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
nchenkinAuthor Commented:
Footloose, Thanks it worked!

But I wound up combining your suggesstion with t004024's comment. The final code is:

BOOL CChildFrame::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
                         DWORD dwStyle, const RECT& rect,
                         CMDIFrameWnd* pParentWnd, CCreateContext* pContext)
{
   if ( CMDIChildWnd::Create( lpszClassName, lpszWindowName, dwStyle,
                              rect, pParentWnd, pContext ) )
   {
      CMenu *p = GetSystemMenu( FALSE );
      p->RemoveMenu( SC_CLOSE, MF_BYCOMMAND );
      return TRUE;
   }
   return FALSE;
}

This gets rid of having to call RemoveMenu twice as your example required (why is that?!),
and also changes the hardcoded '5' to a friendlier SC_CLOSE symbol.

I realize I also need to implement Thui's trap.
Thanks all for your help.
0
footlooseCommented:
Final comment: RemoveMenu is called twice because you have to remove CLOSE first and then the seperator below it. Otherwise you get two seperators one next to another.
0
nchenkinAuthor Commented:
Footloose, A final comment to your final comment:

Gotcha. Given that, I'll probably put the 2nd call back in to get rid of the seperator.
Thanks for the clarification.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.