Fixing an accessibility bug with the trackbar common control

The trackbar common control is a strange beast. 

The trackbar can be oriented either horizontally or vertically.  On LTR language machines, when the trackbar is horizontal, it works much as you’d expect it to: The minimum value of the trackbar is on the left, the maximum value is on the right (it’s reversed for RTL languages so it works consistently).

When the trackbar is vertical, it’s a different beast entirely.  For whatever reason, the trackbar control designers set the minimum value of the trackbar at the top, the maximum value at the bottom.  If you think about how the trackbar designers actually implemented the trackbar this makes sense.  If you orient the trackbar so that the minimum value is at the top, then when you need to draw the trackbar you can use the same drawing code to draw the horizontal trackbar – you just swap the X and Y axis (I have absolutely no idea if that’s how it works internally, it just seems to make sense that way).

While this works great for the implementer, for the consumer of the trackbar, it’s a pain in the neck.  That’s because the users who interact with the control expect the maximum value to be at the top of the control, not the bottom.

As a result, when you look at code that uses the trackbar control, you end up seeing a lot of:

 LRESULT MyDialog::OnNeedTTText(int idCtrl, LPNMHDR pnmh)
{
    LPTOOLTIPTEXT pTT = (LPTOOLTIPTEXT)pnmh;
    if (idCtrl == IDD_TRACKBAR)
    {
        int nPos = (int)SendMessage(TBM_GETPOS, 0, 0);

        StringCchPrintf(pTT->szText, ARRAYSIZE(pTT->szText), TEXT("%d"), 100 - nPos);
    }
    return 0;
}

In other words, retrieve the position from the trackbar, convert it from 0..100 to 100..0 and return that as a tooltip text.

All of this works great – you have a lot of 100 - <n> scattered throughout your code, but it’s not the end of the world.  And then one day a tester comes to you and says that when he uses the narrator tool to read the contents of your tool, it reports that the value reported by the control is wrong – when the slider’s at the top (tooltip 100), it reports that the value is 0, when it’s at the 1/4 point (tooltip 75), it reports that the value is 25.

Crud.  At this point many developers start scratching their heads and start thinking about subclassing the trackbar to replace the reported position.  However that’s way more work than they need to do.

It turns out that the designers of the trackbar control thought of this problem.  If you’re using version 5.8 or higher of the common controls, you can specify the TBS_REVERSED trackbar control style to your trackbar.  If you do, the visuals of the trackbar are unchanged as is the trackbar functionality, but the Microsoft accessibility framework will look for the presence of the TBS_REVERSED style and if it is found, it assumes that the control is “backwards” and it reports the position for the toolbar as if the maximum and minimum values were reversed.

And no, I didn’t know about this before today.  But it was too good a trick not to share.