What Are Command Links?

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

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.

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".

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".

Open the dialog class' header file. At the top, #include "CGCommandLinkButton.h" and change the declaration of m_btnCommand from CButton to CCGCommandLinkButton.

Build and run. You won't see the command link button yet.

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.

CCGCommandLinkButton::CCGCommandLinkButton()
{
m_bPreVista = TRUE;
}

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.

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.

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.

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.

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.

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.

Adding BS_OWNERDRAW style means that you have to draw the button yourself. Traditionally, this is done by the parent window. But, in MFC, you can make this a self-contained functionality of a class by overriding DrawItem. So, like you added PreCreateWindow, go ahead and add the DrawItem override to the CCGCommandLinkButton class.

Add a simple drawing code like below. It simply draws the window text.

How do you get the note text now? If you thought GetNote() were an answer, you will see that it won't work because pre-Vista platforms aren't aware of it at all. To get the note, this is what you have to do.

Realize that calling SetNote(sometext), simply is a call to SendMessage BCM_SETNOTE passing the text as lParam. This is a good hint. All you need to do now is to handle this message in the CCGCommandLinkButton class and cache it within a CString member variable for later use. You do this only for preVista. For Vista and above, you let the default go through.

Again, open the CCGCommandLinkButton.h file. Add protected function like below:

afx_msg LRESULT OnSetNote(WPARAM wParam, LPARAM lParam);

Add a private CString variable called m_szNote;. Open CCGCommandLinkButton.cpp. Add a message map entry like below:

That takes care of caching the note information. You now need to udpate your implementation of DrawItem to reflect the note. A simple implementation would be to draw the text below the window text. You will do a little more than that in the code snippet below to make the window text bold font and the note in normal font. The code below is a simple code to do all that.

Build and run. You should see the button and the note below it now in the proper fonts.

That takes care of the SetNote call. You now have two more calls to take care of, GetNote and GetNoteLength. Now that you have the note cached, this shouldn't be a tough job as long as you handle the proper messages.

What is now left is to make the m_bPreVista variable be configured at runtime. You use the GetVersionEx API and check for the major version. For Vista, the major version is 6. The code for the CCGCommandLinkButton constructor becomes something like this:

Conclusion

What we just did is by no means a complete solution (it doesn't handle different appearances of the button in different states, and it doesn't handle themes). The source code in the downloads section is a better one, although it doesn't handle themes. My goal for this article was to enable writing common code for all OSes without sacrificing on some of the newer features. The command link button possibly serves as a useful addition and one that probably would be used much more meaningfully in place of checkboxes and radio buttons. I hope this small class goes some way to achieving that.

There are some articles that probably already do the theme handling and I have included one such article in the references below. This could be used to enhance the look and feel. But, personally, I wouldn't go to extra lengths in doing that because this is just a stop-gap measure until Vista becomes a more regularly used platform.

Top White Papers and Webcasts

Live Event Date: March 19, 2015 @ 1:00 p.m. ET / 10:00 a.m. PT
The 2015 Enterprise Mobile Application Survey asked 250 mobility professionals what their biggest mobile challenges are, how many employees they are equipping with mobile apps, and their methods for driving value with mobility.
Join Dan Woods, Editor and CTO of CITO Research, and Alan Murray, SVP of Products at Apperian, as they break down the results of this survey and discuss how enterprises are using mobile application management and private …

Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …