In the previous post, we’ve discussed about progressbar overlay on taskbar button. In this installment, we will see how to add the toolbar buttons to the flyout thumbnail Window. The best example is Windows Media Player. You can control the tracks using the flyout window on hovering the thumbnail button.

As you can see above, the thumbnail popping up is not really displaying the actual content of the window( which is by default). It show a custom thumbnail image and toolbar buttons down the image to control the track.
Adding these buttons are quite easy.
There are few peculiarities for the buttons displayed as toolbar in thumbnail area.
- A maximum of 7 buttons can be added to thumbnail
- The size of the bitmaps specified for thumbnails is dependent on the current DPI. Usually we use the 16×16 image for default DPI( 96 ). The bitmap size can be calculated easily by calling GetSystemMetrics( SM_CXSIZE ) and GetSystemMetrics( SM_CYSIZE ). This has to be taken care if the application is DPI aware.
- The buttons follows the same order they specified in the array.
- The buttons can’t be removed once it’s added, however we can hide/update the buttons.
- The image lists created to specify the bitmaps can be released after calling adding it to the thumbnail area. The ITaskbarList3 interface can also be released if we’re finished processing.
- The most important thing is, Every application must handle the “TaskbarButtonCreated” message before using the ITaskList3 interface functions. Otherwise the application may face exceptions. Especially when the buttons and other overlays are initialized on application initialization.
- The button effect on disabling, enabling, hovering is controlled by shell itself. We don’t need to keep separate image list for this.
Handling TaskbarButtonCreated message in MFC Application
As I mentioned before, the first task is to handle the “TaskbarButtonCreated” message in the application for proper initialization of ITaskbarList3 interface.
Following code will help you to do this. You can initialize the variable globally or inside the constructor of the class.
UINT g_uTBBC = RegisterWindowMessage(L"TaskbarButtonCreated");
Once this parameter initialized, handle the message inside the message map of the dialog class. Note that you should not use ON_MESSAGE to handle the message. ON_REGISTERED_MESSAGE function must be used.
BEGIN_MESSAGE_MAP(CTaskBarSampleDlg, CDialogEx) ...
...
ON_REGISTERED_MESSAGE( g_uTBBC, CTaskBarSampleDlg::OnCreateThumbToolBar )
...
END_MESSAGE_MAP()
Proceed to initialize ITaskbarList3 after receving notification in the message handler.
LRESULT CTaskBarSampleDlg::OnCreateThumbToolBar( WPARAM, LPARAM )
{ AddThumbarButtons(); return 0; }
Initializing and Adding Buttons
Now let’s see the initialization process. This can be accomplished in following steps
- Initialize the ITaskbarList3 interface
- Create Image list to specify as the bitmap buttons
- Add image list to the interface
- Prepare the button information as array
- Add the buttons
I’ve added enough comment in the source, I hope it’s easy to understand.
void CTaskBarSampleDlg::AddThumbarButtons()
{
if( NULL == m_pTaskBarlist )
{
CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_ALL,
IID_ITaskbarList3, (void**)&m_pTaskBarlist);
}
m_pTaskBarlist->HrInit();
THUMBBUTTONMASK dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS;
CImageList ImageList;
ImageList.Create( MAKEINTRESOURCE( IDB_BITMAP_PAUSE), 16, 2, RGB( 0xFF,0,0xFF));
THUMBBUTTON thbButtons[2];
thbButtons[0].dwMask = dwMask;
thbButtons[0].iId = IDB_THB_BUTTON_START;
thbButtons[0].iBitmap = 0;
lstrcpy( thbButtons[0].szTip, TEXT("Start"));
thbButtons[0].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
thbButtons[1].dwMask = dwMask;
thbButtons[1].iId = IDB_THB_BUTTON_PAUSE;
thbButtons[1].iBitmap = 1;
thbButtons[1].dwFlags = THBF_DISABLED | THBF_DISMISSONCLICK;
lstrcpy( thbButtons[1].szTip, TEXT("Pause"));
m_pTaskBarlist->ThumbBarSetImageList(m_hWnd, ImageList.GetSafeHandle());
m_pTaskBarlist->ThumbBarAddButtons(m_hWnd, ARRAYSIZE(thbButtons), thbButtons);
}
Handling the Events from Buttons
The events from buttons will be delivered as WM_COMMAND message for Window. As we’re using MFC, please override OnCommand function. The button ID can be extracted from wParam using LOWORD macro.
BOOL CTaskBarSampleDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
UINT CommandID = LOWORD( wParam );
if( IDB_THB_BUTTON_START == CommandID )
OnBnClickedButton1();
else if( IDB_THB_BUTTON_PAUSE == CommandID )
ResetProgress();
else
return CDialogEx::OnCommand(wParam, lParam);
}
Updating Buttons
We’ve received the button event, so it’s time to update the button. We can’t remove the buttons as I specified before, we can hide, disable, enable the state of the buttons. Other properties like bitmaps and tooltip can also be updated
void CTaskBarSampleDlg::ResetProgress()
{
m_pTaskBarlist->SetProgressState( m_hWnd, TBPF_NOPROGRESS );
THUMBBUTTONMASK dwMask = THB_FLAGS;
THUMBBUTTON thbButtons[2];
thbButtons[0].dwMask = dwMask;
thbButtons[0].iId = IDB_THB_BUTTON_START;
thbButtons[0].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
thbButtons[1].dwMask = dwMask;
thbButtons[1].iId = IDB_THB_BUTTON_PAUSE;
thbButtons[1].dwFlags = THBF_DISABLED| THBF_DISMISSONCLICK;
m_pTaskBarlist->ThumbBarUpdateButtons( m_hWnd, ARRAYSIZE(thbButtons), thbButtons );
}
Note that the code specified above doesn’t not handling the icons based on the DPI. It always use the icon size of default DPI (16pix). You can maintain different bitmaps to handle this. This code in the sample is just mere demonstration of this API. It may not have a professional standard.
Final Output
To get the icons, you can locate this down in the Visual Studio Installation directory. Usually it appears at
C:\Program Files\Microsoft Visual Studio 10.0\Common7\VS2010ImageLibrary\1033\VS2010ImageLibrary\VS2010ImageLibrary
Note: You’ve the sole responsibility of downloading executing the code. It works only with Windows 7 and compiled under Visual Studio 2010