What’s New in MFC 9.0 (Orcas): Command Link Buttons

What Are Command Links?

Command Links are a new thing in Vista. If you aren’t familiar with them, they look like the figure below. You can find additional information here.

A Command Link essentially has two parts: the main text and a note, as shown below.

A Command Link, as a matter of fact, is not a new class of control . It is just a new style for the BUTTON class. One can make a commonly used button control look like a command link by adding the BS_COMMANDLINK style. The Vista UI guidelines suggest one to use Command Links as a means of conveying additional information related to the specific commands.

MFC Enhancements for Command Links

In Visual Studio 2008 (beta 2 is available for download now), MFC has been enhanced to accomodate the new Vista features. The MFC classes are now in sync with Vista’s demands. Thus, the following member functions have been added to CButton class w.r.t. Command Links:

  • CButton::GetNote
  • CButton::GetNoteLength
  • CButton::SetNote

These probably are self explanatory for those familiar with MFC’s function naming convention. In addition, the resource editor toobox has been enhanced to reflect the new controls. See below:

The Problem

Note: Before proceeding, make sure you have downloaded and installed Visual Studio 2008 beta 2, if you have not done so already.

Application developers often write applications that are targeted for multiple operating systems. In typical deployment scenarios, it is not uncommon to have a single binary targeting multiple versions of the operating systems. This situation demands that, as an application developer, one tries to cut down on features that aren’t commonly available on all of them, or, if it all possible, one will attempt to have some workaround to make them functionally identical even though, under the hood, there will be platform-specific code dealing with all those limitations.

This practise becomes even more challenging on the user interface side, wherein applications are required to have the best UI, and a common UI across all platforms because it directly relates to how the user interacts with the software itself. Any differences in the look and feel or behavior can lead to slow adoption of the software by customers, increased customer support calls, and so forth.

Often times, most software applications also ship with offline user guides. This means a uniform look and feel is a must; otherwise, it will have an impact on maintainence of different versions of the guides catering to different scenarios.

How does it all relate to Command Links? Simple. Command Links aren’t available on platforms earlier than Vista. So, how does one go about having an application that is more user-friendly with command links, but one that also has a similar functionality on pre-Vista platforms?

The problem will be even more evident if one creates a sample MFC dialog project with Visual Studio 2008 (uses MFC 9.0), drops a command link on the dialog resource, builds, and runs it. If your development environment is pre-Vista, you will see that the Command Link is simply not visible. It does appear when you run the same application on Vista.

The Workaround

I call this a workaround, because it is no more than that. It is not a complete solution. A complete solution would mean identical behavior on pre-Vista, but that is like rewriting the control itself. The workaround discussed enables the software to use the command links that come with Vista, but degrades gracefully to provide, if not the same experience, a similar experience on pre-Vista platforms without going overboard trying to do so.

The problem discussed in the earlier section was that, on pre-Vista platforms, the common control DLL cannot interpret the BS_COMMANDLINK style. It doesn’t seem that Microsoft has any intention on making them available on newer commctrl.dll for pre-Vista platforms anyway. However, the Command Link button is a pretty simple one. Perhaps there is a way to get at least a similar functionality on pre-Vista platforms?

Let me dig a little deeper. Okay, you know that the BS_COMMANDLINK style doesn’t help much. What if you leave the style as is on Vista platforms, but remove it for non-Vista platforms? That should work, because then it is a regular button control.

There are a couple of ways one can do this:

  • Let the application remove the style on its own by using button.ModifyStyle(BS_COMMANDLINK,0);.
  • Subclass the button control the MFC way and modify it from within the class. What I am getting at it is, what if you write your own class, say CCGCommandLinkButton, derived from CButton and handle it all within this class? The usage then becomes easy. All the users will have to do is associate the command link resources in the template with CCGCommandLinkButton objects.

