Before we can address the subtleties of using various features that have to do with taskbar buttons, we must understand how these taskbar buttons are assigned and created in the first place.
The most natural inclination is to say this: If you have multiple top-level windows* created by the same process, then they all belong to the same taskbar button. Furthermore, if you have M instances of that same process (with possibly N windows displayed by each instance), then these MxN instances all belong to the same taskbar button.
This intuitive approach works very well for the vast majority of applications, and this is also the default. Try launching multiple Notepad windows and see them all grouped under the same taskbar button.
However, some applications might require more sophisticated behavior. For example, assume that you have a host process that runs various types of plug-in applications (e.g. an “Office” host process that runs “Word”, “Excel” and “PowerPoint”). This host process doesn’t want a taskbar button at all, while its plug-ins want their own private taskbar button for each application type.
This variety of scenarios is addressed by assigning each window an identifier called the Application ID (app id) which determines the taskbar button to which the window belongs. The default app id for a window is a default app id generated for the process to which the window belongs, which is in turn a default app id generated for the executable file that the process runs.
These defaults explain very well the default behavior for windows within multiple processes of the same executable. The customized behavior we discussed above can be obtained by explicitly setting the app id of the various windows to different values – a different value for each plug-in that wants its own taskbar button.
If you wanted to have a separate taskbar button for each process (including all windows owned by that process), you can also set an explicit app id for the entire process, which affects all windows within that process that did not set an explicit app id.
Setting the explicit process app id is fairly straightforward – it involves a single call to the SetCurrentProcessExplicitAppUserModelID function. In the managed wrapper, this is exposed by the Windows7Taskbar.SetCurrentProcessAppId method which takes a string and sets the app id (there is also an equivalent Get method).
Setting the app id for a window is slightly less straightforward – it requires calling the SHGetPropertyStoreForWindow function and then manipulating the resulting IPropertyStore object to retrieve the requested property. While this isn’t rocket science, the managed wrapper is certainly more user-friendly – Windows7Taskbar.SetWindowAppId does the job (it takes a window handle and a string). There is also an extension method that takes a Form instance – and it’s easy to write another extension method that will take a WPF Window instance.
In the Windows7.DesktopIntegration.MainDemo project you will find a button that changes the window’s app id. You can experiment with that by running two instances of the demo app and clicking the button in one of them. This will result in having two taskbar buttons, one for each window, instead of the default single taskbar button that has both windows “behind it”. (The code is trivial, so I won’t even bother repeating it here.)
By the way, the window app id is dynamic, so it’s entirely possible for a window to show up as part of one taskbar button and then change its app id so that it appears on an entirely different taskbar button. This has interesting effects: for example, the jump list is attached to a taskbar button (with a specific app id), so the same window might show a different jump list when it is reattached to an entirely different taskbar button. This has a good potential to confuse users, however, so the recommended practice would be setting the window app id and sticking to it (using the same deterministic process for determining the app id every time the window is shown).
As an aside, there are additional ways to affect the app id of a window: through the IShellLink object (shortcut) that represents the window’s application, through the file association registration in the registry, when constructing a jump list and in other scenarios. We might return to this later when we discuss jump lists. The one thing to remember for now is that if you begin using explicit app ids, then you need to do so consistently – if you forget to explicitly specify the app id somewhere, you will likely affect something completely different from what you meant.
* The Windows 7 DWM only recognizes top-level windows. To display a child window (e.g. a tab from a tab control or an MDI document) as a separate thumbnail in the taskbar, we must resort to custom previews – which will be the subject of a future post.