2011-02-02 78 views
18

Necesito ayuda en la implementación de una barra de progreso circular de la siguiente manera:Cómo crear un estilo circular ProgressBar

CircularProgressbar

¿Cómo debo aplicar el Círculo de llenar aumentando Value propiedad?

+0

el enlace en cuestión está roto. Por favor inserte una imagen en la pregunta. –

Respuesta

21

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:

  1. Crear un nuevo control de usuario
  2. Añadir nuevo valor, las propiedades de máximo y mínimo de dependencia a la misma.
  3. Controle los eventos Value, Maximum y Minimum property change en su control de usuario para calcular una propiedad Angle.
  4. Construye dos 'pie pieces' en el código detrás (mira this post) y agrégalos a la IU.
+3

¿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

+2

@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

+0

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

6

¿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:

  1. Añadir un círculo llenan de color amarillo.

  2. Agregue otro círculo en la parte superior con color naranja.

  3. 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.

  4. Clip el círculo en 2. con geometría devuelta en 3.

  5. Downvoter me devuelve mi repz.

+0

-1, Esto no se puede lograr con un convertidor de valor. Tiene que ser abordado en el árbol visual. – Josh

+0

Ver las nuevas ediciones. – NVM

+0

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

6

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.

enter image description here

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.

0

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:

  1. Coloque el código fuente en una nueva clase en cualquier parte de su proyecto de Windows Forms, el nombre de la clase 'CircularProgressBar.cs'.
  2. Compile el proyecto.
  3. Después de la compilación, debería ver un nuevo Control o "Componente" en la barra de herramientas.
  4. Arrastre y suelte este nuevo control en cualquier forma y personalice sus propiedades.

de control se ve así:

Control Preview

disfrutar.

Cuestiones relacionadas