如何在C#.NET 3.5中更改Progressbar的颜色?


Answers:


82

由于先前的答案似乎不适用于“视觉样式”。您可能需要创建自己的类或扩展进度栏:

public class NewProgressBar : ProgressBar
{
    public NewProgressBar()
    {
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        Rectangle rec = e.ClipRectangle;

        rec.Width = (int)(rec.Width * ((double)Value / Maximum)) - 4;
        if(ProgressBarRenderer.IsSupported)
           ProgressBarRenderer.DrawHorizontalBar(e.Graphics, e.ClipRectangle);
        rec.Height = rec.Height - 4;
        e.Graphics.FillRectangle(Brushes.Red, 2, 2, rec.Width, rec.Height);
    }
}

编辑:更新的代码以使进度条使用背景的视觉样式


2
为此,我可以在解决方案中使用它。
欧文,

真的很酷,谢谢。它确实帮助了我目前正在处理的事情,尽管我将其更改为每次都重新绘制整个控件,而不仅仅是e.ClipRectangle指定的区域。否则,只有部分控件被另一个窗口或屏幕边缘无效后,它才能正确地重新绘制。
马特·布莱恩

@马特·布莱恩(Matt Blaine):您能否将您的修改发布为答案的修改?我相信会有足够的人对您的完整解决方案感兴趣。
Marek '01

@Marek刚注意到您的评论。我没有足够的代表来编辑答案,所以我发表了自己的答案。谢谢。stackoverflow.com/questions/778678/…–
马特·布莱恩

3
我意识到这很老了;而且我意识到这是个巧合(因为Minimum几乎总是零);但是宽度比例因子应为(((double)Value-(double)Minimum)/((double)Maximum-(double)Minimum))以保持肛门正确。:)
Jesse Chisholm 2014年

121

好的,我花了一些时间阅读所有答案和链接。这是我从他们那里得到的:

样品结果

接受的答案将禁用视觉样式,但确实允许您将颜色设置为所需的任何颜色,但结果看起来很简单:

在此处输入图片说明

使用以下方法,您可以得到如下所示的内容:

在此处输入图片说明

如何

首先,包括以下内容: using System.Runtime.InteropServices;

其次,您可以创建此新类,或将其代码放入现有的static非泛型类中:

public static class ModifyProgressBarColor
{
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
    static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr w, IntPtr l);
    public static void SetState(this ProgressBar pBar, int state)
    {
        SendMessage(pBar.Handle, 1040, (IntPtr)state, IntPtr.Zero);
    }
}

现在,要使用它,只需调用:

progressBar1.SetState(2);

注意SetState中的第二个参数,1 =正常(绿色);2 =错误(红色);3 =警告(黄色)。

希望能帮助到你!


18
您创建了扩展方法,因此调用应为progressBar1.SetState(2);。除此之外,很好的答案!
Edgar Hernandez 2014年

5
应该注意的是,这仅在Vista +上可用。您可以通过搜索PBM_SETSTATE来查看完整的文档。
2014年

美丽!谢谢
sapbucket 2015年

1
这太了不起了,但是它部分地打破了我的进度条,它永远不会填满100%....
Norbert Boros

1
public const uint PBM_SETSTATE = 0x0410; // 1040
安德烈·克拉苏斯基

37

这是最受接受的代码的无闪烁版本,您可以找到该问题的答案。所有这些都归功于这些致命答案的发布者。感谢Dusty,Chris,Matt和Josh!

就像其中一个评论中“ Fueled”的请求一样,我还需要一个表现得更...专业的版本。此代码与以前的代码一样保留样式,但是添加了屏幕外图像渲染和图形缓冲(并正确处理了图形对象)。

结果:一切正常,没有闪烁。:)

public class NewProgressBar : ProgressBar
{
    public NewProgressBar()
    {
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
        // None... Helps control the flicker.
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        const int inset = 2; // A single inset value to control teh sizing of the inner rect.

        using (Image offscreenImage = new Bitmap(this.Width, this.Height))
        {
            using (Graphics offscreen = Graphics.FromImage(offscreenImage))
            {
                Rectangle rect = new Rectangle(0, 0, this.Width, this.Height);

                if (ProgressBarRenderer.IsSupported)
                    ProgressBarRenderer.DrawHorizontalBar(offscreen, rect);

                rect.Inflate(new Size(-inset, -inset)); // Deflate inner rect.
                rect.Width = (int)(rect.Width * ((double)this.Value / this.Maximum));
                if (rect.Width == 0) rect.Width = 1; // Can't draw rec with width of 0.

                LinearGradientBrush brush = new LinearGradientBrush(rect, this.BackColor, this.ForeColor, LinearGradientMode.Vertical);
                offscreen.FillRectangle(brush, inset, inset, rect.Width, rect.Height);

                e.Graphics.DrawImage(offscreenImage, 0, 0);
            }
        }
    }
}

好吧,这是最接近Microsoft的原始实现。
YakupÜnyılmaz2013年

1
拥有一个using块调用Dispose实际上有点多余。很好的答案。
凯文·史翠克

1
我意识到这很老了;而且我意识到这是个巧合(因为Minimum几乎总是零);但是宽度比例因子应为(((double)Value-(double)Minimum)/((double)Maximum-(double)Minimum))以保持肛门正确。:)
Jesse Chisholm 2014年

当我使用TextRenderer.DrawText()将文本绘制到offscreenimage中时,屏幕上的最终结果看起来像是像素化的,而不像使用完全相同的字体的标签。不使用offscreenimager将所有这些直接绘制到e.Graphics上可以得到正确的结果。
CuriousGeorge

1
是的,我确实尝试过缩放。最后,我只是切换到WPF,所以我需要的一切仅仅是内置的。
CuriousGeorge

28

在设计器中,您只需要将ForeColor属性设置为所需的任何颜色即可。对于红色,有一个预定义的颜色。

要在代码(C#)中执行此操作,请执行以下操作:

pgs.ForeColor = Color.Red;

编辑:是的,还将样式设置为连续。在代码中,如下所示:

pgs.Style = System.Windows.Forms.ProgressBarStyle.Continuous;

另一个修改:您还需要删除以下内容:Application.EnableVisualStyles()从您的Program.cs(或类似内容)。如果您因为希望应用程序的其余部分具有视觉样式而无法执行此操作,那么我建议您自己绘制控件或继续使用WPF,因为使用WPF可以很容易地做到这一点。您可以找到有关所有者的教程,在Codeplex上绘制进度条


3
前景色=红色; Backcolor =蓝色;风格=连续。一切都没有改变,就像我从未碰过它一样保持
不变

3
h ...好吧,我明白了。您需要禁用视觉样式,以便它可以与属性一起使用。可能使用您选择的主题绘画。在Program.cs中,有一行读取Application.EnableVisualStyles()。删除该行,这将起作用。否则,您将需要自己绘制控件。另一种选择是使用WPF(如果可能的话),因为它可以让您真正轻松地进行此类操作。
–ustyburwell

请给我更多有关“如何自己绘画”的信息。
伊万·普罗达诺夫

1
这是有关所有者绘制进度条的教程。这个过程太复杂了,无法发表评论。codeproject.com/KB/cpp/VistaProgressBar.aspx
dustyburwell

1
这是一个可耻的Application.EnableVisualStyles()是此解决方案的关键部分。不幸的是,这破坏了所有其他样式。
mr.user1065741 '08年

16

使用Matt Blaine和Chris Persichetti的答案,我创建了一个进度条,在允许无限的颜色选择的同时看起来更好一些(基本上我在Matt的解决方案中更改了一行):

ProgressBarEx:

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace QuantumConcepts.Common.Forms.UI.Controls
{
    public class ProgressBarEx : ProgressBar
    {
        public ProgressBarEx()
        {
            this.SetStyle(ControlStyles.UserPaint, true);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            LinearGradientBrush brush = null;
            Rectangle rec = new Rectangle(0, 0, this.Width, this.Height);
            double scaleFactor = (((double)Value - (double)Minimum) / ((double)Maximum - (double)Minimum));

            if (ProgressBarRenderer.IsSupported)
                ProgressBarRenderer.DrawHorizontalBar(e.Graphics, rec);

            rec.Width = (int)((rec.Width * scaleFactor) - 4);
            rec.Height -= 4;
            brush = new LinearGradientBrush(rec, this.ForeColor, this.BackColor, LinearGradientMode.Vertical);
            e.Graphics.FillRectangle(brush, 2, 2, rec.Width, rec.Height);
        }
    }
}

用法:

progressBar.ForeColor = Color.FromArgb(255, 0, 0);
progressBar.BackColor = Color.FromArgb(150, 0, 0);

结果

您可以使用任何喜欢的渐变!

下载

https://skydrive.live.com/?cid=0EDE5D21BDC5F270&id=EDE5D21BDC5F270%21160&sc=documents#


2
我意识到这很老了;而且我意识到这是个尼特(因为Minimum几乎总是零);但是宽度比例因子应为(((double)Value-(double)Minimum)/((double)Maximum-(double)Minimum))以保持肛门正确。:)
Jesse Chisholm 2014年

