Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Clipz - A Friendly Introduction to the Windows 7 Taskbar Features

, 17 Dec 2009 CPOL
An overview of the Windows 7 taskbar features, and how to use then in your own applications.
Clipz.zip
Clipz
bin
Debug
Clipz.csproj.user
CopyIcon.ico
DeleteIcon.ico
Model
Native Methods
Properties
Settings.settings
Resources
Audio.png
Clipboard.png
NextIcon.ico
PreviousIcon.ico
Utility
Core
AppRestartRecovery
Dialogs
Common
TaskDialogs
Interop
AppRestartRecovery
Dialogs
NetworkList
PowerManagement
TaskDialogs
NetworkList
PowerManagement
Properties
PropertySystem
SafeHandles
Libraries
StructureMap.dll
Shell
Common
CommonFileDialogs
Controls
Design
ShellObjects.cd
ShellThumbnailClassDiagram.cd
DesktopWindowManager
ExplorerBrowser
ExplorerBrowserDiagram.cd
Interop
Common
Dialogs
ExplorerBrowser
KnownFolders
PropertySystem
StockIcons
Taskbar
KnownFolders
Properties
PropertySystem
StockIcons
Taskbar
//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using Microsoft.WindowsAPICodePack.Shell;
using MS.WindowsAPICodePack.Internal;

namespace Microsoft.WindowsAPICodePack.Taskbar
{
    /// <summary>
    /// Represents a tabbed thumbnail on the taskbar for a given window or a control.
    /// </summary>
    public class TabbedThumbnail : IDisposable
    {
        #region Internal members

        internal IntPtr WindowHandle
        {
            get;
            set;
        }

        internal IntPtr ParentWindowHandle
        {
            get;
            set;
        }

        internal UIElement WindowsControl
        {
            get;
            set;
        }

        internal Window WindowsControlParentWindow
        {
            get;
            set;
        }

        private TaskbarWindow taskbarWindow;
        internal TaskbarWindow TaskbarWindow
        {
            get { return taskbarWindow; }
            set
            {
                taskbarWindow = value;

                // If we have a TaskbarWindow assigned, set it's icon
                if (taskbarWindow != null && taskbarWindow.TabbedThumbnailProxyWindow != null)
                    TaskbarWindow.TabbedThumbnailProxyWindow.Icon = Icon;
            }
        }

        private bool addedToTaskbar;
        internal bool AddedToTaskbar
        {
            get
            {
                return addedToTaskbar;
            }
            set
            {
                addedToTaskbar = value;

                // The user has updated the clipping region, so invalidate our existing preview
                if (TaskbarWindowManager.Instance != null && ClippingRectangle != null)
                    TaskbarWindowManager.Instance.InvalidatePreview(this.TaskbarWindow);
            }
        }