It’s time to get started. If you do not have the project yet, crank up a dialog-based MFC project from within VS2008, accepting all defaults. Drop a “Command Button control” onto the dialog. Build and run. For the purpose of this article, you will use a pre-Vista platform for testing (because on Vista platforms, it should just work). You will see a dialog with OK and Cancel, but no Command Link button.

  1. Close the dialog resource if open. Go to menu Project->”Add Class”. Select “MFC” and on the right hand side, select “MFC Class”. Click on “Add”. Give the class name as “CCGCommandLinkButton”. Change the base class from CWnd to CButton. Click “Finish”.
  2. Go to resource view. Open the dialog resource. Click on the command button to select it, right-click on it, and select “Add Variable”. Set the variable name as, say, m_btnCommand. Select “Finish”.
  3. Open the dialog class’ header file. At the top, #include “CGCommandLinkButton.h” and change the declaration of m_btnCommand from CButton to CCGCommandLinkButton.
  4. Build and run. You won’t see the command link button yet.
  5. The next step is to remove the BS_COMMANDLINK style on non-Vista platforms. For the sake of simplicity, add a BOOL private variable to the CCGCommandLinkButton class, called m_bPreVista. In the constructor, set it to TRUE. Later, you will replace this hard setting by the proper value by querying the Windows Version.
       m_bPreVista = TRUE;
  6. Open the CCGCommandLinkButton.h file. Make sure you are within the class scope in the header file. You should now see the Properties panel with a button for “Overrides”. Select this and, from the list, select PreCreateWindow and drop down and add PreCreateWindow. Similarly, add PreSubclassWindow.
  7. PreCreateWindow is a virtual function called as a part of CWnd::Create/Ex function before the window is actually created. It gives you a chance to modify the CREATESTRUCT. You tap into this feature to remove the BS_COMMANDLINK style if one were to use Create/Ex functions to create the button.

    Add the following code:

    BOOL CCGCommandLinkButton::PreCreateWindow(CREATESTRUCT& cs)
       // TODO: Add your specialized code here and/or call the
       //       base class
       if(TRUE == m_bPreVista)
          //remove the BS_COMMANDLINK style because it doesn't
          //apply to preVista platforms
          cs.style &= (~BS_COMMANDLINK);
       return CButton::PreCreateWindow(cs);
  8. PresubclassWindow is a virtual function called as a part of MFC’s DoDataExchange to subclass the actual control the first time. It gives you a chance to modify any styles after window creation. This is useful in the dialog template scenario where it is too late to influence the creation procedure. You tap into this feature to remove the BS_COMMANDLINK style again.

    Add the following code:

    void CCGCommandLinkButton::PreSubclassWindow()
       // TODO: Add your specialized code here and/or call the
       //       base class
       if(TRUE == m_bPreVista)
          //remove the BS_COMMANDLINK style because it doesn't
          //apply to preVista platforms
  9. The above code snippets do something simple. They remove the BS_COMMANDLINK style on pre-Vista platforms because they don’t work. Build and run. You should “see” the button now.
  10. Now, open the dialog class implementation file and add code to set the note information like below to the end of OnInitDialog:
    // TODO: Add extra initialization here
    m_btnCommand.SetNote(_T("This is a test note"));
    return TRUE;    // return TRUE unless you set the focus to
                    // a control

    Build and run. You will see that this doesn’t change anything. The text doesn’t appear anywhere. This means is that the note is not part of the window text at all.

  11. Now, your task is to make the button at least look like a command link. That is, you need to show the button text, and beneath it, the note. There are couple of ways you can achieve this.
    • You could combine the note and actual window text as one, with a newline in between, and set this combined text as the window text. However, this won’t work from the experiment you just did in that doing so alters the window text. This is not desirable.
    • The other approach is to maintain the two texts independently, but draw them on the button yourself. This means you need an owner drawn button (BS_OWNERDRAW style). This is doable. For one, people using Command Link buttons will most likely not be using the BS_OWNERDRAW style. If one were using BS_OWNERDRAW to do custom drawing, why use the Command Link style anyway? So, take this approach.

More by Author

Must Read