C#中如何在任务栏显示Windows 7进度条?

77

如果你注意到在Windows 7 beta中,当你复制文件或进行其他系统操作时,任务栏上的Windows资源管理器图标会填充一个类似于表单进度条的绿色进度条。那么,在我的C#表单中,是否有一种方法可以强制我的任务栏进度条与我正在执行的任何任务的进度匹配?转换、传输、生成等都可以使用这个进度条。

7个回答

91

我只是想在我的WinForms应用程序中添加一些任务栏进度动画,而不必下载代码包或切换到WPF以使用TaskbarItemInfo。

解决方案是使用ITaskbarList3接口的类:

using System;
using System.Runtime.InteropServices;

public static class TaskbarProgress
{
    public enum TaskbarStates
    {
        NoProgress    = 0,
        Indeterminate = 0x1,
        Normal        = 0x2,
        Error         = 0x4,
        Paused        = 0x8
    }

    [ComImport()]
    [Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface ITaskbarList3
    {
        // ITaskbarList
        [PreserveSig]
        void HrInit();
        [PreserveSig]
        void AddTab(IntPtr hwnd);
        [PreserveSig]
        void DeleteTab(IntPtr hwnd);
        [PreserveSig]
        void ActivateTab(IntPtr hwnd);
        [PreserveSig]
        void SetActiveAlt(IntPtr hwnd);

        // ITaskbarList2
        [PreserveSig]
        void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);

        // ITaskbarList3
        [PreserveSig]
        void SetProgressValue(IntPtr hwnd, UInt64 ullCompleted, UInt64 ullTotal);
        [PreserveSig]
        void SetProgressState(IntPtr hwnd, TaskbarStates state);
    }

    [ComImport()]    
    [Guid("56fdf344-fd6d-11d0-958a-006097c9a090")]
    [ClassInterface(ClassInterfaceType.None)]
    private class TaskbarInstance
    {
    }

    private static ITaskbarList3 taskbarInstance = (ITaskbarList3)new TaskbarInstance();
    private static bool taskbarSupported = Environment.OSVersion.Version >= new Version(6, 1);

    public static void SetState(IntPtr windowHandle, TaskbarStates taskbarState)
    {
        if (taskbarSupported) taskbarInstance.SetProgressState(windowHandle, taskbarState);
    }

    public static void SetValue(IntPtr windowHandle, double progressValue, double progressMax)
    {
        if (taskbarSupported) taskbarInstance.SetProgressValue(windowHandle, (ulong)progressValue, (ulong)progressMax);
    }
}

使用起来非常简单的示例:

TaskbarProgress.SetState(this.Handle, TaskbarProgress.TaskbarStates.Indeterminate);

or

TaskbarProgress.SetValue(this.Handle, 50, 100);
TaskbarProgress.SetState(this.Handle, TaskbarProgress.TaskbarStates.Error);

5
这是一个非常棒的答案;没有外部依赖或下载,而且只需要运行。 - Ben
1
如果您想像我一样添加SetOverlayIcon,请确保按照正确的顺序添加所有其他方法。请参阅http://stackoverflow.com/questions/1024786/windows-7-taskbar-setoverlayicon-from-wpf-app-doesnt-work的被接受答案。 - Todd Myhre
1
不幸的是,在Windows XP上运行时,这会破坏应用程序!有什么解决方案吗? - Kevin Vuilleumier
6
解决方法是确保您不在XP中这样做。 - Chibueze Opata
@KevinVuilleumier 真正的解决方案是在运行代码之前检查Windows版本。 - user5395084

54

对于那些想跳过阅读文档直接使用的人...

  • 下载Microsoft .Net Framework的Windows API Code Pack
  • 运行安装程序并创建一个zip文件
  • 从zip文件的二进制文件夹中提取以下dll文件:
    • Microsoft.WindowsAPICodePack.dll
    • Microsoft.WindowsAPICodePack.Shell.dll
  • 在您的项目中添加对它们的引用
  • 将以下代码放入您的应用程序并根据需要进行修改:

--

  int max = 100;
  var prog = Microsoft.WindowsAPICodePack.Taskbar.TaskbarManager.Instance;
  prog.SetProgressState(Microsoft.WindowsAPICodePack.Taskbar.TaskbarProgressBarState.Normal);
  for(int i=0;i<max;i++) {
     prog.SetProgressValue(i, max);
     Thread.Sleep(100);     
  }
  prog.SetProgressState(Microsoft.WindowsAPICodePack.Taskbar.TaskbarProgressBarState.NoProgress);

Nuget的下载有点烦人,但是没错,它很好用。我已经在我的进度条处理类中实现了它 :) - Nyerguds
@Nyerguds:Nuget下载有什么烦人的地方吗?只需将命令复制并粘贴到Nuget控制台中,就完成了。 - Jack
这意味着需要下载和安装另一个东西...即NuGet。 - Nyerguds
我在使用Microsoft的WindowsAPICodePack时遇到了麻烦(由于需要强命名库,我的应用程序在启动时崩溃)。 一个非官方的nuget包https://www.nuget.org/packages/Microsoft.WindowsAPICodePack.Shell/对我来说效果更好。 - kibitzerCZ

39

查看适用于Microsoft .Net Framework的Windows API Code Pack。

您可以访问此链接


2
甚至还有一个关于这个主题的精美视频。 - Larsenal
1
需要 .NET 3.5 或 4。似乎不适用于 .NET 2。 - Uwe Keim
根据http://en.wikipedia.org/wiki/.NET_Framework的说明,3.5版本与Windows 7一起分发。 - jocull
包含这个会在XP上运行时破坏应用程序吗?(已安装.NET 3.5 / 4.0) - DLeh

5

4

2

已在Windows 10.0.19044下测试通过

您只需要在表单中添加调用新表单属性的代码,即可更新图标。(为什么要在表单中添加API调用、过程和枚举,如果可以避免呢?)

将以下代码放入项目的新类模块中。

using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;

namespace System.Windows.Forms
{
    [ToolboxBitmap(typeof(Form))]
    public class ProgressForm : Form
    {
        private ThumbnailProgressState m_State = ThumbnailProgressState.NoProgress;
        private int m_Maximum = 100;
        private int m_Value = 0;

