Tag Archives: MDI Client

Double-click in MDI Client

It’s hard to believe that handling mouse double-clicks in MDI Client windows can be super useful, but I found this problem in a discussion forum. I presumed that it was not only for fun, so I tried to resolve it. First, I subclassed the MDI Client window in a CWnd-derived class. That is possible for a standard MFC project in this way:

But no success: WM_LBUTTONDBLCLK message handler is not called in CMDIClientWnd class. Then I tried to catch it in overridden PreTranslateMessage or even in a WH_MOUSE hook with the same result. Finally, I had a look using Spy++ tool. Bingo! The MDI Client (of predefined window class named “MdiClient“) has no CS_DBLCLKS style so it does not deal with mouse double-clicks.

MDIClient

MDIClient

All wat he have to do is to create the The MDI Client window with our own registered window class. This is possible by overriding CMDIFrameWnd::CreateClient.

Create a MDI client window which belongs to a window class having CS_DBLCLKS style

Handle MDI Client double-clicks in standard MFC applications

Once we have created the MDI Client window and subclassed it as shown above, can simply handle (for example) WM_LBUTTONDBLCLK in our CWnd-derived class.

Handle MDI Client double-clicks in Visual Studio or Office-style MFC applications

CMDIFrameWndEx uses its own CWnd-derived class for the MDI Client window, so we cannot use another one in  main frame class. However, this is not so big problem because we can now catch WM_LBUTTONDBLCLK in overridden PreTranslateMessage method.

Or, you can catch it in a WH_MOUSE hook, if think that can be more exciting. :)

Note that creating the MDI Client window in overridden CMDIFrameWnd::CreateClient cand be done in all cases in the same way.

Demo solution

Download: MDI client double-click samples.zip (288)
The sample Visual Studio solution contains two projects: one is a standard MDI application, and the other one is Office-style. Just enjoy of MDI Client double-clicks! :)

Resources and related articles

Custom Paint in MDI Client

In some MDI applications, would be nice to perform some custom painting the MDI Client area. For example we can fill the background with a brush or draw a logo image. One classic and well known method is to subclass the MDI Client window in our own class derived from CWnd MFC class. After subclassing, all we have to do is to handle WM_PAINT and/or WM_ERASEBKGND and perform our custom painting. There are tens of articles describing this method in detail, and you cand find some by clicking the links shown in the bottom of this article.

That works great if the main frame class is derived from CMDIFrameWnd, but it’s no more possible if the main frame class is derived from CMDIFrameWndEx (shipped with Visual Studio 2008 with Feature Pack and newer Visual Studio versions). That’s because CMDIFrameWndEx subclases the MDI Client window, itself (see CMDIFrameWndEx::m_wndClientArea of type CMDIClientAreaWnd). MFC, adds the subclassed window in a permanent map and does not allow subclassing twice. One can say “well, just set CMDIFrameWndEx::m_bDoSubclass to FALSE before calling CMDIFrameWndEx::OnCreateClient; next we can do subclassing as we wish”. That doesn’t work either, because in many places the framework expects using exactly CMDIFrameWndEx::m_wndClientArea and nothing else.

Searching for a solution

In an older discussion about a related subject, Joseph M. Newcomer proposed a simple trick: temporarily attach the MDI Client window handle to your own class, whenever it’s necessary to force  repainting the MDI Client area.

void CMyApp::RedrawClientArea()
{
CMySublcass w;
w.Attach(m_wndClientArea.Detach());
w->Invalidate();
w->UpdateWindow();
m_wndClientArea.Attach(w.Detach());
}

Next, just have to call CMyApp::RedrawClientArea in our MFC application, where necessary. That’s IMO very ingenious and should work but, as the author states himself, it’s “remarkably ugly”. So I spent some time  trying other tricks, which included the “hacking” of application thread maps. None was useful.

The right solution

Finally, I found the better solution. It is incredible simple: just have to override CMDIFrameWndEx::OnEraseMDIClientBackground virtual function!
Here is an example that fills the MDI Client area using a given brush.

Demo project

The demo project, attached here, is an MDI MFC application that paints in the MDI Client a custom background and a logo image.

Custom Paint in MDI Client - Demo

Custom Paint in MDI Client – Demo

 

Resources

See also

Downloads