Necesito ayuda en la implementación de una barra de progreso circular de la siguiente manera:Cómo crear un estilo circular ProgressBar
¿Cómo debo aplicar el Círculo de llenar aumentando Value
propiedad?
Necesito ayuda en la implementación de una barra de progreso circular de la siguiente manera:Cómo crear un estilo circular ProgressBar
¿Cómo debo aplicar el Círculo de llenar aumentando Value
propiedad?
Tiene un par de opciones: la primera es crear una plantilla del control ProgressBar
. Esto resulta ser un poco complicado. Escribí una publicación de blog que describe cómo use an attached ViewModel to achieve the required effect.
La otra alternativa es crear su propio control desde cero. Se podría hacer lo siguiente:
¿Y por qué no plantilla ProgressBar existente? CustomControls en WPF son sin pretensiones precisamente para este propósito. ¿Cuál es el punto de haber pasado por todo el rollo de crear un control sin sentido cuando no vas a reutilizarlo cuando todo lo que necesitas es una representación diferente de él en la IU? – NVM
@NVM, estoy de acuerdo con usted en principio, pero probablemente se requiera algún código aquí. Realmente no hay una manera fácil de crear un arco recortado usando las formas incorporadas en WPF usando XAML puro. Si uno usa el SDK de Expression Blend, hay una forma de Arco que puede hacerlo con bastante facilidad. Por lo tanto, es probable que el OP necesite crear algún tipo de control que pueda extraer el del pastel. Pero la implementación de la barra de progreso debe ser una plantilla que use este nuevo control "circular". – Josh
Ver mi respuesta editada. No es que no puedas hacerlo de la manera que sugieres. Creo que, en general, es mejor escribir tan poco código como puedas. – NVM
¿Has visto ValueConverter
s? Puede enlazar a la propiedad Value en la plantilla usando TemplateBinding
y usar un convertidor de valor apropiado para cambiar el valor a lo que es útil para una barra de progreso circular.
EDIT:
En la plantilla:
Añadir un círculo llenan de color amarillo.
Agregue otro círculo en la parte superior con color naranja.
utilizar un convertidor de valor (o convertidor de múltiples valor) para devolver un (segmento de arco utilizando posiblemente) la geometría de recorte para el círculo añadido en 2.
Clip el círculo en 2. con geometría devuelta en 3.
Downvoter me devuelve mi repz.
-1, Esto no se puede lograr con un convertidor de valor. Tiene que ser abordado en el árbol visual. – Josh
Ver las nuevas ediciones. – NVM
Quité la votación negativa porque sí, supongo que técnicamente podrías hacerlo de esa manera. :) Pero sería mucho más simple hacer esto con una subclase de UIElement y luego pegar eso en la plantilla de una ProgressBar. – Josh
Es un poco complicado pero no imposible. Aquí está la implementación de mi usando animaciones suaves para guiar. Los convertidores de valores se deben usar para crear una CircularProgressBar.
CircularProgressBar.cs
public partial class CircularProgressBar : ProgressBar
{
public CircularProgressBar()
{
this.ValueChanged += CircularProgressBar_ValueChanged;
}
void CircularProgressBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
CircularProgressBar bar = sender as CircularProgressBar;
double currentAngle = bar.Angle;
double targetAngle = e.NewValue/bar.Maximum * 359.999;
DoubleAnimation anim = new DoubleAnimation(currentAngle, targetAngle, TimeSpan.FromMilliseconds(500));
bar.BeginAnimation(CircularProgressBar.AngleProperty, anim, HandoffBehavior.SnapshotAndReplace);
}
public double Angle
{
get { return (double)GetValue(AngleProperty); }
set { SetValue(AngleProperty, value); }
}
// Using a DependencyProperty as the backing store for Angle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(0.0));
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
// Using a DependencyProperty as the backing store for StrokeThickness. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StrokeThicknessProperty =
DependencyProperty.Register("StrokeThickness", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(10.0));
}
AngleToPointConverter.cs
class AngleToPointConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double angle = (double)value;
double radius = 50;
double piang = angle * Math.PI/180;
double px = Math.Sin(piang) * radius + radius;
double py = -Math.Cos(piang) * radius + radius;
return new Point(px, py);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
AngleToIsLargeConverter.cs
class AngleToIsLargeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double angle = (double)value;
return angle > 180;
}
public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
App.xaml
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
xmlns:my="clr-namespace:WpfApplication1">
<Application.Resources>
<my:AngleToPointConverter x:Key="prConverter"/>
<my:AngleToIsLargeConverter x:Key="isLargeConverter"/>
<Style x:Key="circularProgressBar" TargetType="my:CircularProgressBar">
<Setter Property="Value" Value="10"/>
<Setter Property="Maximum" Value="100"/>
<Setter Property="StrokeThickness" Value="10"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:CircularProgressBar">
<Canvas Width="100" Height="100">
<Ellipse Width="100" Height="100" Stroke="LightGray"
StrokeThickness="1"/>
<Path Stroke="{TemplateBinding Background}"
StrokeThickness="{TemplateBinding StrokeThickness}">
<Path.Data>
<PathGeometry>
<PathFigure x:Name="fig" StartPoint="50,0">
<ArcSegment RotationAngle="0" SweepDirection="Clockwise"
Size="50,50"
Point="{Binding Path=Angle, Converter={StaticResource prConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}"
IsLargeArc="{Binding Path=Angle, Converter={StaticResource isLargeConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}"
>
</ArcSegment>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Border Width="100" Height="100">
<TextBlock Foreground="Gray" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Path=Value, StringFormat={}%{0},
RelativeSource={RelativeSource TemplatedParent}}"
FontSize="{TemplateBinding FontSize}"/>
</Border>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
Puede ser más personalizada mediante la adición de unas pocas propiedades más como InnerRadius, radio, etc.
Sé que esto es un viejo problema, pero de todos modos aquí está mi solución:
para Winforms:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public class CircularProgressBar : Control
{
/* CREDITS:
* Autor: Sajjad Arif Gul/October 12, 2016/C#, Source Codes
* https://www.csharpens.com/c-sharp/circular-progress-bar-in-c-sharp-windows-form-applications-23/
* Modified by Jhollman Chacon, 2017 */
#region Enums
public enum _ProgressShape
{
Round,
Flat
}
#endregion
#region Variables
private long _Value;
private long _Maximum = 100;
private Color _ProgressColor1 = Color.Orange;
private Color _ProgressColor2 = Color.Orange;
private Color _LineColor = Color.Silver;
private _ProgressShape ProgressShapeVal;
#endregion
#region Custom Properties
public long Value
{
get { return _Value; }
set
{
if (value > _Maximum)
value = _Maximum;
_Value = value;
Invalidate();
}
}
public long Maximum
{
get { return _Maximum; }
set
{
if (value < 1)
value = 1;
_Maximum = value;
Invalidate();
}
}
public Color ProgressColor1
{
get { return _ProgressColor1; }
set
{
_ProgressColor1 = value;
Invalidate();
}
}
public Color ProgressColor2
{
get { return _ProgressColor2; }
set
{
_ProgressColor2 = value;
Invalidate();
}
}
public Color LineColor
{
get { return _LineColor; }
set
{
_LineColor = value;
Invalidate();
}
}
public _ProgressShape ProgressShape
{
get { return ProgressShapeVal; }
set
{
ProgressShapeVal = value;
Invalidate();
}
}
#endregion
#region EventArgs
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
SetStandardSize();
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
SetStandardSize();
}
protected override void OnPaintBackground(PaintEventArgs p)
{
base.OnPaintBackground(p);
}
#endregion
#region Methods
public CircularProgressBar()
{
Size = new Size(130, 130);
Font = new Font("Segoe UI", 15);
MinimumSize = new Size(100, 100);
DoubleBuffered = true;
Value = 57;
ProgressShape = _ProgressShape.Flat;
this.ForeColor = Color.DimGray;
}
private void SetStandardSize()
{
int _Size = Math.Max(Width, Height);
Size = new Size(_Size, _Size);
}
public void Increment(int Val)
{
this._Value += Val;
Invalidate();
}
public void Decrement(int Val)
{
this._Value -= Val;
Invalidate();
}
#endregion
#region Events
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (Bitmap bitmap = new Bitmap(this.Width, this.Height))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.Clear(this.BackColor);
// Dibuja la Linea
using (Pen pen2 = new Pen(LineColor))
{
graphics.DrawEllipse(pen2, 0x18 - 6, 0x18 - 6, (this.Width - 0x30) + 12, (this.Height - 0x30) + 12);
}
//Dibuja la Barra de Progreso
using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle, this._ProgressColor1, this._ProgressColor2, LinearGradientMode.ForwardDiagonal))
{
using (Pen pen = new Pen(brush, 14f))
{
switch (this.ProgressShapeVal)
{
case _ProgressShape.Round:
pen.StartCap = LineCap.Round;
pen.EndCap = LineCap.Round;
break;
case _ProgressShape.Flat:
pen.StartCap = LineCap.Flat;
pen.EndCap = LineCap.Flat;
break;
}
//Aqui se dibuja el Progreso
graphics.DrawArc(pen, 0x12, 0x12, (this.Width - 0x23) - 2, (this.Height - 0x23) - 2, -90, (int)Math.Round((double)((360.0/((double)this._Maximum)) * this._Value)));
}
}
//Dibuja el Texto de Progreso:
Brush FontColor = new SolidBrush(this.ForeColor);
SizeF MS = graphics.MeasureString(Convert.ToString(Convert.ToInt32((100/_Maximum) * _Value)), Font);
graphics.DrawString(Convert.ToString(Convert.ToInt32((100/_Maximum) * _Value)), Font, FontColor, Convert.ToInt32(Width/2 - MS.Width/2), Convert.ToInt32(Height/2 - MS.Height/2));
e.Graphics.DrawImage(bitmap, 0, 0);
graphics.Dispose();
bitmap.Dispose();
}
}
}
#endregion
}
APLICACIÓN:
de control se ve así:
disfrutar.
el enlace en cuestión está roto. Por favor inserte una imagen en la pregunta. –