Window 7: Setting custom Thumbnail clip

 

In the previous posts I covered the basics of setting progress overlay and adding buttons to the thumbnail flyout area.

All thumbnail previews displays the real-time content of the window client area. Suppose if we’re watching movies, we can see it through thumbnails. If we’re listening music, we can see the album artwork on hovering the thumbnail. But we can customize the window area to display in the thumbnail area.

Let’s take the previous example. by default Windows will display the entire window area.

image

Let’s take an example of clipping the preview to the scrollbar.

image

What we can do, it’s quite simple and straight forward. Just set the offset from the client area to be clipped. See the example below. Setting a NULL area will reset the thumbnail preview to default.

BEGIN_MESSAGE_MAP(CTaskBarSampleDlg, CDialogEx)
	ON_REGISTERED_MESSAGE( g_uTBBC, CTaskBarSampleDlg::OnCreateThumbToolBar )
END_MESSAGE_MAP()

LRESULT CTaskBarSampleDlg::OnCreateThumbToolBar( WPARAM, LPARAM )
{
	// Initialize the pointer. You can also do this in the constructor.
	// Remember to release after use
	if( NULL == m_pTaskBarlist )
	{
		CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_ALL,
						  IID_ITaskbarList3, (void**)&m_pTaskBarlist);
	}
	return 0;
}

void CTaskBarSampleDlg::OnBnClickedButton2()
{
	static bool bClip = true;
	if( bClip )
	{
		CRect rect;
		m_Progress.GetWindowRect( rect );
		ScreenToClient( rect );
		m_pTaskBarlist->SetThumbnailClip( m_hWnd,rect );

	}
	else
		m_pTaskBarlist->SetThumbnailClip( m_hWnd, NULL ); // NULL will reset to default

	bClip = !bClip;
}
 

Windows 7: Adding toolbar buttons to taskbar button flyout

 

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.

image

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.

  1. A maximum of 7 buttons can be added to thumbnail
  2. 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.
  3. The buttons follows the same order they specified in the array.
  4. The buttons can’t be removed once it’s added, however we can hide/update the buttons.
  5. 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.
  6. 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.
  7. 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()
{
        // Initialize the pointer. You can also do this in the constructor.
    // Remember to release after use
    if( NULL == m_pTaskBarlist )
    {
        CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_ALL,
                          IID_ITaskbarList3, (void**)&m_pTaskBarlist);
    }

    m_pTaskBarlist->HrInit();

    // We mention that, we're specifying bitmap, tooltpi and other flags.
    // This is mask, only specified parameters will be taken from structure
    THUMBBUTTONMASK dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS;

    // Create the images from a single bitmap which contains
    // buttons seperated 16pix width. Like we handle toolbar buttons
    CImageList  ImageList;
    ImageList.Create( MAKEINTRESOURCE( IDB_BITMAP_PAUSE), 16, 2, RGB( 0xFF,0,0xFF));

    // Prepare the thumbar buttons
    THUMBBUTTON thbButtons[2];
    thbButtons[0].dwMask = dwMask;
    // This can be any integer required on handling button events
    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"));

    // Declare the image list that contains the button images.
    m_pTaskBarlist->ThumbBarSetImageList(m_hWnd, ImageList.GetSafeHandle());

    // Attach the toolbar to the thumbnail.
    m_pTaskBarlist->ThumbBarAddButtons(m_hWnd, ARRAYSIZE(thbButtons), thbButtons);

    // In this program we're using the interface later, so not releasing now.
    // It's Ok to release if finished using. We can create again when necessary
}

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()
{
    // reset the progress state
    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

image 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

Download Source[VS2010] – TaskbarSample.rar

Note: You’ve the sole responsibility of downloading executing the code. It works only with Windows 7 and compiled under Visual Studio 2010

 

Windows 7: How to display progress bar on taskbar icon?

 

The best thing you may notice in Windows 7 may be its taskbar. It’s totally revamped and gives us a fresh look, more usable with progress bar overlays, jump lists to easily open the last opened documents, and improved and interactive thumbnail previews applications etc. You can see more details in Windows 7 website.

In this post, I am covering progress bar to display as taskbar button overlay. In Windows 7, you might have noticed the progress bar on taskbar button in many applications like Windows Explorer shows progress of file copy, Internet Explorer or Safari displays progress of downloads, etc.

We can program this for our application as well. It can be easily accomplished using the ITaskList3 COM Interface. This Interface not only for progress bars. It can be used for:

  • When working with a TDI (Tabbed Document Interface) application (such as Internet Explorer) or a MDI application (such as Microsoft Excel) that is displaying its windows as a group on the taskbar:
    • Provide the taskbar with a thumbnail that represents the view of an individual tab or document. (You can see this in action with Safari Or Internet Explorer Browser)
    • Remove the thumbnail of an individual tab or document from the group.
    • Change the order of thumbnails in the group.
    • Set a tab thumbnail as the selected item when the thumbnails are shown.
  • When applying an overlay to a taskbar icon, such as a notification.
    • When showing the progress of an operation, such as copying or installing an item.
    • When adding a toolbar to a thumbnail. (like iTunes of Windows Media Player)

Now let’s see how we can do this. I’ve prepared a simple dialog based application which shows a progressbar as below. You can also see the progressbar over the Icon of application displayed in the thumbnail area.

image

image

The code is self explanatory. See the code snippet and comments. Don’t forget to release the ITaskListInterface after usage. You should call m_pTaskList>Release(); to release the interface.

Step #1

Declare the pointer to access ITaskbarList3 interface. You can make this as a member of the dialog class.

ITaskbarList3* m_pTaskBarlist;
CoCreateInstance(
            CLSID_TaskbarList, NULL, CLSCTX_ALL,
            IID_ITaskbarList3, (void**)&m_pTaskBarlist);

Step #2

Initialize the progressbar state to enable the progressbar overlay:

m_pTaskBarlist->SetProgressState( m_hWnd, TBPF_INDETERMINATE );

The parameters are relatively simple. Windows handle must be passed along with the type of progressbar that needs to be displayed. If you check the definition, you can see the following types can be passed to the function. These states are exactly similar state of a normal progressbar.

TBPF_NOPROGRESS This flag disables the progressbar overlay and displays normal taskbar button
TBPF_INDETERMINATE Instead of displaying the constant progress, it displays horizontal marquee
TBPF_NORMAL Normal progressbar
TBPF_ERROR Progressbar with an red colored overlay (based on the theme)
TBPF_PAUSED Paused state. In this state, you can see a flashing horizontal on progressbars

Step #3 Progress

Simply call SetProgressValue() as given below. The current value and maximum value should be given.

m_pTaskBarlist->SetProgressValue( m_hWnd, nPos, nMax );

Once you finish the task, you’ve to disable the progress state. I hope you know how to do it :)

See the full scoop here.

To compile this code, either use Visual Studio 2010 or download the Windows 7 SDK from MSDN website.

// Declare this as member of the class or global (not recommended)
ITaskbarList3* m_pTaskBarlist;
void CTaskBarSampleDlg::OnBnClickedButtonStart()
{
	m_Progress.SetRange( 0, 10 ); // set the range for the control
	m_Progress.SetPos( 0 ); // Set initial position
	// Initialize the pointer. You can also do this in the constructor.
	// Remember to release after use
	if( NULL == m_pTaskBarlist )
	{
		CoCreateInstance(
			CLSID_TaskbarList, NULL, CLSCTX_ALL,
			IID_ITaskbarList3, (void**)&m_pTaskBarlist);
	}
	m_pTaskBarlist->SetProgressState( m_hWnd, TBPF_INDETERMINATE );
	SetTimer( 0, 500, 0 );
}
// Timer to update the progress
void CTaskBarSampleDlg::OnTimer(UINT_PTR nIDEvent)
{
	int nPos = m_Progress.GetPos();
	int nMin, nMax;
	m_Progress.GetRange( nMin, nMax );
	nPos++;
	// if finished, kill timer and return
	if( nPos > nMax )
	{
		// reset the progress state
		m_pTaskBarlist->SetProgressState( m_hWnd, TBPF_NOPROGRESS );
		// just flash the window to notify user
		FlashWindow( true );
		// Stop the timer
		KillTimer( 0 );
		// You can release the pointer if necessary here.
                  // But not a good practice
		return;
	}
	// set progress to taskbar overlay
	m_pTaskBarlist->SetProgressValue( m_hWnd, nPos, nMax );
	// set the control's progress state
	m_Progress.SetPos( nPos );
}