        internal bool RemovedFromTaskbar
        {
            get;
            set;
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new TabbedThumbnail with the given window handle of the parent and
        /// a child control/window's handle (e.g. TabPage or Panel)
        /// </summary>
        /// <param name="parentWindowHandle">Window handle of the parent window. 
        /// This window has to be a top-level window and the handle cannot be null or IntPtr.Zero</param>
        /// <param name="windowHandle">Window handle of the child control or window for which a tabbed 
        /// thumbnail needs to be displayed</param>
        public TabbedThumbnail(IntPtr parentWindowHandle, IntPtr windowHandle)
        {
            if (parentWindowHandle == IntPtr.Zero)
                throw new ArgumentException("Parent window handle cannot be zero.", "parentWindowHandle");
            if (windowHandle == IntPtr.Zero)
                throw new ArgumentException("Child control's window handle cannot be zero.", "windowHandle");

            WindowHandle = windowHandle;
            ParentWindowHandle = parentWindowHandle;
        }

        /// <summary>
        /// Creates a new TabbedThumbnail with the given window handle of the parent and
        /// a child control (e.g. TabPage or Panel)
        /// </summary>
        /// <param name="parentWindowHandle">Window handle of the parent window. 
        /// This window has to be a top-level window and the handle cannot be null or IntPtr.Zero</param>
        /// <param name="control">Child control for which a tabbed thumbnail needs to be displayed</param>
        /// <remarks>This method can also be called when using a WindowsFormHost control in a WPF application.
        ///  Call this method with the main WPF Window's handle, and windowsFormHost.Child control.</remarks>
        public TabbedThumbnail(IntPtr parentWindowHandle, Control control)
        {
            if (parentWindowHandle == IntPtr.Zero)
                throw new ArgumentException("Parent window handle cannot be zero.", "parentWindowHandle");
            if (control == null)
                throw new ArgumentNullException("control");

            WindowHandle = control.Handle;
            ParentWindowHandle = parentWindowHandle;
        }

        /// <summary>
        /// Creates a new TabbedThumbnail with the given window handle of the parent and
        /// a WPF child Window. For WindowsFormHost control, use TabbedThumbnail(IntPtr, Control) overload and pass
        /// the WindowsFormHost.Child as the second parameter.
        /// </summary>
        /// <param name="parentWindow">Parent window for the UIElement control. 
        /// This window has to be a top-level window and the handle cannot be null</param>
        /// <param name="windowsControl">WPF Control (UIElement) for which a tabbed thumbnail needs to be displayed</param>
        /// <param name="peekOffset">Offset point used for displaying the peek bitmap. This setting is
        /// recomended for hidden WPF controls as it is difficult to calculate their offset.</param>
        public TabbedThumbnail(Window parentWindow, UIElement windowsControl, Vector peekOffset)
        {
            if (windowsControl == null)
                throw new ArgumentNullException("control");
            if (parentWindow == null)
                throw new ArgumentNullException("parentWindow");

            WindowHandle = IntPtr.Zero;
            WindowsControl = windowsControl;
            WindowsControlParentWindow = parentWindow;
            ParentWindowHandle = (new WindowInteropHelper(parentWindow)).Handle;
            PeekOffset = peekOffset;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// This event is raised when the Title property changes.
        /// </summary>
        public event EventHandler TitleChanged;

        /// <summary>
        /// This event is raised when the Tooltip property changes.
        /// </summary>
        public event EventHandler TooltipChanged;

        private string title;
        /// <summary>
        /// Title for the window shown as the taskbar thumbnail.
        /// </summary>
        public string Title
        {
            get
            {
                return title;
            }
            set
            {
                if (value != title)
                {
                    title = value;

                    if (TitleChanged != null)
                        TitleChanged(this, EventArgs.Empty);
                }
            }
        }

        private string tooltip;
        /// <summary>
        /// Tooltip to be shown for this thumbnail on the taskbar. 
        /// By default this is full title of the window shown on the taskbar.
        /// </summary>
        public string Tooltip
        {
            get { return tooltip; }
            set
            {
                if (value != tooltip)
                {
                    tooltip = value;

                    if (TooltipChanged != null)
                        TooltipChanged(this, EventArgs.Empty);
                }
            }
        }

        internal Icon Icon
        {
            get;
            private set;
        }

        /// <summary>
        /// Sets the window icon for this thumbnail preview
        /// </summary>
        /// <param name="icon">System.Drawing.Icon for the window/control associated with this preview</param>
        public void SetWindowIcon(Icon icon)
        {
            Icon = icon;

            // If we have a TaskbarWindow assigned, set its icon
            if (TaskbarWindow != null && TaskbarWindow.TabbedThumbnailProxyWindow != null)
                TaskbarWindow.TabbedThumbnailProxyWindow.Icon = Icon;
        }

        /// <summary>
        /// Sets the window icon for this thumbnail preview
        /// </summary>
        /// <param name="hIcon">Icon handle (hIcon) for the window/control associated with this preview</param>
        /// <remarks>This method will not release the icon handle. It is the caller's responsibility to release the icon handle.</remarks>
        public void SetWindowIcon(IntPtr hIcon)
        {
            if (hIcon != IntPtr.Zero)
                Icon = System.Drawing.Icon.FromHandle(hIcon);
            else
                Icon = null;

            // If we have a TaskbarWindow assigned, set it's icon
            if (TaskbarWindow != null && TaskbarWindow.TabbedThumbnailProxyWindow != null)
                TaskbarWindow.TabbedThumbnailProxyWindow.Icon = Icon;
        }

        private Rectangle? clippingRectangle;
        /// <summary>
        /// Specifies that only a portion of the window's client area
        /// should be used in the window's thumbnail.
        /// <para>A value of null will clear the clipping area and use the default thumbnail.</para>
        /// </summary>
        public Rectangle? ClippingRectangle
        {
            get { return clippingRectangle; }
            set
            {
                clippingRectangle = value;

                // The user has updated the clipping region, so invalidate our existing preview
                if (TaskbarWindowManager.Instance != null)
                    TaskbarWindowManager.Instance.InvalidatePreview(this.TaskbarWindow);
            }
        }

        internal IntPtr CurrentHBitmap
        {
            get;
            set;
        }

        /// <summary>
        /// Override the thumbnail and peek bitmap. 
        /// By providing this bitmap manually, Thumbnail Window manager will provide the 
        /// Desktop Window Manager (DWM) this bitmap instead of rendering one automatically.
        /// Use this property to update the bitmap whenever the control is updated and the user
        /// needs to be shown a new thumbnail on the taskbar preview (or aero peek).
        /// </summary>
        /// <param name="bitmap">The image to use.</param>
        /// <remarks>
        /// If the bitmap doesn't have the right dimensions, the DWM may scale it or not 
        /// render certain areas as appropriate - it is the user's responsibility
        /// to render a bitmap with the proper dimensions.
        /// </remarks>
        public void SetImage(Bitmap bitmap)
        {
            if (bitmap != null)
            {
                SetImage(bitmap.GetHbitmap());
            }
            else
            {
                SetImage(IntPtr.Zero);
            }
        }

        /// <summary>
        /// Override the thumbnail and peek bitmap. 
        /// By providing this bitmap manually, Thumbnail Window manager will provide the 
        /// Desktop Window Manager (DWM) this bitmap instead of rendering one automatically.
        /// Use this property to update the bitmap whenever the control is updated and the user
        /// needs to be shown a new thumbnail on the taskbar preview (or aero peek).
        /// </summary>
        /// <param name="bitmapSource">The image to use.</param>
        /// <remarks>
        /// If the bitmap doesn't have the right dimensions, the DWM may scale it or not 
        /// render certain areas as appropriate - it is the user's responsibility
        /// to render a bitmap with the proper dimensions.
        /// </remarks>
        public void SetImage(BitmapSource bitmapSource)
        {
            if (bitmapSource == null)
            {
                SetImage(IntPtr.Zero);
                return;
            }

            using (MemoryStream memoryStream = new MemoryStream())
            {
                BmpBitmapEncoder encoder = new BmpBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
                encoder.Save(memoryStream);
                memoryStream.Position = 0;
                Bitmap bmp = new Bitmap(memoryStream);
                if (bmp != null)
                {
                    try
                    {
                        SetImage(bmp.GetHbitmap());
                    }
                    finally
                    {
                        //Delete the bitmap
                        bmp.Dispose();
                        bmp = null;
                    }
                }
            }
        }


        /// <summary>
        /// Override the thumbnail and peek bitmap. 
        /// By providing this bitmap manually, Thumbnail Window manager will provide the 
        /// Desktop Window Manager (DWM) this bitmap instead of rendering one automatically.
        /// Use this property to update the bitmap whenever the control is updated and the user
        /// needs to be shown a new thumbnail on the taskbar preview (or aero peek).
        /// </summary>
        /// <param name="hBitmap">A bitmap handle for the image to use.
        /// <para>When the TabbedThumbnail is finalized, this class will delete the provided hBitmap.</para></param>
        /// <remarks>
        /// If the bitmap doesn't have the right dimensions, the DWM may scale it or not 
        /// render certain areas as appropriate - it is the user's responsibility
        /// to render a bitmap with the proper dimensions.
        /// </remarks>
        internal void SetImage(IntPtr hBitmap)
        {
             
            // Before we set a new bitmap, dispose the old one
            if (CurrentHBitmap != IntPtr.Zero)
            {
                ShellNativeMethods.DeleteObject(CurrentHBitmap);
            }
            
            // Set the new bitmap
            CurrentHBitmap = hBitmap;

            // Let DWM know to invalidate its cached thumbnail/preview and ask us for a new one (i.e. the one
            // user just updated)
            if (TaskbarWindowManager.Instance != null)
                TaskbarWindowManager.Instance.InvalidatePreview(TaskbarWindow);
        }

        /// <summary>
        /// Specifies whether a standard window frame will be displayed
        /// around the bitmap.  If the bitmap represents a top-level window,
        /// you would probably set this flag to <b>true</b>.  If the bitmap
        /// represents a child window (or a frameless window), you would
        /// probably set this flag to <b>false</b>.
        /// </summary>
        public bool DisplayFrameAroundBitmap
        {
            get;
            set;
        }

        /// <summary>
        /// Invalidate any existing thumbnail preview. Calling this method
        /// will force DWM to request a new bitmap next time user previews the thumbnails
        /// or requests Aero peek preview.
        /// </summary>
        public void InvalidatePreview()
        {
            // invalidate the thumbnail bitmap
            if (TaskbarWindowManager.Instance != null)
            {
                SetImage(IntPtr.Zero);
            }
        }

        /// <summary>
        /// Gets or sets the offset used for displaying the peek bitmap. This setting is
        /// recomended for hidden WPF controls as it is difficult to calculate their offset.
        /// </summary>
        public Vector? PeekOffset
        {
            get;
            set;
        }

        #endregion

        #region Events


        /// <summary>
        /// The event that occurs when a tab is closed on the taskbar thumbnail preview.
        /// </summary>
        public event EventHandler<TabbedThumbnailEventArgs> TabbedThumbnailClosed;

        /// <summary>
        /// The event that occurs when a tab is maximized via the taskbar thumbnail preview (context menu).
        /// </summary>
        public event EventHandler<TabbedThumbnailEventArgs> TabbedThumbnailMaximized;

        /// <summary>
        /// The event that occurs when a tab is minimized via the taskbar thumbnail preview (context menu).
        /// </summary>
        public event EventHandler<TabbedThumbnailEventArgs> TabbedThumbnailMinimized;

        /// <summary>
        /// The event that occurs when a tab is activated (clicked) on the taskbar thumbnail preview.
        /// </summary>
        public event EventHandler<TabbedThumbnailEventArgs> TabbedThumbnailActivated;

        /// <summary>
        /// The event that occurs when a thumbnail or peek bitmap is requested by the user.
        /// </summary>
        public event EventHandler<TabbedThumbnailBitmapRequestedEventArgs> TabbedThumbnailBitmapRequested;


        internal void OnTabbedThumbnailMaximized()
        {
            if (TabbedThumbnailMaximized != null)
            {
                TabbedThumbnailMaximized(this, GetTabbedThumbnailEventArgs());
            }
            else
            {
                // No one is listening to these events.
                // Forward the message to the main window
                CoreNativeMethods.SendMessage(ParentWindowHandle, TabbedThumbnailNativeMethods.WM_SYSCOMMAND, new IntPtr(TabbedThumbnailNativeMethods.SC_MAXIMIZE), IntPtr.Zero);
            }
        }

        internal void OnTabbedThumbnailMinimized()
        {
            if (TabbedThumbnailMinimized != null)
                TabbedThumbnailMinimized(this, GetTabbedThumbnailEventArgs());
            else
            {
                // No one is listening to these events.
                // Forward the message to the main window
                CoreNativeMethods.SendMessage(ParentWindowHandle, TabbedThumbnailNativeMethods.WM_SYSCOMMAND, new IntPtr(TabbedThumbnailNativeMethods.SC_MINIMIZE), IntPtr.Zero);
            }

        }

        internal void OnTabbedThumbnailClosed()
        {
            if (TabbedThumbnailClosed != null)
                TabbedThumbnailClosed(this, GetTabbedThumbnailEventArgs());
            else
            {
                // No one is listening to these events.
                // Forward the message to the main window
                CoreNativeMethods.SendMessage(ParentWindowHandle, TabbedThumbnailNativeMethods.WM_NCDESTROY, IntPtr.Zero, IntPtr.Zero);
            }

            // Remove it from the internal list as well as the taskbar
            TaskbarManager.Instance.TabbedThumbnail.RemoveThumbnailPreview(this);
        }

        internal void OnTabbedThumbnailActivated()
        {
            if (TabbedThumbnailActivated != null)
                TabbedThumbnailActivated(this, GetTabbedThumbnailEventArgs());
            else
            {
                // No one is listening to these events.
                // Forward the message to the main window
                CoreNativeMethods.SendMessage(ParentWindowHandle, TabbedThumbnailNativeMethods.WM_ACTIVATEAPP, new IntPtr(1), new IntPtr(Thread.CurrentThread.GetHashCode()));
            }
        }

        internal void OnTabbedThumbnailBitmapRequested()
        {
            if (TabbedThumbnailBitmapRequested != null)
            {
                TabbedThumbnailBitmapRequestedEventArgs eventArgs = null;

                if (this.WindowHandle != IntPtr.Zero)
                    eventArgs = new TabbedThumbnailBitmapRequestedEventArgs(this.WindowHandle, this);
                else if (this.WindowsControl != null)
                    eventArgs = new TabbedThumbnailBitmapRequestedEventArgs(this.WindowsControl, this);

                TabbedThumbnailBitmapRequested(this, eventArgs);
            }
        }

        private TabbedThumbnailEventArgs GetTabbedThumbnailEventArgs()
        {
            TabbedThumbnailEventArgs eventArgs = null;

            if (this.WindowHandle != IntPtr.Zero)
                eventArgs = new TabbedThumbnailEventArgs(this.WindowHandle, this);
            else if (this.WindowsControl != null)
                eventArgs = new TabbedThumbnailEventArgs(this.WindowsControl, this);
                
            return eventArgs;
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// 
        /// </summary>
        ~TabbedThumbnail()
        {
            Dispose(false);
        }

        /// <summary>
        /// Release the native objects.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Release the native objects.
        /// </summary>
        /// <param name="disposing"></param>
        public void Dispose(bool disposing)
        {
            if (disposing)
            {
                taskbarWindow = null;

                if (Icon != null)
                    Icon.Dispose();
                Icon = null;

                title = null;
                tooltip = null;
                WindowsControl = null;
            }

            if (CurrentHBitmap != IntPtr.Zero)
            {
                ShellNativeMethods.DeleteObject(CurrentHBitmap);
                CurrentHBitmap = IntPtr.Zero;
            }
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

TylerBrinks
Web Developer PageLabs
United States United States
I'm the founder of PageLabs, a web-based performance and SEO optimization site.

Give your site a boost in performance, even take a free speed test!
 
http://www.pagelabs.com
Follow on   Twitter

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141223.1 | Last Updated 17 Dec 2009
Article Copyright 2009 by TylerBrinks
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid