(Print this page)

Windows API Code Pack for Microsoft .Net Framework – Part 2
Published date: Sunday, November 8, 2009
On: Moer and Éric Moreau's web site

As promised last month, here is the second (and last) part about the Windows API Code Pack. If you missed the first part (or you need a refresher), remember that you can access it from here.

This month’s article explores features that weren’t covered in the previous article (of course). While many features explored last month were working on Windows Vista as well as Windows 7, it is not the case with the features explored this month as they are all specific to Windows 7.

The features that we will explore here are not mandatory when you create your applications but as users will start using Windows 7 and applications designed to run on Windows 7, they will start to get use to those features and really love them. Shortly after they discover them, they will surely ask you to implement some of those.

What do you need?

Refer to the last month article to see if you have everything you need in order to run the demo (basically VS.Net 2008 SP1 and the Windows API Code Pack library).

The only difference is that this time you really need Windows 7 otherwise the application won’t even start.

My own demo application

It is written in VB.Net only (sorry for C# guys). I know I already wrote that would do my best to provide code in both languages but I am darn busy at this time of the year!

Figure 1: The very useful demo application

The solution is not the same as last month as it is a completely different project.

How to check that the OS supports the features

In my demo application, I have added some protection that will prevent it from running on an OS that is not supported. Without it, the application triggers an exception because one of the libraries fails to initialize.

I had to put code in 2 places to prevent exception. The first place is in the constructor of the fMenuTaskBar form (because it is my starting form). Right after the call to the InitializeComponent, I have added this line to short-circuit the remaining on the constructor which tries to initialize some components:

'Validate if the OS supports the features
If Not ShellLibrary.IsPlatformSupported Then Return

The ShellLibrary class is part of the Windows API Code Pack itself. The IsPlatformSupported ease the job for us by returning a Boolean value indicating if we have to stop the application or if we can go on.

I have also added these lines to the form’s load event (because the constructor does not accept the application to close):

If Not ShellLibrary.IsPlatformSupported Then
    MessageBox.Show("Sorry but your version OS does not support those features.", _
                    "Windows version not supported", _
                    MessageBoxButtons.OK, _
                    MessageBoxIcon.Error)
    Application.Exit()
End If

After having warned the user that the application cannot run on this version of Windows, the application simply stops.

Taskbar progress bar overlay

The first feature I will explore lets us overly the icon of our application on the taskbar with a progress bar. This is a great visual cue that is always visible (if the taskbar itself is visible).

Figure 2: Demoing the progress bar feature

As shown in figure 2, to demonstrate the features of I have used 4 buttons and a combobox. Let starts with the 4 buttons which are only used to change the progress percentage to specific values (25%, 50%, 75%, and 100%). Setting the progress bar percentage is very easy. All you need to do is to call the SetProgressValue method of the static instance of the taskbar manager (this has changed from prior versions of the Windows API Code Pack so be careful when you see other examples).

In my samples, a single event handler handles all four buttons like this:

Dim intCurrentValue As Integer
If sender Is btnProgressStep1 Then
    intCurrentValue = 25
ElseIf sender Is btnProgressStep2 Then
    intCurrentValue = 50
ElseIf sender Is btnProgressStep3 Then
    intCurrentValue = 75
ElseIf sender Is btnProgressStep4 Then
    intCurrentValue = 100
End If
TaskbarManager.Instance.SetProgressValue(intCurrentValue, 100)

So just by calling the SetProgressValue method in your code you can have a progress indicator on the taskbar. I set using fix values but if you have other values, it will work just as well.

The progress bar can also easily turn from green, to yellow and or red depending on the state of your application. Just below the 4 buttons, there is a State combobox. This combo is initialized into the Shown event of the Form with the values of the TaskbarProgressBarState like this:

For Each state As String In System.Enum.GetNames(GetType(TaskbarProgressBarState))
    cboProgressBarStates.Items.Add(state)
Next state
cboProgressBarStates.SelectedItem = "NoProgress"

When the application runs, you can vary the color of the progress bar by selecting different states:

  • The Normal state displays in green.
  • The Paused state displays in yellow.
  • The Error state displays in red.

If you select the NoProgress state, the progress is completely reset to initial value (which is invisible).

The last state that you can select in the combo is the Inderterminate state. This state is a bit special by showing a continuously moving progress bar. Its color is always green.

The code required to set the state is as simple as calling the SetProgressState method:

' Update the status of the taskbar progress bar
Dim state As TaskbarProgressBarState = CType(System.Enum.Parse(GetType(TaskbarProgressBarState), 
                                             CStr(cboProgressBarStates.SelectedItem)), TaskbarProgressBarState)
TaskbarManager.Instance.SetProgressState(state)

In my code sample, it might seems complex but don’t forget that the state is a string in the combo that has to be converted back to its enumeration value.

So you now know how to make your taskbar icon show progress. It might be helpful when you have long running process and you want an easy way of showing progress to your user.

Taskbar image overlay

The taskbar image overlay is another great cue for your users. It lets you easily add an image to the icon of your application on the taskbar. For example, you might want to add a check mark or a X to the icon of your application. And I don’t mean to generate a second icon of your application with the check mark, I really mean to dynamically overlay the original icon with something else.

In my sample, by clicking one of the three colored picture box, the same colored-square will overlay your application icon in the taskbar.

Once again, a simple call to the SetOverlayIcon method of your Taskbar static instance is sufficient to modify your taskbar icon:

'Reset all pictures' borders
pictureIconOverlay1.BorderStyle = BorderStyle.None
pictureIconOverlay2.BorderStyle = BorderStyle.None
pictureIconOverlay3.BorderStyle = BorderStyle.None

If sender Is btnIconOverlayNone Then
    TaskbarManager.Instance.SetOverlayIcon(Me.Handle, Nothing, Nothing)

ElseIf sender Is pictureIconOverlay1 Then
    TaskbarManager.Instance.SetOverlayIcon(Me.Handle, My.Resources.Green, "Green")
    pictureIconOverlay1.BorderStyle = BorderStyle.Fixed3D

ElseIf sender Is pictureIconOverlay2 Then
    TaskbarManager.Instance.SetOverlayIcon(Me.Handle, My.Resources.Yellow, "Yellow")
    pictureIconOverlay2.BorderStyle = BorderStyle.Fixed3D

ElseIf sender Is pictureIconOverlay3 Then
    TaskbarManager.Instance.SetOverlayIcon(Me.Handle, My.Resources.Red, "Red")
    pictureIconOverlay3.BorderStyle = BorderStyle.Fixed3D
End If

As you can see in this snippet, most of the code is to handle the border around the selected color. If you wander where the colors are coming from, check the Resources folder in my solution. Those are colored -square icons.

And by the way, both the progress bar and the image overlay can be applied at the very same time.

Taskbar Jumplist

I don’t think that every application can benefit from the jumplist feature. But I may be completely wrong! A jumplist is a useful way to offer shortcuts/links to common tasks from your taskbar icon to your users.

Figure 3: A jumplist

I see two kinds of shortcuts/links. The first one offers links to related applications. In my sample, the “Add common tasks” button adds links to applications such as Notepad, Paint, and the Calculator.

The code required to add those links is the following:

' Path to Windows system folder
Dim systemFolder As String = Environment.GetFolderPath(Environment.SpecialFolder.System)

mobjJL.ClearAllUserTasks()

' Add our user tasks
mobjJL.AddUserTasks(New JumpListLink(IO.Path.Combine(systemFolder, "notepad.exe"), "Open Notepad") _
                    With {.IconReference = New IconReference(IO.Path.Combine(systemFolder, "notepad.exe"), 0)})
mobjJL.AddUserTasks(New JumpListLink(IO.Path.Combine(systemFolder, "mspaint.exe"), "Open Paint") _
                    With {.IconReference = New IconReference(IO.Path.Combine(systemFolder, "mspaint.exe"), 0)})
mobjJL.AddUserTasks(New JumpListSeparator())
mobjJL.AddUserTasks(New JumpListLink(IO.Path.Combine(systemFolder, "calc.exe"), "Open Calculator") _
                    With {.IconReference = New IconReference(IO.Path.Combine(systemFolder, "calc.exe"), 0)})
mobjJL.Refresh()

Before being able to use this code, you need to define a Jumplist variable. This is the declaration that appears at the class level:

Private mobjJL As Taskbar.JumpList
Private mobjJLCategory As New JumpListCustomCategory("Custom Category")

Finally, in the Form’s Shown event, you need to initialize your JumpList with code like this:

'Initialize the Jumplist
mobjJL = JumpList.CreateJumpList()
' Add custom categories
mobjJL.AddCustomCategories(mobjJLCategory)

Now that you have links, a user can right-click your taskbar icon and easily start a related-application easily.

The second kind offers links to your own application by the mean of opening it with an already loaded document if this is the purpose of your application. For example, Word and Excel display the list of recent document this way. So if Word is open, right-clicking the taskbar icon of Word will display the recently used file and a user can open one of them directly from there. Those documents can also be pinned (another feature of Windows 7). Another example is Internet Explorer stocking recent URLs in that list. You see the pattern?

So if your application is handling links or files, you can benefits from the jumplist. This time, the “Add custom tasks” button of my sample application is handling it. The code is the following:

Static sintItemCount As Integer
sintItemCount += 1

' Save current folder and path of running executable
Dim strExecutablePath = System.Reflection.Assembly.GetEntryAssembly().Location
Dim strExecutableFolder = IO.Path.GetDirectoryName(strExecutablePath)

' Specify path for shell item
Dim strPath As String = String.Format("{0}\test{1}.txt", strExecutableFolder, sintItemCount)

' Make sure this file exists
EnsureFileExist(strPath)

' Add shell item to custom category
mobjJLCategory.AddJumpListItems(New JumpListItem(strPath))
mobjJL.Refresh()

Basically, this code create a dummy file (by calling the EnsureFileExist method – download the sample to get it) and then adds it using the AddJumpListItems method of JumpListCustomItem class.

But wait, it is not that simple! In order to be able to add a custom jumplist item, it needs to be a file type that is registered with your application (like a .docx is registered or associated to Word). So if you go back to the form’s constructor, you will find a call to the CheckFileRegistration method (download the sample to get it) to ensure that .txt is registered with this application.

Finally, to prove that when you click one of these links really works, the name of the file is displayed in the title bar. This part is also done in the form’s constructor.

Thumbnail toolbar

By default, Windows 7 will display the image of your current form as a thumbnail when you hover your mouse cursor over your application icon on the taskbar. This default thumbnail image is only a visual cue for the user to quickly recognize the wanted form and select it.

But Windows 7 lets you customize it much more. First, it lets you provide a different thumbnail image. As you can see in figure 4, the right thumbnail doesn’t have the listview on its left. The second thing you can customize is that you can add features to this thumbnail. If you check below the picture, you will find 4 buttons. Those buttons are actually ThumbnailToolbarButton I have added and they are triggering events when clicked.

Figure 4: Custom thumbnail

In my sample application, all the required code can be found in the fImageViewer form. I won’t copy all the code of this form here (because most of is for populating the listview and for the navigation) so download the sample to have it all.

Figure 5: The image viewer form (showing one of my daughter top-left and her team winning a soccer tournament last summer)

As I said, the controls below the thumbnail are not plain-old regular control. They are instances of ThumbnailToolbarButton which is the only control type available at this time. You cannot add instances of those controls to a designer because those controls are dynamically created when the form is created and added to the taskbar instance.

Here are main steps to add those buttons. First, you need to declare them. So at the class level, I declared 4 buttons like this:

Private buttonPrevious As ThumbnailToolbarButton
Private buttonNext As ThumbnailToolbarButton
Private buttonFirst As ThumbnailToolbarButton
Private buttonLast As ThumbnailToolbarButton

Now that we have instances of the buttons, we need to initialize them. In the form’s Shown event, you will discover this initialization:

buttonFirst = New ThumbnailToolbarButton(My.Resources.first, "First Image")
AddHandler buttonFirst.Click, AddressOf buttonFirst_Click

buttonPrevious = New ThumbnailToolbarButton(My.Resources.prevArrow, "Previous Image")
AddHandler buttonPrevious.Click, AddressOf buttonPrevious_Click

buttonNext = New ThumbnailToolbarButton(My.Resources.nextArrow, "Next Image")
AddHandler buttonNext.Click, AddressOf buttonNext_Click

buttonLast = New ThumbnailToolbarButton(My.Resources.last, "Last Image")
AddHandler buttonLast.Click, AddressOf buttonLast_Click

' Add the new buttons to the taskbar's toolbar
TaskbarManager.Instance.ThumbnailToolbars.AddButtons(Me.Handle, buttonFirst, buttonPrevious, buttonNext, buttonLast)

So these lines are simply creating the instance, passing an image (from my resources) and a tooltip text to the constructor and adding a handler to the Click event. Once the 4 buttons are initialized, they are added to the taskbar by using the AddButtons method.

If you wander what code you will find into the Click event, you will be surprise to discover that nothing is related to the thumbnails button, it is all about your application just like if the same buttons were clicked on your form. In my sample, it is only a matter of moving the index and displaying the new selected image like this (this is the Next event handler):

Dim newIndex As Integer = listView1.SelectedIndices(0) + 1

If newIndex < listView1.Items.Count Then
    listView1.Items(newIndex).Selected = True
    listView1.Items(newIndex).EnsureVisible()
End If

listView1.Focus()

Because we changed the index of the currently selected item in the listview, the SelectedIndexChanged will be triggered and the PictureBox will be reloaded with the newly selected image. The interesting part of the event handler is this:

buttonFirst.Enabled = False

This buttonFirst control is one of the ThumbnailToolbarButton controls we dynamically created. You can set its properties and they will be modified on the thumbnails as expected.

The last thing I haven’t mentioned yet about this form is how to set the thumbnail picture to something different then the default Form’s image. To do it, you need to call the SetThumbnailClip method and passing it the image you want. In my case, I only need the picture to be displayed into the thumbnail. If you check the SizeChanged event of the PictureBox, you will find this call:

TaskbarManager.Instance.TabbedThumbnail.SetThumbnailClip(Me.Handle, New Rectangle(pictureBox1.Location, pictureBox1.Size))

By setting this, the correct image will be displayed into the thumbnail when required with nothing around it because the coordinates passed to the method are the coordinates of the picturebox control itself.

You might also want to check the remaining of this form to discover how to add image to the listview (mainly in the InitListView and the GetPictures methods).

This is the kind of feature I like but once again, it is very specific to some applications (and might not be suited for all the forms of an application).

Tabbed Thumbnail

This last topic is much like the previous one where I set myself the thumbnail image and I provide some ThumbnailToolbarButton for additional features. The thing that is special here is that my form is a series of TabPages in a TabControl. The TabPages can display either a WebBrowser control (if you use the Navigate button) or a RichTextBox (if you use the Open file button). The content of each TabPage will be displayed in the thumbnails each with its own set of ThumbnailToolbarButton because a WebBrowser has different feature then a RichTextbox. As shown in figure 6, the thumbnail of a WebBrowser offers back, forward, and refresh features while the RichTextBox thumbnail offers cut, copy, paste, and select all features.

Figure 6: The Tabbed thumbnails

In my downloadable sample, this tabbed form is the fTabbedThumbnail form. I strongly encourage you once again to download the sample attached with this article as I won’t paste here all the required code.

Just like the previous example, you will find at the class level the declaration of the 7 ThumbnailToolbarButtons required for the application (3 for the WebBrowser and 4 for the RichtextBox). This time, you will find the instantiations and the binding to the event handlers for the 7 buttons in the form’s constructor. If you remember the last sample, at this point, we were using the AddButtons method to the TaskBar but the constructor does not call it. The reason is simple. Because different buttons will be required according to the showed thumbnails, we have to postpone this operation until we know which control will be displayed.

There are 2 places where you will find the call to the AddButtons method. The first place is when you navigate to a URL (a TabPage is created, on which a new WebBrowser control is added, then buttons are added to the ThumbNails and finally the AddThumbnailPreview is called to complete the setup). The second place is when a file is opened in which the same basic steps are repeated.

The last thing worth mentioning here is the UpdatePreviewBitmap method which is called here and there in the execution of this form. It has the following code:

If tabPage IsNot Nothing Then
    'to let the application finish the previous instruction (cut, paste, ...) before grabbing the image
    Application.DoEvents()

    Dim preview As TabbedThumbnail = TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(tabPage)

    If preview IsNot Nothing Then
        Dim bitmap As Bitmap = TabbedThumbnailScreenCapture.GrabWindowBitmap(tabPage.Handle, tabPage.Size)
        preview.SetImage(bitmap)
    End If
End If

This is the spot where an image of the current TabPage is actually taken to the set the thumbnail image.

Conclusion

That’s about it! Some of the features presented are very easy to implement while others require more code. As I have already said, I am not sure that all the features should be implemented into all business applications. You will have to figure if it worth implementing it.

I hope you appreciated the topic and see you next month.


(Print this page)