Click to Rate and Give Feedback
Related Articles
Here we present techniques for programmatic and declarative data binding and display with Windows Presentation Foundation.

By Josh Smith (July 2008)
See how Windows Forms applications can be adapted to use the new .NET Add-in framework (System.AddIn) this month.

By Mueez Siddiqui (July 2008)
Chris Gray discusses custom applications he’s developed for Windows Home Server and explores applications for energy savings.

By Chris Gray (July 2008)
In this article, author John Torjo presents a guide to his C++ GUI library called eGUI++ and explains how it makes user interface programming easier.

By John Torjo (June 2008)
More ...
Articles by this Author
What's the deal with const functions, and lots more on the reasoning behind the design of the C++/CLI.

By Paul DiLascia (February 2007)
This month DLL problems, context menus, MFC strings to managed C++, and more.

By Paul DiLascia (October 2006)
This month Paul DiLascia teaches readers the right way to create dynamic dialogs, explains satellite DLLs and discusses language resource DLLs.

By Paul DiLascia (September 2006)
This month Paul DiLascia codes some Microsoft Office-style dialog box features.

By Paul DiLascia (August 2006)


By Paul DiLascia (July 2006)
Many of you are no doubt in the process of upgrading to Visual Studio® 2005, so I thought now would be a good time to relate some of my own experiences with the new compiler. What took me so long? Hey, I'm a retro kind of guy! Better late than never!.

By Paul DiLascia (June 2006)
This month: CWebVersion revisited using HTTP instead of FTP, and adding sounds to an MFC-based app.

By Paul DiLascia (May 2006)


By Paul DiLascia (April 2006)
More ...
Popular Articles
Here we introduce you to some of the concepts behind the new F# language, which combines elements of functional and object-oriented .NET languages. We then help you get started writing some simple programs.

By Ted Neward (Launch 2008)
Chris Tavares explains how the ASP.NET MVC Framework's Model View Controller pattern helps you build flexible, easily tested Web applications.

By Chris Tavares (March 2008)
Here the author uses Document Information Panels in the Microsoft 2007 Office system to manipulate metadata from Office docs for better discovery and management.

By Ashish Ghoda (April 2008)
Here the author introduces SQL Server Data Services, which exposes its functionality over standard Web service interfaces.

By David Robinson (July 2008)
More ...
Read the Blog
The most fundamental form of Web testing is HTTP request/response testing. This involves programmatically sending an HTTP request to the Web application, fetching the HTTP response, and examining the response for an expected value. In the May 2008 issue of MSDN Magazine, Read more!
In the November issue of MSDN Magazine, Jeffrey Richter demonstrates some recent additions to the C# programming language that make working with the APM significantly easier. In the June ...
Read more!
The July 2008 issue of MSDN Magazine is now available online. Here's what's in the issue: Data Services: Develop ...
Read more!
The June 2008 issue features the first installment of a new MSDN Magazine column on software design fundamentals. We’ll discuss design patterns and principles in a manner that isn't bound to a specific tool or lifecycle methodology. In this issue, Jeremy Miller starts the Patterns in Practice column ...
Read more!
In the April 2008 issue of MSDN Magazine, Kenny Kerr introduced the Windows Imaging Component (WIC), showing you how you can use it to encode and decode different image ...
Read more!
A combination of the retained-mode graphics system and notification mechanisms such as dependency properties unleash the flexibility and power of Windows Presentation Foundation (WPF, allowing these objects to be targets of data bindings and animations. In the June 2008 issue of MSDN Magazine, Charles ...
Read more!
More ...
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.
MIND
CPopupText for Home-grown Tooltips, Controlling Application Instantiation
Paul DiLascia
Code for this article: CQA0900.exe (109KB)
Q
I have a CListBox-derived class that implements an owner-draw listbox. I want each line of the listbox to be completely visible when the mouse points to the item. That is, when the user moves the mouse over a line I want to show a tooltip window with the entire text of the line at the current mouse position in case the text is wider than the listbox. I tried to do this with CToolTipCtrl::AddTool using the third parameter, but it didn't work. Could you please suggest a solution to this problem?
Mihai Frintu
Bucharest

A
I sympathize with your efforts to use CToolTipCtrl; for some reason no one ever seems to get MFC tooltips to work right the first time. I talked about MFC tooltips in the March 1997 issue of MSJ, and Roger Jack wrote the article "Tiptoe Through the Tooltips with Our All-Encompassing ToolTip Programmer's Guide" in the April 1997 issue of MSJ. Roger's article is the most thorough tooltip guide I know of.
      You could definitely make tooltips work, but what you want is so simple there's almost no need. So just to be contrarian, I'll show you a home-grown solution that uses a class, CPopupText (see Figure 1), that I wrote for the December 1999 issue of MSJ. CPopupText implements a popup window that looks like a tooltipâ€"black text on a light yellow background. CPopupText is like any CWnd-derived object; to use it you must instantiate and create it.

CPopupText wndText;
wndText.Create(...);
      The Create arguments are what you'd expectâ€"parent window, style, IDâ€"plus a CPoint that says where the upper-left corner is. Normally, you'll create the window as invisible (WS_VISIBLE off); then when you want to show a tip, you call SetWindowText to set the text, and ShowWindow to show it.

wndText.SetWindowText("hello, world");
wndText.ShowWindow(SW_SHOWNA);
      CPopupText changes the size of the window to fit the text exactly. By default, CPopupText uses the same font as the status line (as defined by lfStatusFont in the NONCLIENTMETRICS struct returned by SystemParametersInfo(SPI_GETNONCLIENTMETRICS)). It's important to use SW_SHOWNA since you don't want to make the window active, you just want to show it. Alternatively, you can call a special function CPopupText::ShowDelayed, which shows the tip after a specified number of milliseconds elapse. If you give a delay of zero, CPopupText shows the tip immediately; you can use this feature instead of calling ShowWindow. To hide the tip or cancel ShowDelayed, call CPopupText::Cancel.
      OK, so much for CPopupText. Let's move on to the list control. The goal is to implement some kind of doodad you can plop in your listbox to add the show-wide-text feature. CListBoxTipHandler is the answer I came up with. Figure 2 shows the source and Figure 3 shows it in action in my test program, LCTest.
Figure 3 LCTest
Figure 3 LCTest

      Using CListBoxTipHandler is pretty easy. All you have to do is instantiate and Init it. LCTest does it in a dialog.


class CMyDialog {
  CListBox m_wnd_List; // normal listbox
  CListBoxTipHandler 
    m_tipHandler;      // tip handler
•••
};

BOOL CMyDialog::OnInitDialog(){
•••
  m_tipHandler.Init(&m_wndList);
•••
}
Your listbox magically acquires the tip feature shown in Figure 3.
      CListBoxTipHandler is designed to be as easy as possible to use, but how does it work? If you look behind the curtain, you'll see CListBoxTipHandler is derived from CSubclassWnd, the class I use over and over again in my columns. CSubclassWnd lets you subclass a window in MFC without deriving a new class. That's important here; if I had derived a new CListBoxWithTips from CListBox, you wouldn't be able to use it if you had already derived your own CMyListBox. You would have to manually graft my changes onto your classâ€"yuck! CSubclassWnd lets CListBoxTipHandler subclass your listbox by instantiation, not derivation. For details, see C++ Q&A in the June 1997 issue of MSJ and the "Fun with MFC Goodies" trilogy beginning in the January 1997 issue of MSJ.
      When you call Init, CListBoxTipHandler subclasses the listbox, then intercepts all messages sent to your listbox. The only message that it cares about is WM_MOUSEMOVE. When the user moves the mouse in the listbox, the tip handler checks to see if the item under the mouse is completely visible; if not, it uses CPopupText to display the item text in a tip at the exact same position as the item itself. The details are mostly straightforward, therefore I will only highlight a couple of the tricky parts (see Figure 2 for all the details).
      If the user moves the mouse off an item for which a tip is displayed, CListBoxTipHandler calls CPopupText::Cancel to hide the tip. This works fine if the user moves from one item to another, but what happens if the user moves the mouse outside the listbox entirely? There's no way to know that a particular WM_MOUSEMOVE is the last one. It's the proverbial "get off the bus one stop before I do" paradox. To avoid it, CListBoxTipHandler captures the mouse on behalf of the listbox, so all mouse messages go to it, even when the user moves the mouse outside the listbox. When that happens, CListBoxTipHandler releases the mouse.
      That was trick number one. Trick number two has to do with activationâ€"or rather, nonactivation. To position the tip properly, CListBoxTipHandler calculates the rectangle and calls SetWindowPos. It's important to use SWP_NOACTIVATE here; otherwise the tip would be activated, deactivating the dialog and turning its title bar greyâ€"oops. I discovered this one the hard way. It's the same reason you must use SW_SHOWNA if you call CPopupText::ShowWindow.
      My test app, LCTest, uses a vanilla listbox, but CListBoxTipHandler should work with an owner-draw listbox, too; however, you may have to modify it to get the item text and determine its width. CListBoxTipHandler::OnGetItemInfo and CListBoxTipHandler::IsRectCompletelyVisible are the virtual functions to override. These are also the functions to change if you want to make CListBoxTipHandler work with other kinds of listbox-like controls, such as comboboxes and tree controls. You needn't bother with CListBoxTipHandler for tree controls, however, since they already have the tooltip feature built in.

Q
I wrote a dialog-based application and I'm able to open an associated file when the user double-clicks a file in Windows® Explorer. However, each time the user double-clicks a new file, Windows starts a new instance of my app. I know how to make sure my application only runs one instance, but how do I tell the sole instance the name of the file to open?
Hafeez Jaffer

A
The official way to handle this is to use Dynamic Data Exchange (DDE), which is a general-purpose interprocess communication mechanism that lets an application send a command such as open the document foo.txt to another application. You may already have noticed that some apps have registry entries like

HKCR/foofile/shell/open/ddeexec = Open("%1")
which tell Windows to send the DDE Open command with the file name as an argument to the running instance when the user double-clicks a .foo file. I won't go into DDE details here (read the docs), except to say that MFC has functions OnDDEInitiate, OnDDEExecute, and so on to support DDE. Unfortunately, these functions belong to CFrameWnd, which doesn't help if you have a dialog-based app where your main window derives from CDialog.
      Well, never fear. You don't really need DDE to implement the only-one-instance behavior. You can do it easily enough yourself. You just need a way to find the other running instance and a way to make it open a new file. I encapsulated both capabilities into a class called COneInstance (see Figure 4). COneInstance works in any kind of app that has a main window, whether it is derived from CFrameWnd or CDialog. To demonstrate this, I wrote two test apps: MyEdit (a frame-based text editor) and Dlg1Inst (a dialog-based app). You can download both apps from the link at the top of this article.
      To use COneInstance, you must do three things: instantiate, initialize, and then call the object from your app's InitInstance function. Since you only need one COneInstance object for the entire app, and since COneInstance works through your app's main window, it's best to instantiate it is as a global (static) member of your main window class. For example:

// in mainfrm.h
class CMainFrame : public CFrameWnd {
public:
  static COneInstance OnlyInstance;
};
      The reason for making OnlyInstance static is that you need to call it before CMainFrame has been created, as you'll soon see. When you instantiate COneInstance, you must supply two arguments: the name of your main window class and an integer message ID different from any other WM_ message ID your main window uses. I call this the ident message. For MyEdit, it looks like the following:

// in mainfrm.cpp
static LPCTSTR MYCLASSNAME = _T("MyEditApp10");
const WM_MAINFRM_ISME = RegisterWindowMessage
  ("WM_MyEditApp10_IsMe");
COneInstance CMainFrame::OnlyInstance
  (MYCLASSNAME, WM_MAINFRM_ISME);
      The window class name is MyEditApp10 and the ident message ID is WM_MAINFRM_ISME. Note that you must modify your main window's PreCreateWindow function to register and use MYCLASSNAME. (Oops, I guess there are four things you have to do.) COneInstance uses the class name and ident message to find other instances of your app.

CWnd* COneInstance::FindOtherInstance() const
{
  CWnd* pWnd = CWnd::FindWindow(m_sClassName, NULL);
  return (pWnd && pWnd->SendMessage(m_iIdentMsg)) ? pWnd : NULL;
}
      FindWindow finds the other window with the same class name as yours and, to ensure it really is yours, sends the special ident message looking for a TRUE response. COneInstance handles the message itself, so you don't have to do anything to make this part work, except supply the message ID.

LRESULT COneInstance::WindowProc(...)
{
  if (msg==m_iIdentMsg)
    return 1;
  return CSubclassWnd::WindowProc(...);
}
      If some other window happened to have the same class name as yours, it still wouldn't handle m_iIdentMsg (in this case WM_ MAINFRM_ISME), so its WindowProc would return zero. Of course, you must remember to hook up the COneInstance object after your main window is createdâ€"that's step number two.

int CMainFrame::OnCreate(...)
{

•••
  OnlyInstance.Init(this);
  return 0;
}
      For a dialog, the place to call Init is OnInitDialog. Whatever! Once you've instantiated your COneIn-stance object and called Init, you're ready to invoke it. All you need is few lines in your app's InitInstance function.

// standard MFC
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

// do only-one-instance behavior
if (CMainFrame::OnlyInstance.
  OpenOtherInstance(cmdInfo.m_strFileName))
    return FALSE; // exit this instance
      OpenOtherInstance does everything you want: if no other instance is running, it returns NULL and control flows through the rest of InitInstance. If there is another instance, OpenOtherInstance tells it to open the new file and activates the window (there's an optional argument to prevent activation). My sample code assumes the file name is the first argument on the command line, which MFC parses for you into cmdInfo.m_strFileName.
      I've already shown you how COneInstance uses FindWindow and the ident message to find the other instance of your app, but how does it make this instance open a new file? The obvious thing would be to have another WM_ message and pass the file name as LPARAMâ€"except for just one little problem: you can't pass a pointer from one process to another; it has no meaning in the other address space.
      Fortunately, Windows has just the message for passing a string from one app to another: WM_COPYDATA. You fill out a little struct calledâ€"what else?â€"a COPYDATASTRUCT and pass it as LPARAM. Windows copies your data into the other process's address space and, presto, it can read the string. Once again, COneInstance handles WM_COPYDATA so you don't have to.

LRESULT COneInstance::WindowProc(...)
{
  if (msg==WM_COPYDATA) {
    CString sFileName = 
      // get from COPYDATASTRUCT
    AfxGetApp()->OpenDocumentFile(sFileName);
    return TRUE; // handled
  }
  return CSubclassWnd::WindowProc(...);
}
      This is fine and dandy, but what happens if you're already using WM_COPYDATA for something else? WM_COPYDATA lets you specify a code in the COPYDATASTRUCT. You can use different codes to disambiguate different types of WM_COPYDATA messages. COneInstance has an optional third constructor argument I didn't tell you about, the WM_COPYDATA code to use.
      So far I've been assuming you have a frame-based app. For your dialog-based app there are two other tricks you need to know. First, you need the window class name. Quickâ€"what's the window class name for a dialog? (This would be a good question for the Windows edition of Trivial Pursuit.) Answer: It's #32770. All dialogs have this class name. That's why COneInstance needs the ident messageâ€"to distinguish your dialog from others that could be running.
      Dialog trick number two: you have to override CWinApp:: OpenDocumentFile to open the new file. Normally, this function works only in doc/view apps; if you call it in a dialog-based app, you'll have a major boo-boo. In my sample Dlg1Inst app, I overrode OpenDocumentFile like so:

CDocument* CMyApp::OpenDocumentFile(LPCTSTR lpszFileName)
{
  CString s;
  s.Format("My Dialog: %s",lpszFileName);
  m_pMainWnd->SetWindowText(s);
  return NULL;
}
My implementation just sets the caption to show the name of the currently open file. In a real application, you would actually do something useful.
      Now, there is just one little bug in my implementation of COneInstance as I've presented it. I have no doubt that astute readers have already anticipated the problem. Can you guess? Hint: what if there's some other dialog running that's not an instance of your app? For the answer, you'll have to read the sourceâ€"or download it from the link at the top of this article. Ciao!

Paul DiLascia is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at askpd@pobox.com or http://www.dilascia.com.

From the September 2000 issue of MSDN Magazine.

Page view tracker