更新了带有您的建议的答案。
Josh M.

我知道它已经很旧了,但是-一旦LinearGradientBrush读取到Rec宽度为0 ,此代码一旦达到4就会出错。最简单的解决方法是不对代码中的4px进行“填充”,而是将其放入面板或带有填充物的面板(如果需要边框)并制作rec.Width = (int)((rec.Width * scaleFactor) - 4)rec.Width = (int)(rec.Width * scaleFactor) + 1
MiDri

14

修改了dustyburwell的答案。(我没有足够的代表自己编辑它。)像他的回答一样,它启用了“视觉样式”。您可以在任何窗体的设计视图中仅设置进度条的ForeColor属性。

using System;
using System.Windows.Forms;
using System.Drawing;

public class ProgressBarEx : ProgressBar
{
    private SolidBrush brush = null;

    public ProgressBarEx()
    {
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        if (brush == null || brush.Color != this.ForeColor)
            brush = new SolidBrush(this.ForeColor);

        Rectangle rec = new Rectangle(0, 0, this.Width, this.Height);
        if (ProgressBarRenderer.IsSupported)
            ProgressBarRenderer.DrawHorizontalBar(e.Graphics, rec);
        rec.Width = (int)(rec.Width * ((double)Value / Maximum)) - 4;
        rec.Height = rec.Height - 4;
        e.Graphics.FillRectangle(brush, 2, 2, rec.Width, rec.Height);
    }
}

这对于更改前景色非常有用,但会闪烁很多,而默认的ProgressBar控件则不会。任何想法如何解决这个问题?
加油

4
我已经找到了解决闪烁的方法:将双缓冲添加到用户定义的ProgressBar的ControlStyles中,例如:SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer,...)。
加油2010年

我意识到这很老了;而且我意识到这是个尼特(因为Minimum几乎总是零);但是宽度比例因子应为(((double)Value-(double)Minimum)/((double)Maximum-(double)Minimum))以保持肛门正确。:)
Jesse Chisholm 2014年

5

我只是将其放入静态类。

  const int WM_USER = 0x400;
  const int PBM_SETSTATE = WM_USER + 16;
  const int PBM_GETSTATE = WM_USER + 17;

  [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
  static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

  public enum ProgressBarStateEnum : int
  {
   Normal = 1,
   Error = 2,
   Paused = 3,
  }

  public static ProgressBarStateEnum GetState(this ProgressBar pBar)
  {
   return (ProgressBarStateEnum)(int)SendMessage(pBar.Handle, PBM_GETSTATE, IntPtr.Zero, IntPtr.Zero);
  }

  public static void SetState(this ProgressBar pBar, ProgressBarStateEnum state)
  {
   SendMessage(pBar.Handle, PBM_SETSTATE, (IntPtr)state, IntPtr.Zero);
  } 

希望能帮助到你,

马克


+1:我认为这就是大多数人所说的红色。使它显示错误状态。
量子

3

通常,进度条是主题主题或尊重用户的颜色偏好。因此,要更改颜色,您要么需要关闭视觉样式并设置ForeColor自己或绘制控件。

至于连续样式而不是块,您可以设置Style属性:

pBar.Style = ProgressBarStyle.Continuous;

2

编辑

听起来,您正在使用XP主题,该主题具有基于绿色块的编排。尝试将UI样式翻转到Windows Classic并再次进行测试,但是您可能需要实现自己的OnPaint事件,以使其在所有UI样式中都能完成所需的工作

或者像其他人指出的那样,为您的应用程序禁用VisualStyles。

原版的

据我所知,进度栏的渲染与您选择的Windows主题样式(win2K,xp,vista)是一致的

您可以通过设置属性来更改颜色

ProgressBar.ForeColor

我不确定您还能做更多...

做一些谷歌搜索

MS KB这里有一篇有关创建“平滑”进度条的文章

http://support.microsoft.com/kb/323116


@ Eion,Forecolor = red; Backcolor =蓝色;风格=连续。一切都没有改变,就像我从未碰过它一样。
伊万·普罗达诺夫


2

所有这些方法都对我不起作用,但是该方法允许您将其更改为颜色字符串。

请注意,我在StackOverflow上的其他地方找到了此代码,并对其进行了一些更改。从那以后,我忘记了在哪里找到此代码,因此无法链接它,对此感到抱歉。

但是无论如何,我希望这段代码对确实对我有帮助的人有所帮助。

private void ProgressBar_MouseDown(object sender, MouseButtonEventArgs e)
    {
        var converter = new System.Windows.Media.BrushConverter();
        var brush = (Brush)converter.ConvertFromString("#FFB6D301");
        ProgressBar.Foreground = brush;
    }

如果使用名称“ ProgressBar”,请替换为您自己的进度条名称。您也可以使用其他参数触发该事件,只需确保其内括号在某处即可。


不错的工作..我正在使用ProgressChanged事件内部从浅到深... + 1
BENN1TH 2016年

2

更改ColorValue(即时更改)

放在using System.Runtime.InteropServices;顶部...

致电 ColorBar.SetState(progressBar1, ColorBar.Color.Yellow, myValue);

我注意到,如果您更改条形的值(它的大小),则其颜色不是默认绿色时也不会改变。我使用了user1032613的代码,并添加了Value选项。

public static class ColorBar
{
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
    static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr w, IntPtr l);
    public enum Color { None, Green, Red, Yellow }

    public static void SetState(this ProgressBar pBar, Color newColor, int newValue)
    {
        if (pBar.Value == pBar.Minimum)  // If it has not been painted yet, paint the whole thing using defualt color...
        {                                // Max move is instant and this keeps the initial move from going out slowly 
            pBar.Value = pBar.Maximum;   // in wrong color on first painting
            SendMessage(pBar.Handle, 1040, (IntPtr)(int)Color.Green, IntPtr.Zero);
        }
        pBar.Value = newValue;
        SendMessage(pBar.Handle, 1040, (IntPtr)(int)Color.Green, IntPtr.Zero);     // run it out to the correct spot in default
        SendMessage(pBar.Handle, 1040, (IntPtr)(int)newColor, IntPtr.Zero);        // now turn it the correct color
    }

}

2
我们不得不翻转pBar.Value = pBar.Maximum;SendMessage(pBar.Handle, 1040, (IntPtr)(int)Color.Green, IntPtr.Zero);条件,身体内的以显示正确的新颜色的进展。
DomTheDeveloper

1

以防万一有人在寻找其他选择...。您可以扩展面板,将其用作背景(白色或其他),在其中添加另一个面板作为前景(移动条)。然后,您可以完全控制更改颜色等。


1

只需在Visual Basic解决方案资源管理器(您的vb文件所在的位置)上右键单击您的项目,然后从菜单中选择属性。在弹出的窗口中,取消选择“启用XP视觉样式”,现在当您设置前景色时,它应该可以正常工作了。


0

回答:Quote:通常,进度条是主题主题或尊重用户的颜色偏好。因此,要更改颜色,您需要关闭视觉样式并ForeColor自己设置或绘制控件。

至于连续样式而不是块,您可以设置Style属性:

pBar.Style = ProgressBarStyle.Continuous;

启用VistualStyles后,ProgressBarStyle.Continuous与Blocks无效。

块将仅在禁用视觉样式的情况下工作……这将使所有这些成为一个争论点(关于自定义进度颜色)在禁用现有样式的情况下……进度条应基于前景色进行着色。

我结合使用了William Daniel的答案(启用了视觉样式,因此ForeColor不会只是平坦而没有样式)和Barry的答案(用于在进度栏上自定义文本)结合使用:如何在ProgressBar上放置文本?


0

垂直条UP向下为红色:

using System;
using System.Windows.Forms;
using System.Drawing;



public class VerticalProgressBar : ProgressBar
{


    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.Style |= 0x04;
            return cp;

        }
    }
    private SolidBrush brush = null;

    public VerticalProgressBar()
    {
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        if (brush == null || brush.Color != this.ForeColor)
            brush = new SolidBrush(this.ForeColor);

        Rectangle rec = new Rectangle(0, 0, this.Width, this.Height);
        if (ProgressBarRenderer.IsSupported)
        ProgressBarRenderer.DrawVerticalBar(e.Graphics, rec);
        rec.Height = (int)(rec.Height * ((double)Value / Maximum)) - 4;
        rec.Width = rec.Width - 4;
        e.Graphics.FillRectangle(brush, 2, 2, rec.Width, rec.Height);

    } 
}

您能在文本中还是在代码注释中解释这是如何解决问题的?
哈里森

我意识到这是个巧合(因为Minimum几乎总是零);但高度比例因子应为(((double)Value-(double)Minimum)/((double)Maximum-(double)Minimum))以保持肛门。:)
Jesse Chisholm'9

0

尊重WXP Visual Styles答案的VB.Net彩色进度条是...

我从12/3/17的“ user1032613”开始回答。请注意,这现在是一个模块,而不是一个类。从那里我转换了代码,但还需要更多。特别是,转换后的代码显示了DirectCast函数,用于将“状态”整数转换为无效的IntPtr类型。

Imports System.Runtime.InteropServices

Public Module ModifyProgressBarColor

    Private Declare Function SendMessage Lib "User32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=False)> _
    Private Function SendMessage(hWnd As IntPtr, Msg As UInteger, w As IntPtr, l As IntPtr) As IntPtr
    End Function

    <System.Runtime.CompilerServices.Extension()> _
    Public Sub SetState(pBar As ProgressBar, state As Integer)

        '-- Convert state as integer to type IntPtr
        Dim s As IntPtr
        Dim y As Integer = state
        s = IntPtr.op_Explicit(y)

        '-- Modify bar color
        SendMessage(pBar.Handle, 1040, s, IntPtr.Zero)

    End Sub

End Module

再次在此行的使用代码中调用此命令:

Call ModifyProgressBarColor.SetState(prb, 2)

顺便说一句-我尝试了其他颜色-0、4、5-它们都只是显示为绿色。


0

我知道它的方法已经太老了,现在无法回答。但是,仍然要对@进行一些细微调整 Daniel的的答案进行以纠正不显示零值进度条的问题。仅在发现内部矩形的宽度不为零时才绘制进度。

感谢所有贡献者。

        public class ProgressBarEx : ProgressBar
        {
            public ProgressBarEx()
            {
                this.SetStyle(ControlStyles.UserPaint, true);
            }

            protected override void OnPaintBackground(PaintEventArgs pevent){}
                // None... Helps control the flicker.                

            protected override void OnPaint(PaintEventArgs e)
            {
                const int inset = 2; // A single inset value to control teh sizing of the inner rect.

                using (Image offscreenImage = new Bitmap(this.Width, this.Height))
                {
                    using (Graphics offscreen = Graphics.FromImage(offscreenImage))
                    {
                        Rectangle rect = new Rectangle(0, 0, this.Width, this.Height);

                        if (ProgressBarRenderer.IsSupported)
                            ProgressBarRenderer.DrawHorizontalBar(offscreen, rect);

                        rect.Inflate(new Size(-inset, -inset)); // Deflate inner rect.
                        rect.Width = (int)(rect.Width * ((double)this.Value / this.Maximum));

                        if (rect.Width != 0)
                        {
                            LinearGradientBrush brush = new LinearGradientBrush(rect, this.ForeColor, this.BackColor, LinearGradientMode.Vertical);
                            offscreen.FillRectangle(brush, inset, inset, rect.Width, rect.Height);
                            e.Graphics.DrawImage(offscreenImage, 0, 0);
                            offscreenImage.Dispose();
                        }
                    }
                }
            }
        }

0

我发现可以通过在进度条内部绘制一个矩形并根据进度的当前值设置其宽度来完成此操作。我还添加了从右到左进度的支持。这样,您就不需要使用Image,并且由于Rectnalge.Inflate不会被调用,因此绘制的矩形会更小。

public partial class CFProgressBar : ProgressBar
{
    public CFProgressBar()
    {
        InitializeComponent();
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnPaintBackground(PaintEventArgs pevent) { }

    protected override void OnPaint(PaintEventArgs e)
    {
        double scaleFactor = (((double)Value - (double)Minimum) / ((double)Maximum - (double)Minimum));
        int currentWidth = (int)((double)Width * scaleFactor);
        Rectangle rect;
        if (this.RightToLeftLayout)
        {
            int currentX = Width - currentWidth;
            rect = new Rectangle(currentX, 0, this.Width, this.Height);
        }
        else
            rect = new Rectangle(0, 0, currentWidth, this.Height);

        if (rect.Width != 0)
        {
            SolidBrush sBrush = new SolidBrush(ForeColor);
            e.Graphics.FillRectangle(sBrush, rect);
        }
    }
}

0

我认为,所有解决方案中最简单的解决方案只是一个快速解决方案,但是您可以从Program.cs中删除Application.EnableVisualStyles()并对其添加注释,也可以将包含Main函数的部分命名为。之后,您可以通过progressBar.ForeColor = Color.TheColorYouDesire;从进度条自由更改颜色。

static void Main()
        {
            //Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
         }

-5

编辑:在两个分钟中,我花了点时间启动vs并检查了我被打败的语法,并给出了更好的响应。我喜欢这个网站。

        progressBar1 = new ProgressBar();
        progressBar1.ForeColor = Color.Red;

2
不起作用。它现在仍然为空,我添加了progressBar1.value = 50,但仍然为空
Ivan Prodanov,

尝试设置最大值?progressBar1 =新的ProgressBar(); progressBar1.ForeColor = Color.Red; progressBar1.Maximum = 100;
Fusspawn

2
不,问题出在我的Windows主题中。
伊万·普罗达诺夫

不知道这是否可行,但是尝试注释掉Application.EnableVisualThemes();。那是在一个预建文件vs生成文件中(我不记得是哪个文件)
Fusspawn

1
Application.EnableVisualStyles()对不起,主题不对
Fusspawn
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.