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
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

using System.IO;
using System.Reflection;
using Microsoft.WindowsAPICodePack.Shell;

namespace Clipz
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Drawing;
    using System.Linq;
    using System.Windows.Forms;
    using Microsoft.WindowsAPICodePack.Taskbar;
    using NativeMethods;

    public partial class Form1 : Form
    {
        public const string ModeCommandArgument = "-1";
        public const string ClearCommandArgument = "-2";
        private readonly ClipboardManager _clipboardManager = new ClipboardManager();
        private readonly Dictionary<TabbedThumbnail, Control> _thumbnails = new Dictionary<TabbedThumbnail, Control>();
        private IntPtr _clipboardObserverHandle;
        private bool _loaded;
        private bool _copying;
        private RenderMode _mode = RenderMode.Auto;
        private JumpList _jumpList;
        private ThumbnailToolbarButton _previous;
        private ThumbnailToolbarButton _next;
        private ThumbnailToolbarButton _delete;
        private ThumbnailToolbarButton _copy;

        private enum RenderMode
        {
            Paged,
            Auto
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Form1"/> class.
        /// </summary>
        public Form1()
        {
            InitializeComponent();
            _clipboardManager.ItemAdded += _clipboardManager_ItemAdded;
            _clipboardManager.ItemRemoved += _clipboardManager_ItemRemoved;
            _clipboardManager.ItemsCleared += _clipboardManager_ItemsCleared;
            _clipboardManager.SelectedIndexChanged += _clipboardManager_SelectedIndexChanged;

            PreviewImageBox.Image = Resources.Resources.Clipboard;

            Icon = Resources.Resources.CopyIcon;

            _trayIcon.Visible = true;
            _trayIcon.Icon = Resources.Resources.CopyIcon;
            _trayIcon.ShowBalloonTip(2000, "Clipz", Resources.Resources.BalloonTip, ToolTipIcon.Info);

            // Comment these 4 lines out to see the UI.  This is done to keep the window active/visible
            // but off the screen.  Minimized or hidden areas of the screen prevent the thumbnail images
            // from repainting.
            //Left = int.MaxValue;
            //Top = int.MaxValue;
            //Height = Screen.PrimaryScreen.Bounds.Height;
            //Width = Screen.PrimaryScreen.Bounds.Width;
        }

        /// <summary>
        /// Changes the mode taskbar display mode.
        /// </summary>
        public void ChangeMode()
        {
            if (_mode == RenderMode.Paged)
            {
                //Set the thumbnail clip to the parent window
                TaskbarManager.Instance.TabbedThumbnail.SetThumbnailClip(Handle, ClientRectangle);

                // Add a thumbnail preview for each clipboard item
                foreach (var key in _thumbnails.Keys)
                {
                    if (!TaskbarManager.Instance.TabbedThumbnail.IsThumbnailPreviewAdded(key))
                    {
                        TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(key);
                    }
                }

                _mode = RenderMode.Auto;
            }
            else
            {
                SetEmptyPreview();
                _thumbnails.Keys.ToList().ForEach(t => TaskbarManager.Instance.TabbedThumbnail.RemoveThumbnailPreview(t));

                _mode = RenderMode.Paged;
            }

            usePagedPreviewToolStripMenuItem.Text = _mode == RenderMode.Auto
                                                        ? Resources.Resources.PagedPreviewText
                                                        : Resources.Resources.AutoPreviewText;

            CreateJumplist();
        }
        /// <summary>
        /// Clears the clipboard item contents.
        /// </summary>
        public void ClearContents()
        {
            var itemCount = FlowPanel.Controls.Count;

            for (int i = 0; i < itemCount; i++ )
            {
                _clipboardManager.Remove(0);
            }
        }

        /// <summary>
        /// Overrides WndProc to intercept Windows messages, specifically the draw clipboard and 
        /// change chain messages.  These are the two messages necessary for capturing and 
        /// handling clipboard changes.  Decorated with DebuggerNonUserCode for easier 
        /// debugging due to multithreading.
        /// </summary>
        /// <param name="message">The Windows message.</param>
        [DebuggerNonUserCode]
        protected override void WndProc(ref Message message)
        {
            if (!_loaded || _copying) { base.WndProc(ref message); }

            // Check whether a command line argument was sent via another
            // attempted instance of the application.
            if (message.Msg == User32.ChangeModeMessage)
            {
                ChangeMode();
                return;
            }
            if (message.Msg == User32.ClearContents)
            {
                ClearContents();
                return;
            }

            // Monitor clipboard changes.
            switch ((WindowsMessages)message.Msg)
            {
                case WindowsMessages.WM_DRAWCLIPBOARD:
                    UpdateClipboard();

                    User32.SendMessage(_clipboardObserverHandle, message.Msg, message.WParam, message.LParam);
                    break;

                case WindowsMessages.WM_CHANGECBCHAIN:

                    if (message.WParam == _clipboardObserverHandle)
                    {
                        _clipboardObserverHandle = message.LParam;
                    }
                    else
                    {
                        User32.SendMessage(_clipboardObserverHandle, message.Msg, message.WParam, message.LParam);
                    }
                    break;

                default:
                    base.WndProc(ref message);
                    break;
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Form.Load"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            // Subscribe to Windows clipboard notifications
            _clipboardObserverHandle = User32.SetClipboardViewer(Handle);

            _previous = new ThumbnailToolbarButton(Resources.Resources.PreviousIcon, Resources.Resources.PreviousTooltip);
            _next = new ThumbnailToolbarButton(Resources.Resources.NextIcon, Resources.Resources.NextTooltip);
            _delete = new ThumbnailToolbarButton(Resources.Resources.DeleteIcon, Resources.Resources.DeleteTooltip);
            _copy = new ThumbnailToolbarButton(Resources.Resources.CopyIcon, Resources.Resources.CopyTooltip);
            TaskbarManager.Instance.ThumbnailToolbars.AddButtons(Handle, new[] { _previous, _delete, _copy, _next });

            _previous.Enabled = false;
            _next.Enabled = false;
            _delete.Enabled = false;
            _copy.Enabled = false;

            _previous.Click += _previous_Click;
            _next.Click += _next_Click;
            _delete.Click += _delete_Click;
            _copy.Click += _copy_Click;

            _loaded = true;

            base.OnLoad(e);
        }
        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Form.Closing"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.ComponentModel.CancelEventArgs"/> that contains the event data.</param>
        protected override void OnClosing(CancelEventArgs e)
        {
            _trayIcon.Dispose();
            User32.ChangeClipboardChain(Handle, _clipboardObserverHandle);
            base.OnClosing(e);
        }
        /// <summary>
        /// Change the preview window to the selected clipboard item.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Clipz.ClipboardItemIndexEventArgs"/> instance containing the event data.</param>
        private void _clipboardManager_SelectedIndexChanged(object sender, ClipboardItemIndexEventArgs e)
        {
            SetPreview(e.CurrentItemIndex, e.CanMoveNext, e.CanMovePrevious);
        }
        /// <summary>
        /// Adds a new picturebox to the flow panel and set the preview window or thumbnail 
        /// preview image to the new item's thumbnail bitmap.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Clipz.ClipboardItemAddedEventArgs"/> instance containing the event data.</param>
        private void _clipboardManager_ItemAdded(object sender, ClipboardItemAddedEventArgs e)
        {
            var image = new PictureBox
                                  {
                                      Size = new Size(150, 150),
                                      Image = e.Thumbnail
                                  };

            FlowPanel.Controls.Add(image);

            // Subscribe to the close and activate events
            var thumbnail = new TabbedThumbnail(Handle, image);
            thumbnail.SetImage(e.Thumbnail);
            thumbnail.TabbedThumbnailClosed += thumbnail_TabbedThumbnailClosed;
            thumbnail.TabbedThumbnailActivated += thumbnail_TabbedThumbnailActivated;

            _thumbnails.Add(thumbnail, image);

            if (_mode == RenderMode.Auto)
            {
                TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(thumbnail);
            }
            else
            {
                SetPreview(e.CurrentItemIndex, e.CanMoveNext, e.CanMovePrevious);
            }
        }
        /// <summary>
        /// Resets to the placeholder image when no items are available.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Clipz.ClipboardItemAddedEventArgs"/> instance containing the event data.</param>
        private void _clipboardManager_ItemsCleared(object sender, EventArgs e)
        {
            PreviewImageBox.Image = Resources.Resources.Clipboard;
            _delete.Enabled = false;
            _copy.Enabled = false;
        }
        /// <summary>
        /// Remove the clipboard item.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Clipz.ClipboardItemRemovedEventArgs"/> instance containing the event data.</param>
        private void _clipboardManager_ItemRemoved(object sender, ClipboardItemRemovedEventArgs e)
        {
            FlowPanel.Controls.RemoveAt(e.RemovedItemIndex);
            _thumbnails.Remove(_thumbnails.Skip(e.RemovedItemIndex).First().Key);
        }
        /// <summary>
        /// Cycles to the next preview image in scrolling mode.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.WindowsAPICodePack.Taskbar.ThumbnailButtonClickedEventArgs"/> instance containing the event data.</param>
        private void _next_Click(object sender, ThumbnailButtonClickedEventArgs e)
        {
            _clipboardManager.Next();
        }
        /// <summary>
        /// Cycles to the previous preview image in scrolling mode.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.WindowsAPICodePack.Taskbar.ThumbnailButtonClickedEventArgs"/> instance containing the event data.</param>
        private void _previous_Click(object sender, ThumbnailButtonClickedEventArgs e)
        {
            _clipboardManager.Previous();
        }
        /// <summary>
        /// Copy the current item back to the Windows clipboard.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.WindowsAPICodePack.Taskbar.ThumbnailButtonClickedEventArgs"/> instance containing the event data.</param>
        private void _copy_Click(object sender, ThumbnailButtonClickedEventArgs e)
        {
            _copying = true;

            _clipboardManager.Copy();

            _copying = false;
        }
        /// <summary>
        /// Remove the current clipboard item (scrolling mode).
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.WindowsAPICodePack.Taskbar.ThumbnailButtonClickedEventArgs"/> instance containing the event data.</param>
        private void _delete_Click(object sender, ThumbnailButtonClickedEventArgs e)
        {
            _clipboardManager.RemoveCurrent();
        }
        /// <summary>
        /// Sets the preview picture box image to the image that will be displayed in
        /// the Windows taskbar preview.
        /// </summary>
        /// <param name="itemIndex">Index of the item.</param>
        /// <param name="nextEnabled">if set to <c>true</c> next is enabled.</param>
        /// <param name="prevEnabled">if set to <c>true</c> previous is enabled.</param>
        private void SetPreview(int itemIndex, bool nextEnabled, bool prevEnabled)
        {
            try
            {
                PreviewImageBox.Image = ((PictureBox) FlowPanel.Controls[itemIndex]).Image;

                _next.Enabled = nextEnabled;
                _previous.Enabled = prevEnabled;
                _delete.Enabled = true;
                _copy.Enabled = true;
            }
            catch(ArgumentOutOfRangeException)
            {
            }
        }
        /// <summary>
        /// A thumbnail preview tab was clicked.  Copy that item's data to the clipboard.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.WindowsAPICodePack.Taskbar.TabbedThumbnailEventArgs"/> instance containing the event data.</param>
        private void thumbnail_TabbedThumbnailActivated(object sender, TabbedThumbnailEventArgs e)
        {
            var index = FlowPanel.Controls.IndexOf(_thumbnails[e.TabbedThumbnail]);
            Copy(index);
        }
        /// <summary>
        /// A thumbnail preview tab was closed.  Remove that item from memory.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.WindowsAPICodePack.Taskbar.TabbedThumbnailEventArgs"/> instance containing the event data.</param>
        private void thumbnail_TabbedThumbnailClosed(object sender, TabbedThumbnailEventArgs e)
        {
            var control = _thumbnails[e.TabbedThumbnail];

            var index = FlowPanel.Controls.IndexOf(control);
            _clipboardManager.Remove(index);
        }
        /// <summary>
        /// Tells the clipboard manager that new clipboard contents are available.
        /// </summary>
        private void UpdateClipboard()
        {
            // Don't enter until the window is loaded and activated.
            if (!_loaded)
            {
                return;
            }

            lock (this)
            {
                _clipboardManager.UpdateClipboard();
            }
        }
        /// <summary>
        /// Copies the selected item's original contents back into the Windows clipboard.
        /// </summary>
        /// <param name="index">The index.</param>
        private void Copy(int index)
        {
            _copying = true;

            _clipboardManager.Copy(index);

            _copying = false;
        }
        /// <summary>
        /// Sets the preview picture box to the placeholder image.
        /// </summary>
        private void SetEmptyPreview()
        {
            // Set the application's taskbar preview to render only the area bounded by the 
            // preview picture box.  This is how other applications render content like video
            // clips or browser tabs without rendering the rest of the application's window.
            var pictureboxBounds = new Rectangle(630, 0, 150, 150);
            TaskbarManager.Instance.TabbedThumbnail.SetThumbnailClip(Handle, pictureboxBounds);
        }
        /// <summary>
        /// Handles the Shown event of the Form1 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Form1_Shown(object sender, EventArgs e)
        {
            // HACK: force the screen to redraw so the preview image is available
            // for thumbnail rendering when the form loads.
            Height += 1;

            SetEmptyPreview();
            ChangeMode();
        }
        /// <summary>
        /// Creates a taskbar jumplist.  Jump lists need to be recreated each time a command is invoked.
        /// </summary>
        private void CreateJumplist()
        {
            _jumpList = JumpList.CreateJumpList();

            // Don't display the "Frequent" or "Recent" documents categories
            _jumpList.KnownCategoryToDisplay = JumpListKnownCategoryType.Neither;

            // The jump list link's IconReference needs the icon's physical file path
            var path = Path.GetDirectoryName(Application.ExecutablePath);

            var text = _mode == RenderMode.Auto
                           ? Resources.Resources.PagedPreviewText
                           : Resources.Resources.AutoPreviewText;

            // Add the jump list link with a command of -1.  That command, when clicked, is passed in
            // to the exe as a command line agrument.
            _jumpList.AddUserTasks(new JumpListLink(Assembly.GetExecutingAssembly().Location, text)
            {
                Arguments = ModeCommandArgument,
                IconReference = new IconReference(Path.Combine(path, "CopyIcon.ico"), 0)
            });

            // Same thing, but a command to clear all clipboard items
            _jumpList.AddUserTasks(new JumpListLink(Assembly.GetExecutingAssembly().Location, "Clear Contents")
            {
                Arguments = ClearCommandArgument,
                IconReference = new IconReference(Path.Combine(path, "CopyIcon.ico"), 0)
            });

            // Calling refresh displays the jumt list links
            _jumpList.Refresh();
        }
        /// <summary>
        /// Handles the Click event of the exitToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }
        /// <summary>
        /// Toggles between scrolling mode and auto mode.  Scrolling mode displays one preview image at
        /// a time with toolbar buttons.  Auto mode displays one preview thumbnail per clipboard item.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void usePagedPreviewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ChangeMode();
        }
        /// <summary>
        /// Handles the Click event of the clearToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void clearToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ClearContents();
        }
    }
}

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)

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 | Mobile
Web03 | 2.8.140721.1 | Last Updated 17 Dec 2009
Article Copyright 2009 by TylerBrinks
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid