ProgressBarの特殊なスタイルとして、マーキースタイルのプログレスバー(indeterminate progress bar)があります。マーキースタイルのプログレスバーでは、バーブロックが絶え間なく、連続して左から右に移動し続けます。
マーキースタイルは、現在処理が進行中であることをユーザーに知らせる目的で使用します。ただし、処理がどの程度進行したかを示すことができるのであれば通常のプログレスバー(determinate progress bar)を使うべきで、マーキースタイルは進行状況を判断できない場合にのみ使用します。
ここでは、ProgressBarをマーキースタイルで表示する方法を紹介します。
.NET Framework 2.0からは、ProgressBar.StyleプロパティをProgressBarStyle.Marqueeにすることでマーキースタイルにできます。
ProgressBarをマーキースタイルにするとプログレスバーの値は意味がなくなりますので、ProgressBar.Value、Minimum、Maximumなどの設定は無視されます。
また、アプリケーションのvisualスタイルが無効の時はマーキースタイルにならず、ProgressBar.StyleプロパティがBlocksの時と同じになります。visualスタイルを無効にする方法は、「コントロールの外観をビジュアルスタイル(XPスタイル)にする」をご覧ください。
さらに、ProgressBar.MarqueeAnimationSpeedプロパティでブロックの移動速度を変更することができます。デフォルトは100で、値を大きくすると遅くなり、小さくすると速くなります。0にすると、止まります。
'ProgressBar1をマーキースタイルにする ProgressBar1.Style = ProgressBarStyle.Marquee 'ブロックの移動速度をデフォルトの倍にする ProgressBar1.MarqueeAnimationSpeed = 50
//ProgressBar1をマーキースタイルにする ProgressBar1.Style = ProgressBarStyle.Marquee; //ブロックの移動速度をデフォルトの倍にする ProgressBar1.MarqueeAnimationSpeed = 50;
プログレスバーを自分で描画(オーナードロー)する方法は「ProgressBarの色を変える」で紹介しましたが、マーキースタイルのプログレスバーもこれと同じようにして自分で描画することもできます。
以下にその例を示します。ここではvisualスタイルで描画するために.NET Framework 2.0以降でしか使用できないメソッドなどを使用していますが、visualスタイルで描画する部分を削除すれば.NET Framework 1.1以前でも行けるかと思います(未確認です)。
Imports System.Drawing Imports System.Windows.Forms Imports System.ComponentModel ''' <summary> ''' マーキースタイルのプログレスバーコントロールを表します。 ''' </summary> Public Class MarqueeProgressBar Inherits ProgressBar 'プロパティの初期値 Private Const defaultMarqueeWidth As Integer = 127 Private Const defaultMarqueeAnimationSpeed As Integer = 100 Private Const defaultMarqueeAnimationStep As Integer = 6 Private Const defaultForeColorString As String = "Highlight" Private Const defaultBackColorString As String = "ControlLight" Private Const defaultFrameColorString As String = "ActiveBorder" Private Const defaultUseVisualStyle As Boolean = True 'ブロックの移動に使用するタイマー Private marqueeTimer As Timer = Nothing 'ブロックの現在の位置 Private marqueeLeft As Integer = 0 Private _marqueeWidth As Integer ''' <summary> ''' ブロックの幅を取得または設定します。 ''' </summary> <DefaultValue(defaultMarqueeWidth)> _ Public Property MarqueeWidth() As Integer Get Return Me._marqueeWidth End Get Set(value As Integer) Me._marqueeWidth = value '描画し直す Me.Invalidate() End Set End Property Private _marqueeAnimationSpeed As Integer ''' <summary> ''' ブロックが移動する間隔をミリ秒単位で取得または設定します。 ''' </summary> <DefaultValue(defaultMarqueeAnimationSpeed)> _ Public Shadows Property MarqueeAnimationSpeed() As Integer Get Return Me._marqueeAnimationSpeed End Get Set(value As Integer) If value < 1 Then Throw New ArgumentOutOfRangeException( _ "1以上の整数を指定してください") End If Me._marqueeAnimationSpeed = value If Not Me.marqueeTimer Is Nothing Then Me.marqueeTimer.Interval = Me._marqueeAnimationSpeed End If End Set End Property Private _marqueeAnimationStep As Integer ''' <summary> ''' ブロックが一度に移動する距離をピクセル単位で取得または設定します。 ''' </summary> <DefaultValue(defaultMarqueeAnimationStep)> _ Public Property MarqueeAnimationStep() As Integer Get Return Me._marqueeAnimationStep End Get Set(value As Integer) If value < 1 Then Throw New ArgumentOutOfRangeException( _ "1以上の整数を指定してください") End If Me._marqueeAnimationStep = value End Set End Property Private foreColorBrush As Brush = Nothing ''' <summary> ''' プログレスバーのブロックの色を取得または設定します。 ''' </summary> <DefaultValue(GetType(Color), defaultForeColorString)> _ Public Overrides Property ForeColor() As Color Get Return MyBase.ForeColor End Get Set(value As Color) MyBase.ForeColor = value 'Brushを作成する If Not Me.foreColorBrush Is Nothing Then Me.foreColorBrush.Dispose() End If Me.foreColorBrush = New SolidBrush(MyBase.ForeColor) '描画し直す Me.Invalidate() End Set End Property Private backColorBrush As Brush = Nothing ''' <summary> ''' プログレスバーの背景色を取得または設定します。 ''' </summary> <DefaultValue(GetType(Color), defaultBackColorString)> _ Public Overrides Property BackColor() As Color Get Return MyBase.BackColor End Get Set(value As Color) MyBase.BackColor = value 'Brushを作成する If Not Me.backColorBrush Is Nothing Then Me.backColorBrush.Dispose() End If Me.backColorBrush = New SolidBrush(MyBase.BackColor) '描画し直す Me.Invalidate() End Set End Property Private _frameColor As Color Private frameColorPen As Pen = Nothing ''' <summary> ''' プログレスバーの枠の色を取得または設定します。 ''' </summary> <DefaultValue(GetType(Color), defaultFrameColorString)> _ Public Property FrameColor() As Color Get Return Me._frameColor End Get Set(value As Color) If value.IsEmpty Then Return End If Me._frameColor = value 'Penを作成する If Not Me.frameColorPen Is Nothing Then Me.frameColorPen.Dispose() End If Me.frameColorPen = New Pen(Me.FrameColor) '描画し直す Me.Invalidate() End Set End Property Private _useVisualStyle As Boolean ''' <summary> ''' visual スタイルがサポートされている場合に、 ''' visual スタイルを使用してプログレスバーを描画するかどうかを ''' 取得または設定します。 ''' </summary> ''' <remarks> ''' このプロパティがTrueで、visualスタイルが有効である場合は、 ''' ForeColor、BackColor、FrameColorプロパティの値は無視されます。 ''' </remarks> <DefaultValue(defaultUseVisualStyle)> _ Public Property UseVisualStyle() As Boolean Get Return Me._useVisualStyle End Get Set(value As Boolean) Me._useVisualStyle = value End Set End Property ''' <summary> ''' MarqueeProgressBarクラスの新しいインスタンスを初期化します。 ''' </summary> Public Sub New() 'Paintイベントが発生するようにする 'ダブルバッファリングを有効にする MyBase.SetStyle(ControlStyles.UserPaint Or _ ControlStyles.AllPaintingInWmPaint Or _ ControlStyles.OptimizedDoubleBuffer, True) 'プロパティを初期化する Me.MarqueeAnimationSpeed = defaultMarqueeAnimationSpeed Me.MarqueeAnimationStep = defaultMarqueeAnimationStep Me.MarqueeWidth = defaultMarqueeWidth Me.ForeColor = Color.FromName(defaultForeColorString) Me.BackColor = Color.FromName(defaultBackColorString) Me.FrameColor = Color.FromName(defaultFrameColorString) Me.UseVisualStyle = defaultUseVisualStyle 'ブロックの移動を開始する Me.StartMarquee() End Sub ''' <summary> ''' ブロックの移動を開始します。 ''' </summary> Public Sub StartMarquee() '初期化する Me.SetStartMarqueeLeft() 'タイマーを初期化する If Me.marqueeTimer Is Nothing Then Me.marqueeTimer = New Timer() AddHandler Me.marqueeTimer.Tick, AddressOf marqueeTimer_Tick End If Me.marqueeTimer.Interval = Me.MarqueeAnimationSpeed 'タイマーを開始する Me.marqueeTimer.Start() End Sub ''' <summary> ''' ブロックの移動を停止します。 ''' </summary> Public Sub StopMarquee() If Not Me.marqueeTimer Is Nothing Then Me.marqueeTimer.[Stop]() End If End Sub ''' <summary> ''' ブロックの初期位置を設定します。 ''' </summary> Private Sub SetStartMarqueeLeft() Me.marqueeLeft = -Me.MarqueeWidth End Sub ''' <summary> ''' ブロックの位置を次の位置に設定します。 ''' </summary> Private Sub SetNextMarqueeLeft() Me.marqueeLeft += Me.MarqueeAnimationStep '最後まで移動した時ははじめの位置に戻す If Me.ClientRectangle.Width <= Me.marqueeLeft Then Me.SetStartMarqueeLeft() End If End Sub 'marqueeTimer.Tickイベントハンドラメソッド Private Sub marqueeTimer_Tick(sender As Object, e As EventArgs) 'ブロックの位置を次に移動する Me.SetNextMarqueeLeft() '描画する Me.Invalidate() End Sub Protected Overrides Sub OnPaint(e As PaintEventArgs) If Me.UseVisualStyle AndAlso _ Application.RenderWithVisualStyles Then 'visualスタイルで描画する '背景を描画する ProgressBarRenderer.DrawHorizontalBar( _ e.Graphics, Me.ClientRectangle) 'ブロックを描画する '枠を塗りつぶさないようにする e.Graphics.SetClip(New Rectangle(1, 1, _ Me.ClientRectangle.Width - 2, _ Me.ClientRectangle.Height - 2)) Dim chunksRect As New Rectangle(Me.marqueeLeft, 0, _ Me.MarqueeWidth, Me.ClientSize.Height) ProgressBarRenderer.DrawHorizontalChunks(e.Graphics, chunksRect) Else 'visualスタイルを使わずに描画する '背景を描画する e.Graphics.FillRectangle(Me.backColorBrush, Me.ClientRectangle) 'ブロックを描画する Dim chunksRect As New Rectangle(Me.marqueeLeft, 0, _ Me.MarqueeWidth, Me.ClientSize.Height) e.Graphics.FillRectangle(Me.foreColorBrush, chunksRect) '枠を描画する Dim frameRect As New Rectangle(0, 0, _ Me.ClientSize.Width - 1, Me.ClientSize.Height - 1) e.Graphics.DrawRectangle(Me.frameColorPen, frameRect) End If End Sub Protected Overrides Sub Dispose(disposing As Boolean) If disposing Then '後片付け If Not Me.marqueeTimer Is Nothing Then RemoveHandler Me.marqueeTimer.Tick, AddressOf marqueeTimer_Tick Me.marqueeTimer.Dispose() Me.marqueeTimer = Nothing End If If Not Me.foreColorBrush Is Nothing Then Me.foreColorBrush.Dispose() Me.foreColorBrush = Nothing End If If Not Me.backColorBrush Is Nothing Then Me.backColorBrush.Dispose() Me.backColorBrush = Nothing End If If Not Me.frameColorPen Is Nothing Then Me.frameColorPen.Dispose() Me.frameColorPen = Nothing End If End If MyBase.Dispose(disposing) End Sub End Class
using System; using System.Drawing; using System.Windows.Forms; using System.ComponentModel; /// <summary> /// マーキースタイルのプログレスバーコントロールを表します。 /// </summary> public class MarqueeProgressBar : ProgressBar { //プロパティの初期値 private const int defaultMarqueeWidth = 127; private const int defaultMarqueeAnimationSpeed = 100; private const int defaultMarqueeAnimationStep = 6; private const string defaultForeColorString = "Highlight"; private const string defaultBackColorString = "ControlLight"; private const string defaultFrameColorString = "ActiveBorder"; private const bool defaultUseVisualStyle = true; //ブロックの移動に使用するタイマー private Timer marqueeTimer = null; //ブロックの現在の位置 private int marqueeLeft = 0; private int _marqueeWidth; /// <summary> /// ブロックの幅を取得または設定します。 /// </summary> [DefaultValue(defaultMarqueeWidth)] public int MarqueeWidth { get { return this._marqueeWidth; } set { this._marqueeWidth = value; //描画し直す this.Invalidate(); } } private int _marqueeAnimationSpeed; /// <summary> /// ブロックが移動する間隔をミリ秒単位で取得または設定します。 /// </summary> [DefaultValue(defaultMarqueeAnimationSpeed)] public new int MarqueeAnimationSpeed { get { return this._marqueeAnimationSpeed; } set { if (value < 1) { throw new ArgumentOutOfRangeException( "1以上の整数を指定してください"); } this._marqueeAnimationSpeed = value; if (this.marqueeTimer != null) { this.marqueeTimer.Interval = this._marqueeAnimationSpeed; } } } private int _marqueeAnimationStep; /// <summary> /// ブロックが一度に移動する距離をピクセル単位で取得または設定します。 /// </summary> [DefaultValue(defaultMarqueeAnimationStep)] public int MarqueeAnimationStep { get { return this._marqueeAnimationStep; } set { if (value < 1) { throw new ArgumentOutOfRangeException( "1以上の整数を指定してください"); } this._marqueeAnimationStep = value; } } private Brush foreColorBrush = null; /// <summary> /// プログレスバーのブロックの色を取得または設定します。 /// </summary> [DefaultValue(typeof(Color), defaultForeColorString)] public override Color ForeColor { get { return base.ForeColor; } set { base.ForeColor = value; //Brushを作成する if (this.foreColorBrush != null) { this.foreColorBrush.Dispose(); } this.foreColorBrush = new SolidBrush(base.ForeColor); //描画し直す this.Invalidate(); } } private Brush backColorBrush = null; /// <summary> /// プログレスバーの背景色を取得または設定します。 /// </summary> [DefaultValue(typeof(Color), defaultBackColorString)] public override Color BackColor { get { return base.BackColor; } set { base.BackColor = value; //Brushを作成する if (this.backColorBrush != null) { this.backColorBrush.Dispose(); } this.backColorBrush = new SolidBrush(base.BackColor); //描画し直す this.Invalidate(); } } private Color _frameColor; private Pen frameColorPen = null; /// <summary> /// プログレスバーの枠の色を取得または設定します。 /// </summary> [DefaultValue(typeof(Color), defaultFrameColorString)] public Color FrameColor { get { return this._frameColor; } set { if (value.IsEmpty) { return; } this._frameColor = value; //Penを作成する if (this.frameColorPen != null) { this.frameColorPen.Dispose(); } this.frameColorPen = new Pen(this.FrameColor); //描画し直す this.Invalidate(); } } private bool _useVisualStyle; /// <summary> /// visual スタイルがサポートされている場合に、 /// visual スタイルを使用してプログレスバーを描画するかどうかを /// 取得または設定します。 /// </summary> /// <remarks> /// このプロパティがTrueで、visualスタイルが有効である場合は、 /// ForeColor、BackColor、FrameColorプロパティの値は無視されます。 /// </remarks> [DefaultValue(defaultUseVisualStyle)] public bool UseVisualStyle { get { return this._useVisualStyle; } set { this._useVisualStyle = value; } } /// <summary> /// MarqueeProgressBarクラスの新しいインスタンスを初期化します。 /// </summary> public MarqueeProgressBar() { //Paintイベントが発生するようにする //ダブルバッファリングを有効にする base.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); //プロパティを初期化する this.MarqueeAnimationSpeed = defaultMarqueeAnimationSpeed; this.MarqueeAnimationStep = defaultMarqueeAnimationStep; this.MarqueeWidth = defaultMarqueeWidth; this.ForeColor = Color.FromName(defaultForeColorString); this.BackColor = Color.FromName(defaultBackColorString); this.FrameColor = Color.FromName(defaultFrameColorString); this.UseVisualStyle = defaultUseVisualStyle; //ブロックの移動を開始する this.StartMarquee(); } /// <summary> /// ブロックの移動を開始します。 /// </summary> public void StartMarquee() { //初期化する this.SetStartMarqueeLeft(); //タイマーを初期化する if (this.marqueeTimer == null) { this.marqueeTimer = new Timer(); this.marqueeTimer.Tick += marqueeTimer_Tick; } this.marqueeTimer.Interval = this.MarqueeAnimationSpeed; //タイマーを開始する this.marqueeTimer.Start(); } /// <summary> /// ブロックの移動を停止します。 /// </summary> public void StopMarquee() { if (this.marqueeTimer != null) { this.marqueeTimer.Stop(); } } /// <summary> /// ブロックの初期位置を設定します。 /// </summary> private void SetStartMarqueeLeft() { this.marqueeLeft = -this.MarqueeWidth; } /// <summary> /// ブロックの位置を次の位置に設定します。 /// </summary> private void SetNextMarqueeLeft() { this.marqueeLeft += this.MarqueeAnimationStep; //最後まで移動した時ははじめの位置に戻す if (this.ClientRectangle.Width <= this.marqueeLeft) { this.SetStartMarqueeLeft(); } } //marqueeTimer.Tickイベントハンドラメソッド private void marqueeTimer_Tick(object sender, EventArgs e) { //ブロックの位置を次に移動する this.SetNextMarqueeLeft(); //描画する this.Invalidate(); } protected override void OnPaint(PaintEventArgs e) { if (this.UseVisualStyle && Application.RenderWithVisualStyles) { //visualスタイルで描画する //背景を描画する ProgressBarRenderer.DrawHorizontalBar( e.Graphics, this.ClientRectangle); //ブロックを描画する //枠を塗りつぶさないようにする e.Graphics.SetClip(new Rectangle(1, 1, this.ClientRectangle.Width - 2, this.ClientRectangle.Height - 2)); Rectangle chunksRect = new Rectangle(this.marqueeLeft, 0, this.MarqueeWidth, this.ClientSize.Height); ProgressBarRenderer.DrawHorizontalChunks( e.Graphics, chunksRect); } else { //visualスタイルを使わずに描画する //背景を描画する e.Graphics.FillRectangle( this.backColorBrush, this.ClientRectangle); //ブロックを描画する Rectangle chunksRect = new Rectangle(this.marqueeLeft, 0, this.MarqueeWidth, this.ClientSize.Height); e.Graphics.FillRectangle(this.foreColorBrush, chunksRect); //枠を描画する Rectangle frameRect = new Rectangle(0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1); e.Graphics.DrawRectangle(this.frameColorPen, frameRect); } } protected override void Dispose(bool disposing) { if (disposing) { //後片付け if (this.marqueeTimer != null) { this.marqueeTimer.Tick -= marqueeTimer_Tick; this.marqueeTimer.Dispose(); this.marqueeTimer = null; } if (this.foreColorBrush != null) { this.foreColorBrush.Dispose(); this.foreColorBrush = null; } if (this.backColorBrush != null) { this.backColorBrush.Dispose(); this.backColorBrush = null; } if (this.frameColorPen != null) { this.frameColorPen.Dispose(); this.frameColorPen = null; } } base.Dispose(disposing); } }