ProgressBarコントロールのバーの色は通常緑色で、ForeColorとBackColorプロパティの値は無視されます。ここでは、ProgressBarコントロールの色を変更する方法を幾つか紹介します。
アプリケーションのvisualスタイルを無効にすると、ForeColorとBackColorプロパティで指定した色でProgressBarコントロールが表示されるようになります。アプリケーションのvisualスタイルを無効にする方法は「コントロールの外観をビジュアルスタイル(XPスタイル)にする」で説明していますので、そちらを参考にして無効にしてください。通常は、エントリポイントメソッドのMainメソッドで呼び出している「Application.EnableVisualStyles」を削除することでvisualスタイルが無効になるでしょう。
補足:visualスタイルを無効にするとProgressBar.StyleプロパティをProgressBarStyle.Marqueeにしてもマーキースタイルになりません。
補足:「Application.EnableVisualStyles」がどこにあるかどうしても分からない場合は、「Application.EnableVisualStyles」という文字列を検索して探してください。
下の画像は、visualスタイルを無効にして、ForeColorプロパティをColor.Blue、BackColorプロパティをColor.Yellowにした時のProgressBarです。このように、visualスタイルを無効にすると見た目は悪くなります。
補足:バーを点線ではなく連続した線にしたい場合は、ProgressBar.StyleプロパティをProgressBarStyle.Continuousにします。
WindowsメッセージPBM_SETSTATEを使うことで、プログレスバーの状態をエラーや一時停止(ポーズ)にすることができます。ProgressBarの状態をエラーにするとバーは赤っぽい色になり、一時停止にすると黄色っぽい色になります。
下の図で、上のプログレスバーがエラーで、下が一時停止の状態です。
補足:「Progress Bars」にあるガイドラインによると、赤と黄色のプログレスバーは進行中の処理が中断していることを示すために使うようです。ユーザーによって回復が可能な中断の場合はプログレスバーを赤くします。ユーザーによって一時停止されていたり、何らかの理由(ネットワーク状態が悪いなど)で進行が妨げられている場合はプログレスバーを黄色くします。処理が中断し、回復できない場合は、プログレスバーは緑のままで、エラーメッセージを表示します。
補足:この方法でもマーキースタイルにはなりません。また、アニメ効果もありません。
この方法は、Windows Vista以降で有効です。
以下のコードでは、ProgressBar1の状態をエラーに、ProgressBar2の状態を一時停止にしています。
'Imports System.Windows.Forms 'Imports System.Runtime.InteropServices <DllImport("user32.dll", CharSet:=CharSet.Unicode)> _ Private Shared Function SendMessage(hWnd As HandleRef, _ Msg As UInt32, wParam As UInt32, lParam As IntPtr) As IntPtr End Function Private Const WM_USER As UInt32 = &H400 Private Const PBM_SETSTATE As UInt32 = WM_USER + 16 Private Const PBST_NORMAL As UInt32 = &H1 Private Const PBST_ERROR As UInt32 = &H2 Private Const PBST_PAUSED As UInt32 = &H3 Public Sub ChangeProgressState() 'ProgressBar1の状態をエラーにする If ProgressBar1.IsHandleCreated Then SendMessage(New HandleRef(ProgressBar1, ProgressBar1.Handle), _ PBM_SETSTATE, PBST_ERROR, IntPtr.Zero) End If 'ProgressBar2の状態を一時停止にする If ProgressBar2.IsHandleCreated Then SendMessage(New HandleRef(ProgressBar2, ProgressBar2.Handle), _ PBM_SETSTATE, PBST_PAUSED, IntPtr.Zero) End If End Sub
//using System; //using System.Windows.Forms; //using System.Runtime.InteropServices; [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, uint wParam, IntPtr lParam); private const uint WM_USER = 0x400; private const uint PBM_SETSTATE = WM_USER + 16; private const uint PBST_NORMAL = 0x0001; private const uint PBST_ERROR = 0x0002; private const uint PBST_PAUSED = 0x0003; public void ChangeProgressState() { //ProgressBar1の状態をエラーにする if (ProgressBar1.IsHandleCreated) { SendMessage(new HandleRef(ProgressBar1, ProgressBar1.Handle), PBM_SETSTATE, PBST_ERROR, IntPtr.Zero); } //ProgressBar2の状態を一時停止にする if (ProgressBar2.IsHandleCreated) { SendMessage(new HandleRef(ProgressBar2, ProgressBar2.Handle), PBM_SETSTATE, PBST_PAUSED, IntPtr.Zero); } }
私が試した限りでは(Windows 8)、このように状態を変更したプログレスバーを使用した時、ProgressBar.Valueプロパティの値が正しく表示に反映されませんでした。例えばProgressBarのValueプロパティを0から1にしても表示は0のままで、Valueプロパティを2にすると表示が1になりました。このようにValueプロパティに新たな値を設定すると、前の値でProgressBarが表示されるようです。
この対処法を探した所、「Windows Forms Aero」というコントロールのライブラリで提供されているProgressBarでは、ProgressBarを描画する時にProgressBarの状態をNormalに戻してから、ErrorやPausedにしているようでした。
この方法を参考にして、ProgressBar.Valueプロパティの値を変更した後にProgressBarの状態を一旦Normalにしてから戻してみると、正常に表示されるようになりました。しかしProgressBarの状態をNormalにした時、一瞬バーが緑になります。
以下にこの方法を使った例を示します。
'ProgressBar1の値を1つずつ増やす If ProgressBar1.Value = ProgressBar1.Maximum Then ProgressBar1.Value = ProgressBar1.Minimum Else ProgressBar1.Value += 1 End If 'ProgressBarをNormalにしてからPausedに戻す SendMessage(New HandleRef(ProgressBar1, ProgressBar1.Handle), _ PBM_SETSTATE, PBST_NORMAL, IntPtr.Zero) SendMessage(New HandleRef(ProgressBar1, ProgressBar1.Handle), _ PBM_SETSTATE, PBST_PAUSED, IntPtr.Zero)
//ProgressBar1の値を1つずつ増やす if (ProgressBar1.Value == ProgressBar1.Maximum) { ProgressBar1.Value = ProgressBar1.Minimum; } else { ProgressBar1.Value++; } //ProgressBarをNormalにしてからPausedに戻す SendMessage(new HandleRef(ProgressBar1, ProgressBar1.Handle), PBM_SETSTATE, PBST_NORMAL, IntPtr.Zero); SendMessage(new HandleRef(ProgressBar1, ProgressBar1.Handle), PBM_SETSTATE, PBST_PAUSED, IntPtr.Zero);
試行錯誤で、これとは別の方法も見つけました。それは、ProgressBar.Maximumプロパティを変更してすぐに戻すという方法です。これならば一瞬色が変わるということもありません。
'ProgressBar1の値を1つずつ増やす If ProgressBar1.Value = ProgressBar1.Maximum Then ProgressBar1.Value = ProgressBar1.Minimum Else ProgressBar1.Value += 1 End If 'Maximumを1増やして、戻す ProgressBar1.Maximum += 1 ProgressBar1.Maximum -= 1
//ProgressBar1の値を1つずつ増やす if (ProgressBar1.Value == ProgressBar1.Maximum) { ProgressBar1.Value = ProgressBar1.Minimum; } else { ProgressBar1.Value++; } //Maximumを1増やして、戻す ProgressBar1.Maximum++; ProgressBar1.Maximum--;
上記の方法で満足できない場合は、自分で描画(オーナードロー)することになります。
ProgressBarコントロールではPaintイベントが発生しません。Paintイベントが発生するようにするには、Control.SetStyleメソッドでControlStyles.UserPaintをTrueにします。
ProgressBarクラスのOnPaintメソッドをオーバーライドして、オーナードローしているプログレスバーの例を以下に示します。この例ではコードを簡単にするために、背景もバーも四角でベタ塗りしているだけです。
このプログレスバーの使い方は、通常のProgressBarとほぼ同じです。詳しくは、こちらをご覧ください。
このプログレスバーのBackColorをSilver、ForeColorをDodgerBlueにすると、以下の図のように表示されます。
Imports System.Drawing Imports System.Windows.Forms ''' <summary> ''' ForeColorとBackColorプロパティによって色を変えることができる ''' プログレスバーコントロールを表します。 ''' </summary> Public Class ColorProgressBar Inherits ProgressBar Public Sub New() 'Paintイベントが発生するようにする 'ダブルバッファリングを有効にする MyBase.SetStyle(ControlStyles.UserPaint Or _ ControlStyles.AllPaintingInWmPaint Or _ ControlStyles.OptimizedDoubleBuffer, True) End Sub Protected Overrides Sub OnPaint(e As PaintEventArgs) Dim backBrush As Brush = New SolidBrush(Me.BackColor) Dim foreBrush As Brush = New SolidBrush(Me.ForeColor) '背景を描画する e.Graphics.FillRectangle(backBrush, Me.ClientRectangle) 'バーの幅を計算する Dim chunksWidth As Integer = CInt( _ CDbl(Me.ClientSize.Width) * _ CDbl(Me.Value - Me.Minimum) / _ CDbl(Me.Maximum - Me.Minimum)) Dim chunksRect As New Rectangle(0, 0, chunksWidth, Me.ClientSize.Height) 'バーを描画する e.Graphics.FillRectangle(foreBrush, chunksRect) backBrush.Dispose() foreBrush.Dispose() End Sub End Class
using System; using System.Drawing; using System.Windows.Forms; /// <summary> /// ForeColorとBackColorプロパティによって色を変えることができる /// プログレスバーコントロールを表します。 /// </summary> public class ColorProgressBar : ProgressBar { public ColorProgressBar() { //Paintイベントが発生するようにする //ダブルバッファリングを有効にする base.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); } protected override void OnPaint(PaintEventArgs e) { Brush backBrush = new SolidBrush(this.BackColor); Brush foreBrush = new SolidBrush(this.ForeColor); //背景を描画する e.Graphics.FillRectangle(backBrush, this.ClientRectangle); //バーの幅を計算する int chunksWidth = (int)( (double)this.ClientSize.Width * (double)(this.Value - this.Minimum) / (double)(this.Maximum - this.Minimum)); Rectangle chunksRect = new Rectangle(0, 0, chunksWidth, this.ClientSize.Height); //バーを描画する e.Graphics.FillRectangle(foreBrush, chunksRect); backBrush.Dispose(); foreBrush.Dispose(); } }
補足:上記の例では、Styleプロパティなどは無視しています。下の例も同様です。なおマーキースタイルのプログレスバーを自分で描画する方法は、「ProgressBarをマーキースタイル(ブロックが移動するアニメーション)で表示する」で説明しています。
これは蛇足になるかもしれませんので、興味のある方のみお読みください。
先程プログレスバーの状態には、Normalの他に、ErrorやPausedがあることを紹介しましたが、実はこの他にPartialという状態があります。Partialのプログレスバーは青っぽい色になります。Windows Vista以降のエクスプローラで「コンピュータ」を表示すると、有効なドライブの一覧が表示されますが、この時空き領域を示すグラフがPartialのプログレスバーです。このようにパーセンテージを示すメーターとしてプログレスバーを使用する時にPartialにします。
しかしPBM_SETSTATEによる方法ではプログレスバーをPartialにできません。Partialのプログレスバーを表示するには、自分で描画するしかないようです。
プログレスバーはProgressBarRendererクラスを使って描画できますが、これではNormalのプログレスバーしか描画できません。その他の状態のプログレスバーを描画するには、VisualStyleRendererクラスを使用します。VisualStyleRendererクラスを使ってコントロールを描画する方法について詳しくは、「Visualスタイルでコントロールを描画する」をご覧ください。
ProgressBarRendererクラスを使用してオーナードローしたプログレスバーコントロールの例を以下に示します。このコントロールでは、BarStateプロパティでプログレスバーの状態を変更できます。BarStateプロパティをPartialにした時の外観は以下の図のようになります。
Imports System.Drawing Imports System.Windows.Forms Imports System.Windows.Forms.VisualStyles ''' <summary> ''' StateProgressBarで状態を指定する時に使用します。 ''' </summary> Public Enum ProgressBarState Normal [Error] Paused [Partial] End Enum ''' <summary> ''' 状態を変更できるプログレスバーコントロールを表します。 ''' </summary> Public Class StateProgressBar Inherits ProgressBar Private Const progressClassName As String = "PROGRESS" Private Enum PROGRESSPARTS PP_BAR = 1 PP_BARVERT = 2 PP_CHUNK = 3 PP_CHUNKVERT = 4 PP_FILL = 5 PP_FILLVERT = 6 PP_PULSEOVERLAY = 7 PP_MOVEOVERLAY = 8 PP_PULSEOVERLAYVERT = 9 PP_MOVEOVERLAYVERT = 10 PP_TRANSPARENTBAR = 11 PP_TRANSPARENTBARVERT = 12 End Enum Private Enum TRANSPARENTBARSTATES PBBS_NORMAL = 1 PBBS_PARTIAL = 2 End Enum Private Enum TRANSPARENTBARVERTSTATES PBBVS_NORMAL = 1 PBBVS_PARTIAL = 2 End Enum Private Enum FILLSTATES PBFS_NORMAL = 1 PBFS_ERROR = 2 PBFS_PAUSED = 3 PBFS_PARTIAL = 4 End Enum Private Enum FILLVERTSTATES PBFVS_NORMAL = 1 PBFVS_ERROR = 2 PBFVS_PAUSED = 3 PBFVS_PARTIAL = 4 End Enum Private vsRenderer As VisualStyleRenderer = Nothing Private _barState As ProgressBarState = ProgressBarState.Normal ''' <summary> ''' プログレスバーの状態を取得または設定します。 ''' </summary> Public Property BarState() As ProgressBarState Get Return Me._barState End Get Set(value As ProgressBarState) Me._barState = value End Set End Property Public Sub New() 'Paintイベントが発生するようにする 'ダブルバッファリングを有効にする MyBase.SetStyle(ControlStyles.UserPaint Or _ ControlStyles.AllPaintingInWmPaint Or _ ControlStyles.OptimizedDoubleBuffer, True) 'visualスタイルで表示するか調べる If Application.RenderWithVisualStyles AndAlso _ VisualStyleRenderer.IsElementDefined( _ VisualStyleElement.ProgressBar.Bar.Normal) Then Me.vsRenderer = New VisualStyleRenderer( _ VisualStyleElement.ProgressBar.Bar.Normal) End If End Sub Protected Overrides Sub OnPaint(e As PaintEventArgs) 'visualスタイルで描画するか If Me.vsRenderer Is Nothing Then '(VisualStyleRendererを使わないで描画するコードは省略) Return End If '背景とバーのPartとStateの値を決定する Dim backPart As Integer = CInt(PROGRESSPARTS.PP_TRANSPARENTBAR) Dim backState As Integer = CInt(TRANSPARENTBARSTATES.PBBS_NORMAL) Dim forePart As Integer = CInt(PROGRESSPARTS.PP_FILL) Dim foreState As Integer = CInt(FILLSTATES.PBFS_NORMAL) Select Case Me.BarState Case ProgressBarState.Normal Exit Select Case ProgressBarState.[Error] foreState = CInt(FILLSTATES.PBFS_ERROR) Exit Select Case ProgressBarState.Paused foreState = CInt(FILLSTATES.PBFS_PAUSED) Exit Select Case ProgressBarState.[Partial] backState = CInt(TRANSPARENTBARSTATES.PBBS_PARTIAL) foreState = CInt(FILLSTATES.PBFS_PARTIAL) Exit Select End Select '背景を描画する vsRenderer.SetParameters(progressClassName, backPart, backState) vsRenderer.DrawBackground(e.Graphics, Me.ClientRectangle) 'バーの幅を計算する Dim chunksWidth As Integer = CInt( _ CDbl(Me.ClientSize.Width) * _ CDbl(Me.Value - Me.Minimum) / _ CDbl(Me.Maximum - Me.Minimum)) Dim chunksRect As New Rectangle( _ 0, 0, chunksWidth, Me.ClientSize.Height) 'バーを描画する vsRenderer.SetParameters(progressClassName, forePart, foreState) vsRenderer.DrawBackground(e.Graphics, chunksRect) End Sub End Class
using System; using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; /// <summary> /// StateProgressBarで状態を指定する時に使用します。 /// </summary> public enum ProgressBarState { Normal, Error, Paused, Partial, } /// <summary> /// 状態を変更できるプログレスバーコントロールを表します。 /// </summary> public class StateProgressBar : ProgressBar { private const string progressClassName = "PROGRESS"; private enum PROGRESSPARTS { PP_BAR = 1, PP_BARVERT = 2, PP_CHUNK = 3, PP_CHUNKVERT = 4, PP_FILL = 5, PP_FILLVERT = 6, PP_PULSEOVERLAY = 7, PP_MOVEOVERLAY = 8, PP_PULSEOVERLAYVERT = 9, PP_MOVEOVERLAYVERT = 10, PP_TRANSPARENTBAR = 11, PP_TRANSPARENTBARVERT = 12, } private enum TRANSPARENTBARSTATES { PBBS_NORMAL = 1, PBBS_PARTIAL = 2, } private enum TRANSPARENTBARVERTSTATES { PBBVS_NORMAL = 1, PBBVS_PARTIAL = 2, } private enum FILLSTATES { PBFS_NORMAL = 1, PBFS_ERROR = 2, PBFS_PAUSED = 3, PBFS_PARTIAL = 4, } private enum FILLVERTSTATES { PBFVS_NORMAL = 1, PBFVS_ERROR = 2, PBFVS_PAUSED = 3, PBFVS_PARTIAL = 4, } private VisualStyleRenderer vsRenderer = null; private ProgressBarState _barState = ProgressBarState.Normal; /// <summary> /// プログレスバーの状態を取得または設定します。 /// </summary> public ProgressBarState BarState { get { return this._barState; } set { this._barState = value; } } public StateProgressBar() { //Paintイベントが発生するようにする //ダブルバッファリングを有効にする base.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); //visualスタイルで表示するか調べる if (Application.RenderWithVisualStyles && VisualStyleRenderer.IsElementDefined( VisualStyleElement.ProgressBar.Bar.Normal)) { this.vsRenderer = new VisualStyleRenderer(VisualStyleElement.ProgressBar.Bar.Normal); } } protected override void OnPaint(PaintEventArgs e) { //visualスタイルで描画するか if (this.vsRenderer == null) { //(VisualStyleRendererを使わないで描画するコードは省略) return; } //背景とバーのPartとStateの値を決定する int backPart = (int)PROGRESSPARTS.PP_TRANSPARENTBAR; int backState = (int)TRANSPARENTBARSTATES.PBBS_NORMAL; int forePart = (int)PROGRESSPARTS.PP_FILL; int foreState = (int)FILLSTATES.PBFS_NORMAL; switch (this.BarState) { case ProgressBarState.Normal: break; case ProgressBarState.Error: foreState = (int)FILLSTATES.PBFS_ERROR; break; case ProgressBarState.Paused: foreState = (int)FILLSTATES.PBFS_PAUSED; break; case ProgressBarState.Partial: backState = (int)TRANSPARENTBARSTATES.PBBS_PARTIAL; foreState = (int)FILLSTATES.PBFS_PARTIAL; break; } //背景を描画する vsRenderer.SetParameters(progressClassName, backPart, backState); vsRenderer.DrawBackground(e.Graphics, this.ClientRectangle); //バーの幅を計算する int chunksWidth = (int)( (double)this.ClientSize.Width * (double)(this.Value - this.Minimum) / (double)(this.Maximum - this.Minimum)); Rectangle chunksRect = new Rectangle(0, 0, chunksWidth, this.ClientSize.Height); //バーを描画する vsRenderer.SetParameters(progressClassName, forePart, foreState); vsRenderer.DrawBackground(e.Graphics, chunksRect); } }
補足:上の例では使用していませんが、PP_PULSEOVERLAYとPP_MOVEOVERLAYによってプログレスバーが光っているような効果を描画することができます。