C#デスクトップアプリケーションでProgressBarコントロールを使用しました。コントロールが宣言されているスレッド以外のスレッドで使用しました。正常に動作しています。今、「登録の開始」などのプログレスバーコントロール内のテキストをどのように表示できるか疑問に思っています。また、マーキープログレスバーとして使用したいので、助けてください。
たとえば、InfoProgresBarという名前のコントロールを作成します。この機能は、この機能に1つまたは2つのラベル(メインジョブ、現在のジョブ)とProgressBarを提供し、ProgressBarの代わりに使用します。
OnPaintメソッドをオーバーライドし、基本実装を呼び出して、独自のテキストをペイントする必要があります。
独自のCustomProgressBar
を作成し、OnPaint
をオーバーライドして、必要なテキストを描画する必要があります。
カスタムプログレスバークラス
namespace ProgressBarSample
{
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class CustomProgressBar: ProgressBar
{
//Property to set to decide whether to print a % or Text
public ProgressBarDisplayText DisplayStyle { get; set; }
//Property to hold the custom text
public String CustomText { get; set; }
public CustomProgressBar()
{
// Modify the ControlStyles flags
//http://msdn.Microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rect = ClientRectangle;
Graphics g = e.Graphics;
ProgressBarRenderer.DrawHorizontalBar(g, rect);
rect.Inflate(-3, -3);
if (Value > 0)
{
// As we doing this ourselves we need to draw the chunks on the progress bar
Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
}
// Set the Display text (Either a % amount or our custom text
string text = DisplayStyle == ProgressBarDisplayText.Percentage ? Value.ToString() + '%' : CustomText;
using (Font f = new Font(FontFamily.GenericSerif, 10))
{
SizeF len = g.MeasureString(text, f);
// Calculate the location of the text (the middle of progress bar)
// Point location = new Point(Convert.ToInt32((rect.Width / 2) - (len.Width / 2)), Convert.ToInt32((rect.Height / 2) - (len.Height / 2)));
Point location = new Point(Convert.ToInt32((Width / 2) - len.Width / 2), Convert.ToInt32((Height / 2) - len.Height / 2));
// The commented-out code will centre the text into the highlighted area only. This will centre the text regardless of the highlighted area.
// Draw the custom text
g.DrawString(text, f, Brushes.Red, location);
}
}
}
}
WinFormsアプリケーションのサンプル
using System;
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;
namespace ProgressBarSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set our custom Style (% or text)
customProgressBar1.DisplayStyle = ProgressBarDisplayText.CustomText;
customProgressBar1.CustomText = "Initialising";
}
private void btnReset_Click(object sender, EventArgs e)
{
customProgressBar1.Value = 0;
btnStart.Enabled = true;
}
private void btnStart_Click(object sender, EventArgs e)
{
btnReset.Enabled = false;
btnStart.Enabled = false;
for (int i = 0; i < 101; i++)
{
customProgressBar1.Value = i;
// Demo purposes only
System.Threading.Thread.Sleep(100);
// Set the custom text at different intervals for demo purposes
if (i > 30 && i < 50)
{
customProgressBar1.CustomText = "Registering Account";
}
if (i > 80)
{
customProgressBar1.CustomText = "Processing almost complete!";
}
if (i >= 99)
{
customProgressBar1.CustomText = "Complete";
}
}
btnReset.Enabled = true;
}
}
}
上記のBarryが提供する solution は優れていますが、「フリッカー問題」があります。
Valueがゼロを超えると、OnPaintが繰り返し呼び出され、テキストがちらつきます。
これには解決策があります。独自のコードで描画するため、オブジェクトにVisualStylesは必要ありません。
Barryが作成したカスタムオブジェクトに次のコードを追加すると、ちらつきを回避できます。
[DllImportAttribute("uxtheme.dll")]
private static extern int SetWindowTheme(IntPtr hWnd, string appname, string idlist);
protected override void OnHandleCreated(EventArgs e)
{
SetWindowTheme(this.Handle, "", "");
base.OnHandleCreated(e);
}
私はこれを自分で書きませんでした。ここで見つけました: https://stackoverflow.com/a/299983/1163954
私はそれをテストし、動作します。
書いた点滅/ちらつきなしTextProgressBar
このコードが役立つ場合は、回答に投票することを忘れないでください。
ソースコードはこちらにあります: https://github.com/ukushu/TextProgressBar
サンプル:
この単純なコードを使用しましたが、動作します!
for (int i = 0; i < N * N; i++)
{
Thread.Sleep(50);
progressBar1.BeginInvoke(new Action(() => progressBar1.Value = i));
progressBar1.CreateGraphics().DrawString(i.ToString() + "%", new Font("Arial",
(float)10.25, FontStyle.Bold),
Brushes.Red, new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
}
単純な問題が1つだけあります。これは、プログレスバーが上昇し始めたときに、パーセンテージが非表示になってから再び表示されることです。私は自分で書きませんでしたが、ここで見つけました: c#のプログレスバーのテキスト
このコードを使用しましたが、動作します。
進行状況バーの上に透明な背景のラベルを配置しようとしましたが、正しく機能しませんでした。ですから、ここでは Barry のソリューションが非常に有用であることがわかりましたが、美しいVistaスタイルのプログレスバーを見逃していました。そこで、Barryのソリューションを http://www.dreamincode.net/forums/topic/243621-percent-into-progress-bar/ とマージし、テキストの割合を表示しながらネイティブの進行状況バーを維持することに成功しました。またはその上のカスタムテキスト。このソリューションでもちらつきは見られません。古いスレッドを掘り下げてすみませんが、私は今日これを必要としていたので、他の人もそれを必要とするかもしれません。
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class ProgressBarWithCaption : ProgressBar
{
//Property to set to decide whether to print a % or Text
private ProgressBarDisplayText m_DisplayStyle;
public ProgressBarDisplayText DisplayStyle {
get { return m_DisplayStyle; }
set { m_DisplayStyle = value; }
}
//Property to hold the custom text
private string m_CustomText;
public string CustomText {
get { return m_CustomText; }
set {
m_CustomText = value;
this.Invalidate();
}
}
private const int WM_Paint = 0x000F;
protected override void WndProc(ref Message m)
{
base.WndProc(m);
switch (m.Msg) {
case WM_Paint:
int m_Percent = Convert.ToInt32((Convert.ToDouble(Value) / Convert.ToDouble(Maximum)) * 100);
dynamic flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
using (Graphics g = Graphics.FromHwnd(Handle)) {
using (Brush textBrush = new SolidBrush(ForeColor)) {
switch (DisplayStyle) {
case ProgressBarDisplayText.CustomText:
TextRenderer.DrawText(g, CustomText, new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
case ProgressBarDisplayText.Percentage:
TextRenderer.DrawText(g, string.Format("{0}%", m_Percent), new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
}
}
}
break;
}
}
}
TransparentLabelコントロールを使用するInfoProgressBarコントロールを作成しました。タイマー付きのフォームでテストすると、250ミリ秒未満のタイマー間隔を使用すると、30〜40の値が変更されるたびにテキストが表示されます(おそらく、画面の更新に必要な時間がタイマー間隔より長いため) 。
UpdateTextメソッドを変更して、計算されたすべての値をCustomTextに挿入することは可能ですが、それはまだ必要なものではありません。これにより、DisplayTypeプロパティの必要性がなくなり、列挙されます。
TransparentLabelクラスは、新しいUserControlを追加し、次の実装でLabelから継承するように変更することで作成されました。
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Utils.GUI
{
public partial class TransparentLabel : Label
{
// hide the BackColor attribute as much as possible.
// setting the base value has no effect as drawing the
// background is disabled
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override Color BackColor
{
get
{
return Color.Transparent;
}
set
{
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public override ContentAlignment TextAlign
{
get
{
return base.TextAlign;
}
set
{
base.TextAlign = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public TransparentLabel()
{
InitializeComponent();
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
base.BackColor = Color.Transparent;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
RecreateHandle();
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// do nothing
}
}
}
関連するデザイナーコードは変更していませんが、ここでは完全を期しています。
namespace Utils.GUI
{
partial class TransparentLabel
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}
次に、別の新しいUserControlを作成し、次の実装でProgressBarから派生するように変更しました。
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
namespace Utils.GUI
{
[Designer(typeof(InfoProgressBarDesigner))]
public partial class InfoProgressBar : ProgressBar
{
// designer class to add font baseline snapline by copying it from the label
private class InfoProgressBarDesigner : ControlDesigner
{
public override IList SnapLines
{
get
{
IList snapLines = base.SnapLines;
InfoProgressBar control = Control as InfoProgressBar;
if(control != null)
{
using(IDesigner designer = TypeDescriptor.CreateDesigner(control.lblText, typeof(IDesigner)))
{
if(designer != null)
{
designer.Initialize(control.lblText);
ControlDesigner boxDesigner = designer as ControlDesigner;
if(boxDesigner != null)
{
foreach(SnapLine line in boxDesigner.SnapLines)
{
if(line.SnapLineType == SnapLineType.Baseline)
{
snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset, line.Filter, line.Priority));
break;
}
}
}
}
}
}
return snapLines;
}
}
}
// enum to select the type of displayed value
public enum ProgressBarDisplayType
{
Custom = 0,
Percent = 1,
Progress = 2,
Remain = 3,
Value = 4,
}
private string _customText;
private ProgressBarDisplayType _displayType;
private int _range;
[Bindable(false)]
[Browsable(true)]
[DefaultValue("{0}")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// {0} is replaced with the result of the selected calculation
public string CustomText
{
get
{
return _customText;
}
set
{
_customText = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ProgressBarDisplayType.Percent)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ProgressBarDisplayType DisplayType
{
get
{
return _displayType;
}
set
{
_displayType = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// don't use the lblText font as if it is null, it checks the parent font (i.e. this property) and gives an infinite loop
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(100)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Maximum
{
get
{
return base.Maximum;
}
set
{
base.Maximum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Minimum
{
get
{
return base.Minimum;
}
set
{
base.Minimum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ContentAlignment.MiddleLeft)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ContentAlignment TextAlign
{
get
{
return lblText.TextAlign;
}
set
{
lblText.TextAlign = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(typeof(Color), "0x000000")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public Color TextColor
{
get
{
return lblText.ForeColor;
}
set
{
lblText.ForeColor = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Value
{
get
{
return base.Value;
}
set
{
base.Value = value;
UpdateText();
}
}
public InfoProgressBar()
{
InitializeComponent();
CustomText = "{0}";
DisplayType = ProgressBarDisplayType.Percent;
Maximum = 100;
Minimum = 0;
TextAlign = ContentAlignment.MiddleLeft;
TextColor = Color.Black;
Value = 0;
// means the label gets drawn in front of the progress bar
lblText.Parent = this;
_range = base.Maximum - base.Minimum;
}
protected void UpdateText()
{
switch(DisplayType)
{
case ProgressBarDisplayType.Custom:
{
lblText.Text = _customText;
break;
}
case ProgressBarDisplayType.Percent:
{
if(_range > 0)
{
lblText.Text = string.Format(_customText, string.Format("{0}%", (int)((Value * 100) / _range)));
}
else
{
lblText.Text = "100%";
}
break;
}
case ProgressBarDisplayType.Progress:
{
lblText.Text = string.Format(_customText, (Value - Minimum));
break;
}
case ProgressBarDisplayType.Remain:
{
lblText.Text = string.Format(_customText, (Maximum - Value));
break;
}
case ProgressBarDisplayType.Value:
{
lblText.Text = string.Format(_customText, Value);
break;
}
}
}
public new void Increment(int value)
{
base.Increment(value);
UpdateText();
}
public new void PerformStep()
{
base.PerformStep();
UpdateText();
}
}
}
そして、デザイナーのコード:
namespace Utils.GUI
{
partial class InfoProgressBar
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.lblText = new Utils.GUI.TransparentLabel();
this.SuspendLayout();
//
// lblText
//
this.lblText.BackColor = System.Drawing.Color.Transparent;
this.lblText.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblText.Location = new System.Drawing.Point(0, 0);
this.lblText.Name = "lblText";
this.lblText.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.lblText.Size = new System.Drawing.Size(100, 23);
this.lblText.TabIndex = 0;
this.lblText.Text = "transparentLabel1";
this.ResumeLayout(false);
}
#endregion
private TransparentLabel lblText;
}
}