Removing the Frame Caption when Using CMFCVisualManagerOffice2007

When BCGSoft created the classes that Microsoft incorporated as the “MFC Feature Pack” in VS 2008 (and included in SP1), it seems their primary intent was to provide a nearly hands-free implementation of several popularly-requested user interface features.Especially with the “Visual Manager” classes (derived from CMFCVisualManager), it’s clear that the intent was to easily change the appearance of your MFC application to mimic the visual styles of popular Microsoft products like Office and Visual Studio.

It’s also clear that very little effort was spent to make these classes customizable or overridable.If you want your app to look just like Visual Studio 2005 or Office 2007, for instance, you’re in luck – they make it extremely easy to do.If you want your app to be “a lot like” one of these MS apps, but you need to change a couple of appearances or behaviors, you may be in for a lot of pain and frustration.

The Problem Description

I recently lost some more of what little hair I have helping a customer to customize his application that used the Office 2007 Visual Manager (CMFCVisualManagerOffice2007).Specifically, he needed to simply enable a menu item that would remove (or restore) the caption on his main frame window.

This sounds at first blush to be a fairly simple task, right?Just remove the WS_CAPTION style from the window, right?ModifyStyle( WS_DLGFRAME, 0, SWP_FRAMECHANGED ) ;

You see, the folks in the Office product group made the 2007 version so pretty, it apparently requires all sorts of magical incantations and love potions to emulate it.The source code for this class (and its supporting classes) is provided with MFC, so I encourage you to browse through it a bit.It’s pretty elaborate and even includes reading and parsing an XML file that describes the visual style.And the most important (for today’s discussion) point to note is that this visual manager draws the frame caption itself (see IsOwnerDrawCaption and DrawNcCaption), unless Desktop Window Manager (DWM) Composition is on or the ribbon bar is set to replace the frame caption.You can see already that this isn’t going to be as easy as flipping a bit.

But I did it. And I lost hair in the experimentation process.Blogging about it today isn’t going to regrow my hair, but it’ll make me feel a little better. J

The Resolution

First of all, since the visual manager class doesn’t provide a switch to flip the caption on or off (it just assumes there’s always a caption), I had to override the class just to provide this.Let’s see… what should I call my new class? ... CMyVisualManagerOffice2007, of course.

So, as you see, I added a Boolean variable to indicate whether or not to draw a caption, and I overrode the method to answer the question of whether or not to “owner draw” it.

Of course, I had to tell the app to use my new class.This means #include-ing the header file in MainFrm.cpp and replacing all instances of “CMFCVisualManagerOffice2007” with “CMyVisualManagerOffice2007” in that file (in the OnApplicationLook method, by default).

Next, I had to add a similar variable to the CMainFrame class and add some methods to implement the toggleable menu item to toggle this variable in the frame class:

You see that I called something that I named “SetFrameCaption”.This is the function where most of the magic happens to put all the pieces together.Its role is to synchronize the visual manager’s state with the toggle switch we just put into the main frame.

Note that if DWM composition is on, the visual manager doesn’t try to draw the caption itself and our override attempts just screw things up.Fortunately, the DWM does honor the WS_CAPTION style, so we can just toggle it off and let the DWM do the drawing.

I found that the RedrawAll() method, although somewhat radical, is required to make the visual manager redraw with the caption when we’re turning it back on.Also note that WS_CAPTION is really a combination of WS_BORDER | WS_DLGFRAME.Sometimes, I had to use only one of those styles when turning on or off the caption, in order to get the correct behavior.

I also found that the call to SetFrameCaption needed to happen in a couple of other places to make things work smoothly throughout. The OnCreate needs to set it initially, before any toggling will occur later:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

SetFrameCaption( (lpCreateStruct->style & WS_CAPTION) != 0 );

…

}

And I needed to override OnChangeVisualManager, because this app allowed the visual styles to be changed from the menu:

Of course, this is just a way to work around one of the many obstacles in the design of the Feature Pack classes. The CFrameImpl class is yet another example of a class that is very difficult to override. There are others. I'd like to see some serious effort go into revising these classes to allow customization. Of course, this would then open the possibility of introducing breaking changes to code that uses the current classes. I'd say it would be worth the risk.

Hello! Your tutorial has been very helpful to me, as I had to do exactly what you described. However I have another problem now: I have an MDI application and as the caption bar look is centralized in the visual manager class, overriding the owner draw enablement results in the MDI child frames to display a Windows Classic frame. Do you have any suggestion or resource I can refer to to fine tune the visual manager for my application? In this specific case the expected behaviour would be the one that Excel shows, displaying the beautifully owner drawn Office-style frames whatever the system theme is.

Again, thank you very much for this detailed tutorial, it really gave me a better insight in the MFC Feature Pack. I hope you can help me again, I've browsed the code and tried something these days but I really can't seem to get the hold of it.

I'm not really sure why you'd all of a sudden be getting a classic frame appearance unless you completely disabled the Visual Manager and "Ex" classes. If you're looking for the Excel look, that should be exactly what the Office VisualManager classes give, isn't it? You say you overrode the owner-draw enablement; are you talking just about the caption, as above? And if doing so led to the "classic" Windows look, does that mean it looks like most of the other applications on your desktop? If the VisualManager is disabled, unless you're still back in the world of Win XP, the Desktop Window Manager (DWM) takes over the painting.

In my particular case I have to dinamically hide and show the application caption in order to make possible the application to be shown in full screen mode (unfortunately, the ShowFullScreen() method doesn't work for me, as in MDI application it just shows the current child frame and not the entire application, which is what we need).

So what I did was creating my own visual manager, deriving it by CMFCVisualManagerOffice2007 and overriding the IsOwnerDrawCaption(). Then I added the SetFrameCaption() method to my application's MDI main frame, calling it when the fullscreen mode is turned on or off. It worked wonderfully (kudos to you) but the problem was that the IsOwnerDrawCaption() is called for MDI child frames too. This results, when using the Windows Classic theme (the one that enacts the pre-XP look'n'feel), in the child frames not being owner-drawn, thus getting the system caption bar, which is the good ol' blue strip with the three square buttons.

This centralization actually bothers when using an Aero theme: since the composition is enabled the MDI child frames are not owner-drawn and thus, again, receive the system caption bar (not the glassy one but the "Windows 7 Basic"-looking one). I've tried to force owner-drawn caption bars by overriding both the MDI child frames and my custom visual manager's OnNcPaint() and OnNcActivate() methods: I was successful in getting control of the child frames' painting process but unfortunately my attempts resulted in the child frames not drawing anything (despite successfully parsing the visual manager's XML and loading the bitmaps).

I hope I've been clear in explaining the situation, I'm relatively fresh to C++ and MFC development.

Again, thank you vey much for your attention and patience, I hope you may have a few more tips for me. 🙂

I regret that it sounds like your particular business requirements are going to require more thought and investigation than I can reasonably budget for replying to blog comments. Have you tried posting your inquiry on the public forums?

It sounds like you need to look for a solution that affects only the main frame and not the child frames. I feel confident there's a way to do it, but I can't devote the team to research it for you.

I tried to post this question to various forums but I didn't get any answer. It seems an uncharted territory. 🙂

I completely understand your position and would like to thank you for the time you spent replying my questions. Again, thank you very much for such an interesting blog entry, it really have been helpful.