Как создать круговой стиль ProgressBar - PullRequest
22 голосов
/ 02 февраля 2011

Мне нужна помощь по реализации кругового индикатора, например:

CircularProgressbar

Как мне реализовать заполнение круга путем увеличения свойства Value?

Ответы [ 4 ]

22 голосов
/ 02 февраля 2011

У вас есть пара опций - первый - шаблон ProgressBar. Это оказывается немного сложнее. Я написал сообщение в блоге, в котором описывается, как использовать прилагаемую ViewModel для достижения необходимого эффекта .

Другой альтернативой является создание собственного элемента управления с нуля. Вы можете сделать следующее:

  1. Создать новый пользовательский элемент управления
  2. Добавить новые свойства Value, Maximum и Minimum для него.
  3. Обработайте события изменения значения Value, Maximum и Minimum в вашем пользовательском элементе управления для вычисления свойства Angle.
  4. Создайте два «кусочка» в коде (см. этот пост ) и добавьте их в пользовательский интерфейс.
12 голосов
/ 03 сентября 2016

Это немного сложно, но не невозможно.Вот моя реализация, использующая плавную анимацию для руководства.Для создания 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>

Itможно более индивидуально настроить, добавив еще несколько свойств, таких как InnerRadius, Radius и т. д.

8 голосов
/ 07 июня 2017

Я знаю, что это старая проблема, но в любом случае вот мое решение:

ДЛЯ 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
}

РЕАЛИЗАЦИЯ:

  1. Поместите исходный код в новый класс в любом месте вашего проекта WinForms, назовите класс ' CircularProgressBar.cs '.
  2. Скомпилируйте проект.
  3. После компиляции вы должны увидеть новый элемент управления или «Компонент» на панели инструментов.
  4. Перетащите этот новый элемент управления в любую форму и настройте его свойства.

Управление выглядит так:

Control Preview

Наслаждайтесь.

6 голосов
/ 02 февраля 2011

Вы смотрели на ValueConverter с?Вы можете привязать свойство Value в шаблоне, используя TemplateBinding, и использовать соответствующий конвертер значений, чтобы изменить значение на то, что полезно для круглого индикатора выполнения.

РЕДАКТИРОВАТЬ:

В шаблоне:

  1. Добавить заполнить круг желтым цветом.

  2. Добавитьеще один круг сверху с оранжевым цветом.

  3. Используйте преобразователь значений (или преобразователь нескольких значений), чтобы вернуть геометрию ограничения (возможно, с использованием дугового сегмента) для круга, добавленного в 2.

  4. Обрезать круг в 2. с геометрией, возвращенной в 3.

  5. Downvoter возвращает мне мой реп.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...