        //
        // Summary:
        //     Gets or sets the state in which progress should be indicated on the task
        //     bar.
        //
        // Returns:
        //     One of the System.Windows.Forms.ThumbnailProgressState values. The default is System.Windows.Forms.ThumbnailProgressState.NoProgress
        //
        // Exceptions:
        //   T:System.ComponentModel.InvalidEnumArgumentException:
        //     The value is not a member of the System.Windows.Forms.ThumbnailProgressState enumeration.
        //
        // History:
        //   An old Microsoft article
        //   July 2022 - Verified with Win10, reworded/reformatted ThumbnailProgressState.
        //   Argument exception of property Value is more granular.
        //   Windows7orGreater - no longer relevant, but unchanged in case there is a need to distinguish versions beyond Windows 10
        //   
        // Usage:
        //   Add class with this code to your project
        //   Add the inheritance "ProgressForm" to the form that will show progress.
        //   Example: public partial class Form1 : ProgressForm
        //   
        [Browsable(true)]
        [DefaultValue(ThumbnailProgressState.NoProgress)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public ThumbnailProgressState State
        {
            get { return m_State; }
            set
            {
                switch (value)
                {
                    case ThumbnailProgressState.NoProgress:
                    case ThumbnailProgressState.Indeterminate:
                    case ThumbnailProgressState.Normal:
                    case ThumbnailProgressState.Error:
                    case ThumbnailProgressState.Paused:
                        m_State = value;
                        OnStateChanged(new EventArgs());
                        break;
                    default:
                        throw new InvalidEnumArgumentException("The value is not a member of the System.Windows.Forms.ThumbnailProgressState enumeration.");
                }
            }
        }

        //
        // Summary:
        //     Gets or sets the current position of the progress bar.
        //
        // Returns:
        //     The position within the range of the progress bar. The default is 0.
        //
        // Exceptions:
        //   T:System.ArgumentException:
        //   • The value specified is greater than the value of the System.Windows.Forms.ProgressForm.Maximum property.
        //   • The value specified is less than 0.
        [Browsable(true)]
        [DefaultValue(0)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public int Value
        {
            get { return m_Value; }
            set
            {
                if (value > m_Maximum)
                {
                    throw new ArgumentException("The value specified is greater than the value of the System.Windows.Forms.ProgressForm.Maximum property.");
                }
                else if(value < 0)
                {
                    throw new ArgumentException("The value specified is less than 0.");
                }
                else
                {
                    m_Value = value;
                    OnValueChanged(new EventArgs());
                }
            }
        }

        //
        // Summary:
        //     Gets or sets the maximum value of the range of the control.
        //
        // Returns:
        //     The maximum value of the range. The default is 100.
        //
        // Exceptions:
        //   T:System.ArgumentException:
        //     The value specified is less than 0.
        [Browsable(true)]
        [DefaultValue(100)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public int Maximum
        {
            get { return m_Maximum; }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentException("The value specified is less than 0.");
                }
                else
                {
                    m_Maximum = value;
                    if (value < m_Value) m_Value = value;
                    OnMaximumChanged(new EventArgs());
                }
            }
        }

        protected virtual void OnStateChanged(EventArgs e)
        {
            if (Windows7orGreater) SetProgressState();
        }

        protected virtual void OnValueChanged(EventArgs e)
        {
            if (Windows7orGreater) SetProgressValue();
        }

        protected virtual void OnMaximumChanged(EventArgs e)
        {
            if (Windows7orGreater) SetProgressValue();
        }

        protected override void WndProc(ref Message m)
        {
            if (Windows7orGreater)
            {
                // if taskbar button created or recreated, update progress status
                if (m.Msg == WM_TaskbarButtonCreated) SetProgressState();
            }
            base.WndProc(ref m);
        }

        private void SetProgressState()
        {
            // must be Windows7orGreater
            TaskbarList.SetProgressState(Handle, m_State);
            SetProgressValue();
        }
        private void SetProgressValue()
        {

            switch (m_State)
            {
                case ThumbnailProgressState.Normal:
                case ThumbnailProgressState.Error:
                case ThumbnailProgressState.Paused:
                    TaskbarList.SetProgressValue(Handle, (ulong)m_Value, (ulong)m_Maximum);
                    break;
            }
        }

        private static int WM_TaskbarButtonCreated = -1;
        private static int _winVersion = -1;
        internal static bool Windows7orGreater
        {
            get
            {
                if (_winVersion < 0)
                {
                    Version osVersion = Environment.OSVersion.Version;
                    if ((osVersion.Major == 6 && osVersion.Minor > 0) || (osVersion.Major > 6))
                    {
                        // Taskbar progress indicator requires Windows 7 Or Greater
                        _winVersion = 1;

                        // register taskbar creation window message
                        WM_TaskbarButtonCreated = RegisterWindowMessage(@"TaskbarButtonCreated");
                    }
                    else
                    {
                        _winVersion = 0;
                    }
                }
                return (_winVersion > 0);
            }
        }

        private static ITaskbarList3 _taskbarList = null;
        internal static ITaskbarList3 TaskbarList
        {
            get
            {
                if (_taskbarList == null)
                {
                    lock (typeof(ProgressForm))
                    {
                        if (_taskbarList == null)
                        {
                            _taskbarList = (ITaskbarList3)new CTaskbarList();
                            _taskbarList.HrInit();
                        }
                    }
                }
                return _taskbarList;
            }
        }

        [DllImport("user32.dll")]
        internal static extern int RegisterWindowMessage(string message);

        [ComImportAttribute()]
        [GuidAttribute("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
        [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface ITaskbarList3
        {
            // ITaskbarList
            [PreserveSig]
            void HrInit();
            [PreserveSig]
            void AddTab(IntPtr hwnd);
            [PreserveSig]
            void DeleteTab(IntPtr hwnd);
            [PreserveSig]
            void ActivateTab(IntPtr hwnd);
            [PreserveSig]
            void SetActiveAlt(IntPtr hwnd);

            // ITaskbarList2
            [PreserveSig]
            void MarkFullscreenWindow(
              IntPtr hwnd,
              [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);

            // ITaskbarList3
            void SetProgressValue(IntPtr hwnd, UInt64 ullCompleted, UInt64 ullTotal);
            void SetProgressState(IntPtr hwnd, ThumbnailProgressState tbpFlags);
        }

        [GuidAttribute("56FDF344-FD6D-11d0-958A-006097C9A090")]
        [ClassInterfaceAttribute(ClassInterfaceType.None)]
        [ComImportAttribute()]
        internal class CTaskbarList { }
    }

    public enum ThumbnailProgressState
    {
        /// <summary>
        /// No progress is displayed.<br>
        /// yourFormName.Value is ignored.</br> </summary>
        NoProgress = 0,
        /// <summary>
        /// Normal progress is displayed.<br>
        /// The bar is GREEN.</br> </summary>
        Normal = 0x2,
        /// <summary>
        /// The operation is paused.<br>
        /// The bar is YELLOW.</br></summary>
        Paused = 0x8,
        /// <summary>
        /// An error occurred.<br>
        /// The bar is RED.</br> </summary>
        Error = 0x4,
        /// <summary>
        /// The progress is indeterminate.<br>
        /// Marquee style bar (constant scroll).</br> </summary>
        Indeterminate = 0x1
    }
}

ProgressForm 添加到需要显示进度的表单中:
public partial class Form1 : ProgressForm

将以下示例代码添加到您的表单中,并逐步执行以观察其工作:
    if (this.State == ThumbnailProgressState.NoProgress)
    {
        // Show the progress with GREEN
        this.State = ThumbnailProgressState.Normal;
    }
    this.Maximum = 512; // set to any integer above zero (defaults to 100)
    this.Value = 0;     // Visually equivalent to this.State = ThumbnailProgressState.NoProgress;
    this.Value = 256;   // 50% solid GREEN overlay
    this.State = ThumbnailProgressState.Error;  // Still 50% but now solid RED overlay
    this.State = ThumbnailProgressState.Paused; // Still 50% but now solid YELLOW overlay
    this.Value = 384;   // 75% YELLOW overlay (384 / 512 == 75%)
    this.Value = this.Maximum;  // 100% YELLOW overlay (512 of 512)
    this.State = ThumbnailProgressState.Indeterminate;  // Marquee. (Ignores Value and constantly scrolls with a fade)
    this.State = ThumbnailProgressState.NoProgress;     // Ignores Value, there is no overlay

0
我找到了一篇很好的文章(链接),它提供了一个简单的解决方案来解决任务栏进度条问题。简而言之,它指导你从MSDN网站下载Windows API包{{link2:},添加对其中包含的Microsoft.WindowsAPICodePack.Shell.dll的引用,最后在你的应用程序中添加三行代码:}

Imports Microsoft.WindowsAPICodePack
Imports Microsoft.WindowsAPICodePack.Taskbar
// ...
TaskbarManager.Instance.SetProgressValue(X, 100)

其中 X 是您想要显示的进度。


2
请注意:不鼓励仅提供链接的答案。在 Stack Overflow 上,答案应该是搜索解决方案的最终站点(而不是参考资料的另一个中转站,这种参考资料随着时间的推移往往会变得过时)。请考虑在此处添加独立的摘要,并将链接保留为参考资料。 - kleopatra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接