New
#1
Already started adding Windows 7 features to my apps :) Jump List:
Twitpic - Share photos on Twitter
Not added thumbnail toolbars yet, but my current project doesn't need them.
More...It is time to return to our usual routine of describing Windows 7 APIs. We already covered the basics of the Windows 7 Taskbar in Developing for the Windows 7 Taskbar – Application ID, and how you can create a Jump List for your application in Developing for the Windows 7 Taskbar – Jump into Jump Lists – Part 1, Part 2, and Part 3). This post focuses on using thumbnail toolbars to enhance end user productivity while surfacing specifics common to the Thumbnail Preview.
To make sure everyone understands the concepts, let’s dive into a quick review. If you are already familiar with what thumbnail toolbars are, then feel free to jump towards the middle of this post – where the fun starts with some real code.
As we said, thumbnail toolbars are an amazing productivity feature that enables users to access the application’s functionality even when the application is not in focus (or even visible). Specifically, this enables you to access to the application without the need to  switch to it (make it the active window.) Thumbnail toolbars are a set of up to seven buttons at the bottom of the taskbar’s icon thumbnail preview. A good, well known example is the Windows Media Player thumbnail toolbar as shown in the following image. Here you can see that when your hover your mouse pointer over the Media Player icon while the application is running, three buttons display: Play / Pause, Next song, and Previous song. Clicking on each button will trigger the expected result as suggested by the button name.
Another great example we mentioned few weeks ago is the Fishbowl for Facebook application. This application uses the thumbnail preview to provide quick and easy access to the most common task any Facebook user performs, surfacing actions like view your home's or friend’s feeds to the thumbnail toolbar. As you can see in the following image, the Fishbowl thumbnail toolbar has five buttons (actually six, but we’ll get to this in a second.)
Now that we established the need for using this cool taskbar feature, let’ see what it takes to implement it. First, few ground rules:
As usual, we’ll start with native code (Win32) implementation. You can find the following example in the Windows 7 SDK.
- You can display up to seven buttons with a fix and predefined size of 16x16 pixels (or up to 24x14 pixels for high DPI)
- You can set a thumbnail toolbar button to each active window in your application (that assumes you have multiple windows clustered together and you want each to show different thumbnail toolbar
- Once the buttons are set, usually at the time the taskbar icon is constructed, you can’t add or delete buttons from the toolbar; however, if you want to, you can hide the buttons
To add a thumbnail toolbar button you need to use the ITaskbarList4 Interface that exposes methods that allow you to dynamically add, remove, and activate items on the taskbar. Specifically, for working with the thumbnail toolbar you need to use the following methods:
Like every Taskbar icon-related operation, you need to make sure you have a Taskbar icon to work with; to ensure that you have one, you need to register the “TaskbarCreatedMessage” using the RegisterWindowMessage method. Then you need to wait for the message to arrive to your Win32 WndProc method. Once your application receives this message, you are safe to go ahead and create the thumbnail toolbar buttons. The same WndProc method will also receive the WM_COMMAND message when any of the thumbnail buttons you created is clicked. The lower word of the wParam parameter holds the specific button id, signifying which button was pushed, while the upper word is just set to THBN_CLICKED. The following code snippet illustrates this process
- ThumbBarAddButtons - Adds a thumbnail toolbar (a toolbar embedded in a thumbnail image of a window in a taskbar button flyout) with a specified set of buttons to the thumbnail image of a window in a taskbar button flyout
- ThumbBarSetImageList - Specifies an image list that contains button images for a toolbar embedded in a thumbnail image of a window in a taskbar button flyout
- ThumbBarUpdateButtons - Shows, enables, disables, or hides buttons in a thumbnail toolbar as required by the window's current state
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static UINT s_uTBBC = WM_NULL;   if (s_uTBBC == WM_NULL) { // Compute the value for the TaskbarButtonCreated message s_uTBBC = RegisterWindowMessage(L"TaskbarButtonCreated");   // In case the application is run elevated, allow the // TaskbarButtonCreated and WM_COMMAND messages through. ChangeWindowMessageFilter(s_uTBBC, MSGFLT_ADD); ChangeWindowMessageFilter(WM_COMMAND, MSGFLT_ADD); }   if (message == s_uTBBC) //wmTaskbarButtonCreated) { // Once we get the TaskbarButtonCreated message, we can create // our window's thumbnail toolbar. CreateThumbnailToolbar(hWnd); } else switch (message) { case WM_COMMAND: { int const wmId = LOWORD(wParam); switch (wmId) { case IDTB_BUTTON1: case IDTB_BUTTON2: case IDTB_BUTTON3: { WCHAR szMsg[100]; StringCchPrintf( szMsg, ARRAYSIZE(szMsg), L"Thumbnail toolbar button clicked, ID=%d", wmId);   MessageBox(hWnd, szMsg, L"Application", MB_OK); break; } case IDM_EXIT: DestroyWindow(hWnd); break;   default: return DefWindowProc(hWnd, message, wParam, lParam); } break; } case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
Note that right after you register to listen for the TaskbarCreatedMessage message, you call the ChangeWindowMessageFilter method. This method adds or removes a message from the User Interface Privilege Isolation (UIPI) message filter. You need to do so in case you are running elevated application. In short, the Taskbar is part of the explorer.exe process, which runs in medium integrity level – that is, as a standard user. Since Windows Vista and the introduction of UIPI, lower level privileged applications can’t send Windows messages to high level applications. Therefore, you need to change the filter to allow specific messages to be received from a lower integrity sender, specifically the result of the TaskbarCreatedMessage and WM_COMMAND. This looks like a topic for another post just about UIPI, which I’ll write sometime soon.
Back to our topic, you can see that we handle the WM_COMMAND in a very simple way, extracting the specific button ID from the lower word of the wParam parameter. We don’t really do anything with it (the switch is the same for all buttons); it is just an example.
Next let’s describe what it takes to register the thumbnail toolbar. In the above code snippet, we call the CreateThumbnailToolbar helper method. The following is the relevant code snippet.
HRESULT CreateThumbnailToolbar(HWND hWnd) { ITaskbarList4 *pTaskbarList; HRESULT hr = CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pTaskbarList));   if (SUCCEEDED(hr)) { hr = pTaskbarList->HrInit(); if (SUCCEEDED(hr)) { // Figure out what bitmap to use for the // thumbnail toolbar buttons - we decision // based on the system's small icon width. // This will make us DPI-friendly. struct { PCWSTR pbmp; int cx; } const bitmaps[3] = { { MAKEINTRESOURCE(IDB_BUTTONIMAGES_96), 16 }, { MAKEINTRESOURCE(IDB_BUTTONIMAGES_120), 20 }, { MAKEINTRESOURCE(IDB_BUTTONIMAGES_144), 24 } };   int const cxButton = GetSystemMetrics(SM_CXSMICON);   int iButtons = 0; for (int i = 0; i < ARRAYSIZE(bitmaps); i++) { if (bitmaps[i].cx iButtons = i; } }   HIMAGELIST himl = ImageList_LoadImage( g_hInstance, bitmaps[iButtons].pbmp, bitmaps[iButtons].cx, 0, RGB(255,0,255), IMAGE_BITMAP, LR_CREATEDIBSECTION);   if (himl) { hr = pTaskbarList->ThumbBarSetImageList(hWnd, himl); if (SUCCEEDED(hr)) { THUMBBUTTON buttons[3] = {};   // First button buttons[0].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS; buttons[0].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK; buttons[0].iId = IDTB_BUTTON1; buttons[0].iBitmap = 0; StringCchCopy(buttons[0].szTip, ARRAYSIZE(buttons[0].szTip), L"Button 1");   // Second button buttons[1].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS; buttons[1].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK; buttons[1].iId = IDTB_BUTTON2; buttons[1].iBitmap = 1; StringCchCopy(buttons[1].szTip, ARRAYSIZE(buttons[1].szTip), L"Button 2");   // Third button buttons[2].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS; buttons[2].dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK; buttons[2].iId = IDTB_BUTTON3; buttons[2].iBitmap = 2; StringCchCopy(buttons[2].szTip, ARRAYSIZE(buttons[2].szTip), L"Button 3");   // Set the buttons to be the thumbnail toolbar hr = pTaskbarList->ThumbBarAddButtons( hWnd, ARRAYSIZE(buttons), buttons); } ImageList_Destroy(himl); } } // It's OK to release ITaskbarList3 here; // the thumbnail toolbar will remain. pTaskbarList->Release(); } return hr; }
First, you need to Co-Create the ITasbarList4 COM interface. Next as standard practice, you need to call HrInit to initialize the Taskbar list object. This method must be called before any other ITaskbarList methods can be called. Next, set up a bitmap list to work with. Note that you need to check the recommended size of the width (in pixels) of a small icon: your DPI setting can affect this. Finally, call ThumbBarSetImageList to set the icons of the buttons. When you download the source code, try to comment this line and run the app. What do you think happens?
Next, we create an array of three THUMBBUTTONs and for each button we set the bitmap, tooltip, and flags. This is used to configure the toolbar button. For example, the bitmap value indicates that the value of the iBitmap field (part of THUMBBUTTON) is valid. The THB_FLAGS is more interesting as you can use it to specify the button's states and behaviors, like enable, disable, or hidden. You can find more options in the SDK. We also set the button ID to distinguish between each button once it is clicked; this ID is sent with the WM_COMMAND message.
Last, we call the ThumbBarAddButtons method to pass the current hWnd and the array of buttons. This registers the thumbnail toolbar so the next time you hover your mouse pointer above your application Taskbar icon, you will see three colored buttons as shown in the following image. You can use the Windows API Code Pack to create thumbnail toolbars for managed code applications. The following is part of the Windows 7 Training Kit that is available on the Channel 9 Learning Center. The Taskbar Concepts demo, created by Jaime Rodriguez, is a WPF application that showcases EVERY little feature the Windows Taskbar has to offer developers. We’ll explore just the thumbnail toolbar part, and I will leave the rest for you to explore on your own. From time to time, we’ll use this as base to our managed code samples for WPF applications.
As always while using the Windows API Code Pack, you don’t have to worry about registering or handling any Windows messages. Just use the TaskbarManager. Specifically, you want to work with ThumbnailToolbars, which has an AddButtons method that you need to use while adding ThumbnailToolbarButton objects. The ThumbnailToolbarButton represents a taskbar thumbnail button through which you can enable or disable the taskbar button, change its icon or tooltip, and control its visibility. You can also attach an event handler to the button Click event, as you would expect from managed code (note: there is no need to handle funky Windows Messages). You can find the following code snippet in the ToolbarButtons.xaml.cs file. It shows how easy it is to add thumbnail toolbars to your managed code application
private void CreateToolbarButtons() { buttonFirst = new ThumbnailToolbarButton( TaskbarConcepts.Resources.first, "First Image"); buttonFirst.Enabled = false; buttonFirst.Click += buttonFirst_Click;   buttonPrevious = new ThumbnailToolbarButton( TaskbarConcepts.Resources.prevArrow, "Previous Image"); buttonPrevious.Enabled = false; buttonPrevious.Click += buttonPrevious_Click;   buttonNext = new ThumbnailToolbarButton( TaskbarConcepts.Resources.nextArrow, "Next Image"); buttonNext.Click += buttonNext_Click;   buttonLast = new ThumbnailToolbarButton( TaskbarConcepts.Resources.last, "Last Image"); buttonLast.Click += buttonLast_Click;   TaskbarManager.Instance.ThumbnailToolbars.AddButtons ( new WindowInteropHelper(Application.Current.MainWindow).Handle, buttonFirst, buttonPrevious, buttonNext, buttonLast ); }
The rest of the code in this file is responsible for the logic and handling of different events. This results in the ability to control the different images displayed in the main application as you can see in the following image.
I hope by now you see the value of including thumbnail toolbar (where appropriate), and how easy it is to implement.
You can download the Win32 Native sample. 
You can download the managed code (WPF) sample
You can find more Windows 7 Taskbar training at the Channel 9 Learning Center
Already started adding Windows 7 features to my apps :) Jump List:
Twitpic - Share photos on Twitter
Not added thumbnail toolbars yet, but my current project doesn't need them.