2011-02-02 5 views
18

Ho bisogno di aiuto per l'attuazione di una barra di avanzamento circolare in questo modo:Come creare una circolare stile ProgressBar

CircularProgressbar

Come devo realizzare il Circolo di colmare aumentando Value proprietà?

+0

il collegamento in questione è rotto. Si prega di inserire un'immagine nella domanda. –

risposta

21

Avete un paio di opzioni: la prima è di creare il modello del controllo ProgressBar. Questo risulta essere un po 'complicato. Ho scritto un post sul blog che descrive come use an attached ViewModel to achieve the required effect.

L'altra alternativa è creare il proprio controllo da zero. Si potrebbe fare la seguente:

  1. Creare un nuovo controllo utente
  2. Aggiungi nuovo valore, proprietà Massimo e il Minimo di dipendenza ad esso.
  3. Gestire gli eventi di modifica delle proprietà Valore, Massimo e Minimo nel controllo utente per calcolare una proprietà Angolo.
  4. Costruisci due "pezzi di torta" nel codice dietro (vedi this post) e aggiungili all'interfaccia utente.
+3

E perché non creare il modello ProgressBar esistente? I CustomControls in WPF sono esattamente per questo scopo. Qual è il punto di aver attraversato tutte le seccature di creare un controllo senza look quando non hai intenzione di riutilizzarlo quando tutto ciò di cui hai bisogno è una diversa rappresentazione di esso nell'interfaccia utente? – NVM

+2

@ NVM, sono d'accordo con te in linea di massima, ma probabilmente ci sarà bisogno di un codice qui. Non esiste un modo semplice per creare un arco di ritaglio utilizzando le forme incorporate in WPF utilizzando XAML puro. Se si utilizza Expression Blend SDK, c'è una forma ad arco che può farlo abbastanza facilmente. Quindi l'OP avrà probabilmente bisogno di creare un qualche tipo di controllo che possa disegnare la torta. Ma l'implementazione della barra di avanzamento dovrebbe essere un modello che utilizza questo nuovo controllo "a torta". – Josh

+0

Vedere la mia risposta modificata. Non è che non puoi farlo nel modo che suggerisci. Penso che in generale sia meglio scrivere meno codice possibile. – NVM

6

Hai guardato ValueConverter s? È possibile associare alla proprietà Value nel modello utilizzando TemplateBinding e utilizzare un convertitore di valori appropriato per modificare il valore in quello che è utile per una barra di avanzamento circolare.

EDIT:

Nel modello:

  1. Aggiungi un cerchio riempire con il colore giallo.

  2. Aggiungere un altro cerchio in cima con il colore arancione.

  3. utilizzare un convertitore valore (o convertitore di valori più) per restituire un (segmento di arco utilizzando possibilmente) la geometria di ritaglio per il cerchio aggiunto 2.

  4. clip del cerchio 2. con la geometria restituito in 3.

  5. Downvoter mi restituisce il mio repz.

+0

-1, Questo non può essere raggiunto con un convertitore di valori. Deve essere affrontato nell'albero visivo. – Josh

+0

Vedere le nuove modifiche. – NVM

+0

Ho rimosso il downvote perché sì, immagino che tecnicamente potresti farlo in quel modo. :) Ma sarebbe molto più semplice farlo con una sottoclasse di UIElement, quindi inserirla nel modello di una ProgressBar. – Josh

6

È un po 'complicato ma non impossibile. Ecco la mia implementazione utilizzando animazioni fluide da guidare. I convertitori di valori dovrebbero essere utilizzati per creare un 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> 

Può essere più personalizzato aggiungendo alcune proprietà come InnerRadius, Raggio ecc

0

So è un vecchio problema, ma comunque ecco la mia soluzione:

PER 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 
} 

ATTUAZIONE:

  1. Inserire il codice sorgente in una nuova classe ovunque nel progetto WinForms, il nome della classe 'CircularProgressBar.cs'.
  2. Compila il progetto.
  3. Dopo la compilazione si dovrebbe vedere un nuovo Controllo o 'Componente' sulla barra degli strumenti.
  4. Trascinare questo nuovo controllo in qualsiasi forma e personalizzarne le proprietà.

di controllo appare come questa

Control Preview

godere.