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 shows a custom thumbnail image and toolbar buttons down the image to control the track.
Adding these buttons is 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 follow the same order they specified in the array.
- The buttons can’t be removed once they are added, however we can hide/update the buttons.
- The image lists created to specify the bitmaps can be released after 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 a 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
.
The 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 has been 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 receiving 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 the 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 comments 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 handle the icons based on the DPI. It always uses the icon size of default DPI (16pix). You can maintain different bitmaps to handle this. This code in the sample is just a 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 is compiled under Visual Studio 2010.