using System; using System.Drawing; using System.ComponentModel; using System.Drawing.Drawing2D; using System.Windows.Forms; using System.Threading; using DevComponents.DotNetBar.Controls; using DevComponents.DotNetBar.Rendering; namespace DevComponents.DotNetBar { /// /// Represents circular progress indicator. /// [ToolboxItem(false), Designer("DevComponents.DotNetBar.Design.SimpleItemDesigner, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf")] public class CircularProgressItem : BaseItem { #region Events #endregion #region Constructor /// /// Creates new instance of circular progress indicator. /// public CircularProgressItem() : this("", "") { } /// /// Creates new instance of circular progress indicator and assigns the name to it. /// /// Item name. public CircularProgressItem(string sItemName) : this(sItemName, "") { } /// /// Creates new instance of circular progress indicator and assigns the name and text to it. /// /// Item name. /// item text. public CircularProgressItem(string sItemName, string ItemText) : base(sItemName, ItemText) { _SpokeAngles = GetSpokeAngles(_SpokeCount); } /// /// Returns copy of the item. /// public override BaseItem Copy() { CircularProgressItem objCopy = new CircularProgressItem(m_Name); this.CopyToItem(objCopy); return objCopy; } /// /// Copies the ProgressBarItem specific properties to new instance of the item. /// /// New ProgressBarItem instance. internal void InternalCopyToItem(ProgressBarItem copy) { CopyToItem(copy); } /// /// Copies the ProgressBarItem specific properties to new instance of the item. /// /// New ProgressBarItem instance. protected override void CopyToItem(BaseItem copy) { CircularProgressItem objCopy = copy as CircularProgressItem; base.CopyToItem(objCopy); } private bool _IsDisposing = false; protected override void Dispose(bool disposing) { _IsDisposing = true; Stop(true); base.Dispose(disposing); _IsDisposing = false; } #endregion #region Implementation public override void Paint(ItemPaintArgs e) { //e.Graphics.SmoothingMode = SmoothingMode.HighQuality; //e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; if (_ProgressBarType == eCircularProgressType.Line) { PaintLineProgressBar(e); } else if (_ProgressBarType == eCircularProgressType.Dot) { PaintDotProgressBar(e); } else if (_ProgressBarType == eCircularProgressType.Donut) { PaintDonutProgressBar(e); } else if (_ProgressBarType == eCircularProgressType.Spoke) { PaintSpokeProgressBar(e); } else if (_ProgressBarType == eCircularProgressType.Pie) { PaintPieProgressBar(e); } PaintLabel(e); if (this.Focused && this.DesignMode) { Rectangle r = this.DisplayRectangle; r.Inflate(-1, -1); DesignTime.DrawDesignTimeSelection(e.Graphics, r, e.Colors.ItemDesignTimeBorder); } this.DrawInsertMarker(e.Graphics); } private void PaintLabel(ItemPaintArgs e) { if (!_TextVisible || string.IsNullOrEmpty(this.Text)) return; Font font = e.Font; Graphics g = e.Graphics; Color textColor = GetTextColor(e); Rectangle textBounds = Rectangle.Empty; Rectangle bounds = m_Rect; Rectangle progressBounds = GetProgressBarBounds(); eTextFormat format = eTextFormat.Default | eTextFormat.NoClipping; int textContentSpacing = Dpi.Width(TextContentSpacing); if (_TextPosition == eTextPosition.Left) { textBounds = new Rectangle(bounds.X + _TextPadding.Left, bounds.Y + _TextPadding.Top, m_Rect.Width - _TextPadding.Horizontal - textContentSpacing - progressBounds.Width, m_Rect.Height - _TextPadding.Vertical); format |= eTextFormat.VerticalCenter; } else if (_TextPosition == eTextPosition.Right) { textBounds = new Rectangle(bounds.X + _TextPadding.Left + textContentSpacing + progressBounds.Width, bounds.Y + _TextPadding.Top, m_Rect.Width - _TextPadding.Horizontal - textContentSpacing - progressBounds.Width, m_Rect.Height - _TextPadding.Vertical); format |= eTextFormat.VerticalCenter; } else if (_TextPosition == eTextPosition.Top) { textBounds = new Rectangle(bounds.X + _TextPadding.Left, bounds.Y + _TextPadding.Top, m_Rect.Width - _TextPadding.Horizontal, m_Rect.Height - _TextPadding.Vertical - progressBounds.Height - textContentSpacing); format |= eTextFormat.HorizontalCenter; } else if (_TextPosition == eTextPosition.Bottom) { textBounds = new Rectangle(bounds.X + _TextPadding.Left, bounds.Y + _TextPadding.Top + textContentSpacing + progressBounds.Height, m_Rect.Width - _TextPadding.Horizontal, m_Rect.Height - _TextPadding.Vertical - progressBounds.Height - textContentSpacing); format |= eTextFormat.HorizontalCenter; } if (_TextWidth > 0) { textBounds.Width = _TextWidth; format |= eTextFormat.WordBreak; } //g.FillRectangle(Brushes.WhiteSmoke, textBounds); TextDrawing.DrawString(g, this.Text, font, textColor, textBounds, format); } private Color GetTextColor(ItemPaintArgs e) { if (!_TextColor.IsEmpty) return _TextColor; return LabelItem.GetTextColor(e, this.EffectiveStyle, this.GetEnabled(), _TextColor); } private bool RenderesProgressText { get { return _ProgressTextVisible && (!_IsEndlessProgressBar || !string.IsNullOrEmpty(_ProgressText)); } } private void PaintPieProgressBar(ItemPaintArgs e) { Graphics g = e.Graphics; Rectangle bounds = GetProgressBarBounds(); PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2); float penWidth = (float)Math.Max(1.5f, bounds.Height * .2); bounds.Inflate(-1, -1); bounds.Width--; bounds.Height--; float borderWidth = 1f; if (bounds.Height > 31) borderWidth = bounds.Height * .05f; if (!_IsEndlessProgressBar) { int value = GetValue(); Rectangle pieBounds = bounds; pieBounds.Inflate(-(int)borderWidth, -(int)borderWidth); int sweepAngle = (int)(360 * ((double)value / Math.Max(1, (_Maximum - _Minimum)))); using (SolidBrush brush = new SolidBrush(_ProgressColor)) { g.FillPie(brush, pieBounds, 270, sweepAngle); } } else { Rectangle pieBounds = bounds; pieBounds.Inflate(-(int)borderWidth, -(int)borderWidth); int startAngle = (int)(360 * _EndlessProgressValue / (double)_SpokeCount); int sweepAngle = 90; using (SolidBrush brush = new SolidBrush(_ProgressColor)) { g.FillPie(brush, pieBounds, startAngle, sweepAngle); } } Rectangle borderBounds = bounds; borderBounds.Width--; borderBounds.Height--; borderBounds.Offset(1, 1); using (Pen pen = new Pen(_PieBorderDark, borderWidth)) { pen.Alignment = PenAlignment.Inset; g.DrawEllipse(pen, borderBounds); } borderBounds.Offset(-1, -1); //using (Pen pen = new Pen(_PieBorderLight, borderWidth)) // g.DrawEllipse(pen, borderBounds); using (Pen pen = new Pen(_PieBorderLight, borderWidth + .5f)) { pen.Alignment = PenAlignment.Inset; g.DrawEllipse(pen, borderBounds); g.DrawEllipse(pen, borderBounds); } if (RenderesProgressText) { bounds.Offset(1, 0); PaintProgressText(g, bounds, (int)(bounds.Height * .4f), e.Font); } } private void PaintSpokeProgressBar(ItemPaintArgs e) { Graphics g = e.Graphics; Rectangle bounds = GetProgressBarBounds(); // Account for border and shade bounds.Width -= 2; bounds.Height -= 2; PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2); float penWidth = (float)Math.Max(1.5f, bounds.Height * .2); //bounds.Inflate(-(int)(penWidth / 2), -(int)(penWidth / 2)); GraphicsPath clipPath = new GraphicsPath(); Rectangle clipPathEllipse = bounds; clipPathEllipse.Inflate(-(int)((float)bounds.Height / 3f), -(int)((float)bounds.Height / 3f)); clipPath.AddEllipse(clipPathEllipse); Region oldClip = g.Clip; g.SetClip(clipPath, CombineMode.Exclude); if (!_IsEndlessProgressBar) { int value = GetValue(); int sweepAngle = (int)(360 * ((double)value / Math.Max(1, (_Maximum - _Minimum)))); using (SolidBrush brush = new SolidBrush(_ProgressColor)) { g.FillPie(brush, bounds, 270, sweepAngle); } } else { int startAngle = (int)(360 * _EndlessProgressValue / (double)_SpokeCount); int sweepAngle = 90; using (SolidBrush brush = new SolidBrush(_ProgressColor)) { g.FillPie(brush, bounds, startAngle, sweepAngle); } } int radius = bounds.Width / 2; PointF shadeCenterPoint = centerPoint; float shadeSpokeWidth = 1f; float circleWidth = 1f; float shadeCircleWidth = 1f; //if (bounds.Height < 28) //{ // shadeSpokeWidth = 1f; // circleWidth = 1f; // shadeCircleWidth = 1f; //} radius--; using (Pen pen = new Pen(_SpokeBorderDark, shadeSpokeWidth)) { pen.Alignment = PenAlignment.Right; PointF p1 = GetCoordinate(centerPoint, radius, 315); p1.X++; PointF p2 = GetCoordinate(shadeCenterPoint, radius, 135); p2.X++; g.DrawLine(pen, p1, p2); g.DrawLine(pen, p1, p2); p1 = GetCoordinate(centerPoint, radius, 270); p1.X++; p2 = GetCoordinate(shadeCenterPoint, radius, 90); p2.X++; g.DrawLine(pen, p1, p2); p1 = GetCoordinate(centerPoint, radius, 225); p1.Y++; p2 = GetCoordinate(shadeCenterPoint, radius, 45); p2.Y++; g.DrawLine(pen, p1, p2); p1 = GetCoordinate(centerPoint, radius, 180); p1.Y++; p2 = GetCoordinate(shadeCenterPoint, radius, 0); p2.Y++; g.DrawLine(pen, p1, p2); } using (Pen pen = new Pen(_SpokeBorderDark, shadeCircleWidth)) { pen.Alignment = PenAlignment.Inset; Rectangle shadeBounds = bounds; shadeBounds.Offset(1, 1); //shadeBounds.Width--; //shadeBounds.Height--; g.DrawEllipse(pen, shadeBounds); } using (Pen pen = new Pen(_SpokeBorderLight, shadeSpokeWidth)) { g.DrawLine(pen, GetCoordinate(centerPoint, radius, 315), GetCoordinate(centerPoint, radius, 135)); g.DrawLine(pen, GetCoordinate(centerPoint, radius, 270), GetCoordinate(centerPoint, radius, 90)); g.DrawLine(pen, GetCoordinate(centerPoint, radius, 225), GetCoordinate(centerPoint, radius, 45)); g.DrawLine(pen, GetCoordinate(centerPoint, radius, 180), GetCoordinate(centerPoint, radius, 0)); } using (Pen pen = new Pen(_SpokeBorderLight, circleWidth)) { pen.Alignment = PenAlignment.Inset; g.DrawEllipse(pen, bounds); g.DrawEllipse(pen, bounds); } g.Clip = oldClip; oldClip.Dispose(); float innerCircleWidth = 1f; using (Pen pen = new Pen(Color.White, innerCircleWidth)) { pen.Alignment = PenAlignment.Inset; g.DrawEllipse(pen, clipPathEllipse); g.DrawEllipse(pen, clipPathEllipse); } if (RenderesProgressText) { //bounds.Y--; PaintProgressText(g, bounds, (int)(bounds.Height * .35f), e.Font); } } private int GetValue() { int value = Math.Min(_Maximum, Math.Max(_Minimum, _Value)); if (this.DesignMode && value == _Minimum) value = (int)(_Maximum * .75d); return value; } private void PaintDonutProgressBar(ItemPaintArgs e) { Graphics g = e.Graphics; RectangleF bounds = GetProgressBarBounds(); PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2); float penWidth = (float)Math.Max(1.5f, bounds.Height * .2); bounds.Inflate(-Math.Max(1, penWidth / 2), -Math.Max(1, penWidth / 2)); bounds.Width--; bounds.Height--; if (!_IsEndlessProgressBar) { int value = GetValue(); int sweepAngle = (int)(360 * ((double)value / Math.Max(1, (_Maximum - _Minimum)))); using (Pen pen = new Pen(_ProgressColor, penWidth)) { g.DrawArc(pen, bounds, 270, sweepAngle); } } else { int startAngle = (int)(360 * _EndlessProgressValue / (double)_SpokeCount); int sweepAngle = 140; using (Pen pen = new Pen(_ProgressColor, penWidth)) { pen.EndCap = LineCap.Round; pen.StartCap = LineCap.Round; g.DrawArc(pen, bounds, startAngle, sweepAngle); } } if (RenderesProgressText) { PaintProgressText(g, bounds, (int)(bounds.Height * .4f), e.Font); } } private void PaintDotProgressBar(ItemPaintArgs e) { Graphics g = e.Graphics; Rectangle bounds = GetProgressBarBounds(); PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2); int outerRadius = 14; int circleSize = 2; outerRadius = (int)Math.Round(bounds.Width * .40d); circleSize = Math.Max(1, (int)Math.Round(outerRadius * .25d)); int value = GetValue(); if (!_IsEndlessProgressBar) { int spoke = (int)Math.Round(_SpokeCount * ((double)value / Math.Max(1, (_Maximum - _Minimum)))); for (int i = 0; i < spoke; i++) { PointF endPoint = GetCoordinate(centerPoint, outerRadius, _SpokeAngles[i]); RectangleF circleBounds = new RectangleF(endPoint, Size.Empty); circleBounds.Inflate(circleSize, circleSize); using (SolidBrush brush = new SolidBrush(ColorFromSpokeIndex(i))) { g.FillEllipse(brush, circleBounds); } } } else if (_IsRunning) // Endless Progress Bar { int position = _EndlessProgressValue; for (int i = 0; i < _SpokeCount; i++) { position = position % _SpokeCount; PointF endPoint = GetCoordinate(centerPoint, outerRadius, _SpokeAngles[position]); RectangleF circleBounds = new RectangleF(endPoint, Size.Empty); circleBounds.Inflate(circleSize, circleSize); using (SolidBrush brush = new SolidBrush(ColorFromSpokeIndex(i))) { g.FillEllipse(brush, circleBounds); } position++; } } if (RenderesProgressText) { PaintProgressText(g, bounds, (int)(bounds.Height * .35f), e.Font); } } private int _SpokeCount = 12; private int _EndlessProgressValue = 0; private void PaintLineProgressBar(ItemPaintArgs e) { //e.Graphics.DrawRectangle(Pens.Green, new Rectangle(0, 0, this.Width - 1, this.Height - 1)); Graphics g = e.Graphics; Rectangle bounds = GetProgressBarBounds(); PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2); int innerRadius = 6; int outerRadius = 14; int spokeSize = 2; outerRadius = (int)Math.Round(bounds.Width * .45d); innerRadius = (int)Math.Round(outerRadius * .45d); spokeSize = Math.Max(2, (int)Math.Round(outerRadius * .15d)); int value = GetValue(); if (!_IsEndlessProgressBar) { int spoke = (int)Math.Round(_SpokeCount * ((double)value / Math.Max(1, (_Maximum - _Minimum)))); for (int i = 0; i < spoke; i++) { PointF startPoint = GetCoordinate(centerPoint, innerRadius, _SpokeAngles[i]); PointF endPoint = GetCoordinate(centerPoint, outerRadius, _SpokeAngles[i]); using (Pen pen = new Pen(ColorFromSpokeIndex(i), spokeSize)) { pen.EndCap = System.Drawing.Drawing2D.LineCap.Round; pen.StartCap = System.Drawing.Drawing2D.LineCap.Round; g.DrawLine(pen, startPoint, endPoint); } } } else if (_IsRunning) // Endless Progress Bar { int position = _EndlessProgressValue; for (int i = 0; i < _SpokeCount; i++) { position = position % _SpokeCount; PointF startPoint = GetCoordinate(centerPoint, innerRadius, _SpokeAngles[position]); PointF endPoint = GetCoordinate(centerPoint, outerRadius, _SpokeAngles[position]); using (Pen pen = new Pen(ColorFromSpokeIndex(i), spokeSize)) { pen.EndCap = System.Drawing.Drawing2D.LineCap.Round; pen.StartCap = System.Drawing.Drawing2D.LineCap.Round; g.DrawLine(pen, startPoint, endPoint); } position++; } } if (RenderesProgressText) { PaintProgressText(g, bounds, innerRadius, e.Font); } } private string GetProgressValueText() { try { if (!string.IsNullOrEmpty(_ProgressText)) return _ProgressText; return string.Format(_ProgressTextFormat, _Value); } catch { return "Format Error"; } } private void PaintProgressText(Graphics g, RectangleF bounds, int innerRadius, Font baseFont) { //StringFormat format = (StringFormat)StringFormat.GenericDefault.Clone(); //format.Alignment = StringAlignment.Center; //format.LineAlignment = StringAlignment.Center; //format.FormatFlags = StringFormatFlags.NoWrap; //format.Trimming = StringTrimming.None; eTextFormat format = eTextFormat.HorizontalCenter | eTextFormat.VerticalCenter | eTextFormat.NoPadding | eTextFormat.SingleLine | eTextFormat.NoClipping; float fontSize = Math.Max(1f, innerRadius / 1.8f); Color textColor = _ProgressTextColor.IsEmpty ? _ProgressColor : _ProgressTextColor; using (Font font = new Font(baseFont.FontFamily, fontSize, FontStyle.Regular)) { TextDrawing.DrawString(g, GetProgressValueText(), font, textColor, Rectangle.Round(bounds), format); //using (SolidBrush brush = new SolidBrush(textColor)) // g.DrawString(string.Format("{0}%", _Value), font, brush, bounds, format); } } private Color ColorFromSpokeIndex(int spokeIndex, int spokeCount) { return Color.FromArgb((int)(210 * (double)spokeIndex / spokeCount) + 45, _ProgressColor); } private Color ColorFromSpokeIndex(int spokeIndex) { return ColorFromSpokeIndex(spokeIndex, _SpokeCount); } private PointF GetCoordinate(PointF centerPoint, int radius, double angle) { double dblAngle = Math.PI * angle / 180; return new PointF(centerPoint.X + radius * (float)Math.Cos(dblAngle), centerPoint.Y + radius * (float)Math.Sin(dblAngle)); } internal static readonly int TextContentSpacing = 3; private Size _TextSize = Size.Empty; public override void RecalcSize() { int diameter = _Diameter; if (!(this.ContainerControl is CircularProgress)) diameter = Dpi.Width(_Diameter); Rectangle r = new Rectangle(m_Rect.X, m_Rect.Y, diameter, diameter); if (_TextVisible && !string.IsNullOrEmpty(this.Text)) { Control parent = this.ContainerControl as Control; if (parent != null) { Font font = parent.Font; using (Graphics g = parent.CreateGraphics()) { Size textSize = ButtonItemLayout.MeasureItemText(this, g, _TextWidth, font, (_TextWidth > 0 ? eTextFormat.WordBreak : eTextFormat.SingleLine), parent.RightToLeft == RightToLeft.Yes); _TextSize = textSize; textSize.Width += _TextPadding.Horizontal; textSize.Height += _TextPadding.Vertical; int textContentSpacing = Dpi.Width(TextContentSpacing); if (_TextPosition == eTextPosition.Left || _TextPosition == eTextPosition.Right) { textSize.Width += textContentSpacing; r.Width += textSize.Width; r.Height = Math.Max(r.Height, textSize.Height); } else { textSize.Height += textContentSpacing; r.Height += textSize.Height; r.Width = Math.Max(r.Width, textSize.Width); } } } } m_Rect = r; base.RecalcSize(); } private Rectangle _ProgressBarBounds = Rectangle.Empty; private Rectangle GetProgressBarBounds() { int diameter = _Diameter; if (!(this.ContainerControl is CircularProgress)) diameter = Dpi.Width(_Diameter); if (string.IsNullOrEmpty(Text) || !_TextVisible) { return new Rectangle(m_Rect.X + (m_Rect.Width - diameter) / 2, m_Rect.Y + (m_Rect.Height - diameter) / 2, diameter, diameter); } if (_TextPosition == eTextPosition.Top) { return new Rectangle(m_Rect.X + (m_Rect.Width - diameter) / 2, m_Rect.Y + (m_Rect.Height - diameter), diameter, diameter); } else if (_TextPosition == eTextPosition.Right) { return new Rectangle(m_Rect.X, m_Rect.Y + (m_Rect.Height - diameter) / 2, diameter, diameter); } else if (_TextPosition == eTextPosition.Left) { return new Rectangle(m_Rect.Right - diameter, m_Rect.Y + (m_Rect.Height - diameter) / 2, diameter, diameter); } else if (_TextPosition == eTextPosition.Bottom) { return new Rectangle(m_Rect.X + (m_Rect.Width - diameter) / 2, m_Rect.Y, diameter, diameter); } return new Rectangle(m_Rect.X, m_Rect.Y, diameter, diameter); } private double[] _SpokeAngles = null; private double[] GetSpokeAngles(int numberOfSpokes) { double[] angles = new double[numberOfSpokes]; double angleStep = 360d / numberOfSpokes; for (int i = 0; i < numberOfSpokes; i++) angles[i] = (i == 0 ? 270 + angleStep : angles[i - 1] + angleStep); return angles; } private eCircularProgressType _ProgressBarType = eCircularProgressType.Line; /// /// Gets or sets the circular progress bar type. /// [DefaultValue(eCircularProgressType.Line), Category("Appearance"), Description("Indicates circular progress bar type.")] public eCircularProgressType ProgressBarType { get { return _ProgressBarType; } set { if (value != _ProgressBarType) { eCircularProgressType oldValue = _ProgressBarType; _ProgressBarType = value; OnProgressBarTypeChanged(oldValue, value); } } } private void OnProgressBarTypeChanged(eCircularProgressType oldValue, eCircularProgressType newValue) { OnPropertyChanged(new PropertyChangedEventArgs("ProgressBarType")); this.Refresh(); } private int _Maximum = 100; /// /// Gets or sets the maximum value of the progress bar. /// [Description("Indicates maximum value of the progress bar."), Category("Behavior"), DefaultValue(100)] public int Maximum { get { return _Maximum; } set { if (value != _Maximum) { int oldValue = _Maximum; _Maximum = value; OnMaximumChanged(oldValue, value); } } } private void OnMaximumChanged(int oldValue, int newValue) { OnPropertyChanged(new PropertyChangedEventArgs("Maximum")); CoerceValue(); } private int _Minimum = 0; /// /// Gets or sets the minimum value of the progress bar. /// [Description("Indicates minimum value of the progress bar."), Category("Behavior"), DefaultValue(0)] public int Minimum { get { return _Minimum; } set { if (value != _Minimum) { int oldValue = _Minimum; _Minimum = value; OnMinimumChanged(oldValue, value); } } } private void OnMinimumChanged(int oldValue, int newValue) { OnPropertyChanged(new PropertyChangedEventArgs("Minimum")); CoerceValue(); } private void CoerceValue() { int newValue = _Value; if (_Value < _Minimum) newValue = _Minimum; else if (_Value > _Maximum) newValue = _Maximum; Value = newValue; } private Color _ProgressTextColor = Color.Empty; /// /// Gets or sets the color of the progress percentage text. /// [Category("Appearance"), Description("Indicates color of progress percentage text")] public Color ProgressTextColor { get { return _ProgressTextColor; } set { _ProgressTextColor = value; this.Refresh(); } } /// /// Gets whether property should be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeProgressTextColor() { return !_ProgressTextColor.IsEmpty; } /// /// Resets property to its default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetProgressTextColor() { this.ProgressTextColor = Color.Empty; } private bool _ProgressTextVisible = false; /// /// Gets or sets whether text that displays the progress bar completion percentage text is visible. Default value is false. /// [DefaultValue(false), Category("Appearance"), Description("Indicates whether text that displays the progress bar completion percentage text is visible")] public bool ProgressTextVisible { get { return _ProgressTextVisible; } set { if (value != _ProgressTextVisible) { bool oldValue = _ProgressTextVisible; _ProgressTextVisible = value; OnProgressTextVisibleChanged(oldValue, value); } } } private void OnProgressTextVisibleChanged(bool oldValue, bool newValue) { OnPropertyChanged(new PropertyChangedEventArgs("ProgressTextVisible")); this.Refresh(); } private string _ProgressText = ""; /// /// Gets or sets the text displayed on top of the circular progress bar. /// [DefaultValue(""), Category("Appearance"), Description("Indicates text displayed on top of the circular progress bar.")] public string ProgressText { get { return _ProgressText; } set { if (value != _ProgressText) { string oldValue = _ProgressText; _ProgressText = value; OnProgressTextChanged(oldValue, value); } } } private void OnProgressTextChanged(string oldValue, string newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("ProgressText")); Refresh(); } private int _Value; /// /// Gets or sets the current value of the progress bar. /// [Description("Indicates current value of the progress bar."), Category("Behavior"), DefaultValue(0)] public int Value { get { return _Value; } set { value = Math.Min(_Maximum, Math.Max(value, _Minimum)); if (value != _Value) { int oldValue = _Value; _Value = value; OnValueChanged(oldValue, value); } } } private void OnValueChanged(int oldValue, int newValue) { if (!_IsEndlessProgressBar) this.Refresh(); OnValueChanged(EventArgs.Empty); OnPropertyChanged(new PropertyChangedEventArgs("Value")); } /// /// Occurs when Value property has changed. /// public event EventHandler ValueChanged; /// /// Raises ValueChanged event. /// /// Provides event arguments. protected virtual void OnValueChanged(EventArgs e) { EventHandler handler = ValueChanged; if (handler != null) handler(this, e); } /// /// Called when property on CircularProgressBar changes. /// /// Property Change Arguments protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { } private void MoveEndlessProgressBar() { if (!this.IsRunning || this.IsDisposed || _IsDisposing) return; if (this.InvokeRequired) { this.BeginInvoke(new MethodInvoker(delegate() { MoveEndlessProgressBar(); })); return; } _EndlessProgressValue = ++_EndlessProgressValue % _SpokeCount; this.Refresh(); Control container = this.ContainerControl as Control; if (container != null) container.Update(); } private bool _IsEndlessProgressBar = false; private BackgroundWorker _LoopWorker = null; /// /// Starts the progress bar loop for endless type progress bar. Progress bar will continue to run until Stop() method is called. /// public void Start() { if (_IsRunning) return; _IsEndlessProgressBar = true; _IsRunning = true; _LoopWorker = new BackgroundWorker(); _LoopWorker.WorkerSupportsCancellation = true; _LoopWorker.DoWork += LoopWorkerDoWork; _LoopWorker.RunWorkerAsync(); } void LoopWorkerDoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = (BackgroundWorker)sender; while (!worker.CancellationPending) { MoveEndlessProgressBar(); try { using ( System.Threading.ManualResetEvent wait = new System.Threading.ManualResetEvent(false)) wait.WaitOne(_AnimationSpeed); //Thread.Sleep(_AnimationSpeed); } catch { e.Cancel = true; } } e.Cancel = true; } /// /// Stops the progress bar loop for endless type progress bar. /// public void Stop() { Stop(false); } public void Stop(bool disposing) { if (!_IsRunning) return; _IsEndlessProgressBar = false; _IsRunning = false; BackgroundWorker worker = _LoopWorker; _LoopWorker = null; worker.CancelAsync(); while (worker.IsBusy) Application.DoEvents(); worker.DoWork -= LoopWorkerDoWork; worker.Dispose(); if (!disposing) this.Refresh(); } private bool _IsRunning = false; /// /// Gets or sets whether endless type progress bar is running. /// [Browsable(false), DefaultValue(false)] public bool IsRunning { get { return _IsRunning; } set { if (_IsRunning != value) { if (value) Start(); else Stop(); } } } private static readonly Color DefaultProgressColor = Color.DarkSlateGray;//Color.FromArgb(143, 223, 95); private Color _ProgressColor = DefaultProgressColor; /// /// Gets or sets the color of the color of progress indicator. /// [Category("Columns"), Description("Indicates color of progress indicator.")] public Color ProgressColor { get { return _ProgressColor; } set { _ProgressColor = value; this.Refresh(); } } /// /// Gets whether property should be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeProgressColor() { return _ProgressColor != DefaultProgressColor; } /// /// Resets property to its default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetProgressColor() { this.ProgressColor = DefaultProgressColor; } private int _Diameter = 24; /// /// Gets or sets circular progress indicator diameter in pixels. /// [DefaultValue(24), Category("Appearance"), Description("Indicates circular progress indicator diameter in pixels.")] public int Diameter { get { return _Diameter; } set { if (value != _Diameter) { int oldValue = _Diameter; _Diameter = value; OnDiameterChanged(oldValue, value); } } } private void OnDiameterChanged(int oldValue, int newValue) { OnPropertyChanged(new PropertyChangedEventArgs("Diameter")); NeedRecalcSize = true; this.Refresh(); } private eTextPosition _TextPosition = eTextPosition.Left; /// /// Gets or sets the text position in relation to the circular progress indicator. /// [DefaultValue(eTextPosition.Left), Category("Appearance"), Description("Indicatesd text position in relation to the circular progress indicator.")] public eTextPosition TextPosition { get { return _TextPosition; } set { if (value != _TextPosition) { eTextPosition oldValue = _TextPosition; _TextPosition = value; OnTextPositionChanged(oldValue, value); } } } private void OnTextPositionChanged(eTextPosition oldValue, eTextPosition newValue) { NeedRecalcSize = true; this.Refresh(); OnPropertyChanged(new PropertyChangedEventArgs("TextPosition")); } private bool _TextVisible = true; /// /// Gets or sets whether text/label displayed next to the item is visible. /// [DefaultValue(true), Category("Appearance"), Description("Indicates whether caption/label set using Text property is visible.")] public bool TextVisible { get { return _TextVisible; } set { if (value != _TextVisible) { bool oldValue = _TextVisible; _TextVisible = value; OnTextVisibleChanged(oldValue, value); } } } private void OnTextVisibleChanged(bool oldValue, bool newValue) { OnPropertyChanged(new PropertyChangedEventArgs("TextVisible")); NeedRecalcSize = true; this.Refresh(); } private int _TextWidth = 0; /// /// Gets or sets the suggested text-width. If you want to make sure that text you set wraps over multiple lines you can set suggested text-width so word break is performed. /// [DefaultValue(0), Category("Appearance"), Description("Indicates suggested text-width. If you want to make sure that text you set wraps over multiple lines you can set suggested text-width so word break is performed.")] public int TextWidth { get { return _TextWidth; } set { if (value != _TextWidth) { int oldValue = _TextWidth; _TextWidth = value; OnTextWidthChanged(oldValue, value); } } } private void OnTextWidthChanged(int oldValue, int newValue) { OnPropertyChanged(new PropertyChangedEventArgs("TextWidth")); NeedRecalcSize = true; Refresh(); } private Padding _TextPadding = new Padding(0, 0, 0, 0); /// /// Gets or sets text padding. /// [Browsable(true), Category("Appearance"), Description("Gets or sets text padding."), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public Padding TextPadding { get { return _TextPadding; } } [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeTextPadding() { return _TextPadding.Bottom != 0 || _TextPadding.Top != 0 || _TextPadding.Left != 0 || _TextPadding.Right != 0; } [EditorBrowsable(EditorBrowsableState.Never)] public void ResetTextPadding() { _TextPadding = new Padding(0, 0, 0, 0); } private void TextPaddingPropertyChanged(object sender, PropertyChangedEventArgs e) { NeedRecalcSize = true; this.Refresh(); } private Color _TextColor = Color.Empty; /// /// Gets or sets the color of the text label. /// [Category("Columns"), Description("Indicates color of text label.")] public Color TextColor { get { return _TextColor; } set { _TextColor = value; this.Refresh(); } } /// /// Gets whether property should be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeTextColor() { return !_TextColor.IsEmpty; } /// /// Resets property to its default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetTextColor() { this.TextColor = Color.Empty; } private static readonly Color DefaultPieBorderDarkColor = Color.FromArgb(50, Color.Black); private Color _PieBorderDark = DefaultPieBorderDarkColor; /// /// Gets or sets the color of the pie progress bar dark border. /// [Category("Pie"), Description("Indicates color of pie progress bar dark border.")] public Color PieBorderDark { get { return _PieBorderDark; } set { _PieBorderDark = value; this.Refresh(); } } /// /// Gets whether property should be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializePieBorderDark() { return _PieBorderDark != DefaultPieBorderDarkColor; } /// /// Resets property to its default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetPieBorderDark() { this.PieBorderDark = DefaultPieBorderDarkColor; } private static readonly Color DefaultPieBorderLightColor = Color.FromArgb(255, Color.White); private Color _PieBorderLight = DefaultPieBorderLightColor; /// /// Gets or sets the color of the pie progress bar light border. /// [Category("Pie"), Description("Indicates color of pie progress bar light border. ")] public Color PieBorderLight { get { return _PieBorderLight; } set { _PieBorderLight = value; this.Refresh(); } } /// /// Gets whether property should be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializePieBorderLight() { return _PieBorderLight != DefaultPieBorderLightColor; } /// /// Resets property to its default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetPieBorderLight() { this.PieBorderLight = DefaultPieBorderLightColor; } private static readonly Color DefaultSpokeBorderDarkColor = Color.FromArgb(48, Color.Black); private Color _SpokeBorderDark = DefaultSpokeBorderDarkColor; /// /// Gets or sets the color of the spoke progress bar dark border. /// [Category("Spoke"), Description("Indicates color of spoke progress bar dark border.")] public Color SpokeBorderDark { get { return _SpokeBorderDark; } set { _SpokeBorderDark = value; this.Refresh(); } } /// /// Gets whether property should be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeSpokeBorderDark() { return _SpokeBorderDark != DefaultSpokeBorderDarkColor; } /// /// Resets property to its default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetSpokeBorderDark() { this.SpokeBorderDark = DefaultSpokeBorderDarkColor; } private static readonly Color DefaultSpokeBorderLightColor = Color.FromArgb(255, Color.White); private Color _SpokeBorderLight = DefaultSpokeBorderLightColor; /// /// Gets or sets the color of the spoke progress bar light border. /// [Category("Spoke"), Description("Indicates color of spoke progress bar light border..")] public Color SpokeBorderLight { get { return _SpokeBorderLight; } set { _SpokeBorderLight = value; this.Refresh(); } } /// /// Gets whether property should be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeSpokeBorderLight() { return _SpokeBorderLight != DefaultSpokeBorderLightColor; } /// /// Resets property to its default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetSpokeBorderLight() { this.SpokeBorderLight = DefaultSpokeBorderLightColor; } private string _ProgressTextFormat = "{0}%"; /// /// Gets or sets format string for progress value. /// [DefaultValue("{0}%"), Category("Appearance"), Description("Indicates format string for progress value.")] public string ProgressTextFormat { get { return _ProgressTextFormat; } set { if (value != _ProgressTextFormat) { string oldValue = _ProgressTextFormat; _ProgressTextFormat = value; OnProgressTextFormatChanged(oldValue, value); } } } /// /// Called when ProgressTextFormat property has changed. /// /// Old property value /// New property value protected virtual void OnProgressTextFormatChanged(string oldValue, string newValue) { this.Refresh(); OnPropertyChanged(new PropertyChangedEventArgs("ProgressTextFormat")); } private int _AnimationSpeed = 100; /// /// Gets or sets the animation speed for endless running progress. Lower number means faster running. /// [DefaultValue(100), Description("Indicates the animation speed for endless running progress. Lower number means faster running."), Category("Behavior")] public int AnimationSpeed { get { return _AnimationSpeed; } set { if (value < 1) value = 1; _AnimationSpeed = value; } } #endregion } /// /// Defines available circular progress bar types. /// public enum eCircularProgressType { /// /// Line spokes progress bar. /// Line, /// /// Dot type/FireFox progress bar. /// Dot, /// /// Donut type progress bar. /// Donut, /// /// Spoke type progress bar. /// Spoke, /// /// Pie type progress bar. /// Pie } }