C# LineColor;} 设置 { _LineColor=值; 使无效(); } } [说明(“中间线主持人”)、类别(“外观”)] 公共整数线宽 { 获取{return\u LineWitdh;} 设置 { _LineWitdh=值; 使无效(); } }

C# LineColor;} 设置 { _LineColor=值; 使无效(); } } [说明(“中间线主持人”)、类别(“外观”)] 公共整数线宽 { 获取{return\u LineWitdh;} 设置 { _LineWitdh=值; 使无效(); } },c#,wpf,user-controls,progress-bar,C#,Wpf,User Controls,Progress Bar,LineColor;} 设置 { _LineColor=值; 使无效(); } } [说明(“中间线主持人”)、类别(“外观”)] 公共整数线宽 { 获取{return\u LineWitdh;} 设置 { _LineWitdh=值; 使无效(); } } [说明(“项目总站形式的建筑物”),类别(“外观”)] 公共_progressshapeprogressshape { 获取{return ProgressShapeVal;} 设置 { ProgressShapeVal=值; 使无效();

LineColor;} 设置 { _LineColor=值; 使无效(); } } [说明(“中间线主持人”)、类别(“外观”)] 公共整数线宽 { 获取{return\u LineWitdh;} 设置 { _LineWitdh=值; 使无效(); } } [说明(“项目总站形式的建筑物”),类别(“外观”)] 公共_progressshapeprogressshape { 获取{return ProgressShapeVal;} 设置 { ProgressShapeVal=值; 使无效(); } } [说明(“项目进展情况”)类别(“行为”)] 公共模式 { 获取{return ProgressTextMode;} 设置 { ProgressTextMode=值; 使无效(); } } [说明(“控制措施”),类别(“行为”)] 公共重写字符串文本{get;set;} #端区 #区域事件参数 受保护的覆盖void OnResize(事件参数e) { 基数(e); 设置标准尺寸(); } IzeChanged上的受保护覆盖无效(EventArgs e) { 基地.OnSizeChanged(e); 设置标准尺寸(); } PaintBackground上受保护的覆盖无效(PaintEventArgs p) { 基于绘画背景(p); } #端区 #区域方法 私有void SetStandardSize() { int _Size=Math.Max(宽度、高度); 尺寸=新尺寸(_尺寸,_尺寸); } 公共无效增量(int Val) { 这是._值+=Val; 使无效(); } 公共无效递减率(int Val) { 此值为._值-=Val; 使无效(); } #端区 #地区活动 受保护的覆盖无效OnPaint(PaintEventArgs e) { 基础漆(e); 使用(位图位图=新位图(this.Width,this.Height)) { 使用(Graphics=Graphics.FromImage(位图)) { graphics.InterpolationMode=System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear; graphics.CompositingQuality=System.Drawing.Drawing2D.CompositingQuality.HighQuality; graphics.PixelOffsetMode=System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; graphics.SmoothingMode=System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
//图形。清晰(颜色。透明);//为什么不模板化现有的ProgressBar?WPF中的CustomControls正是为了这个目的而设计的。当你不打算重用一个lookless控件,而你只需要在UI中对它进行不同的表示时,创建一个lookless控件有什么意义呢?@NVM,我原则上同意你的观点,但有些代码这里可能需要我。使用纯XAML,使用WPF中的内置形状创建剪切弧并不是一个简单的方法。如果使用Expression Blend SDK,则有一个弧形状可以非常轻松地完成此操作。因此OP可能需要创建某种控件来绘制饼图。但是pro的实现gress bar应该是使用此新“饼图”的模板控件。查看我编辑过的答案。这并不是说你不能按照你建议的方式来做。我认为一般来说,编写尽可能少的代码会更好。-1,这不能通过值转换器来实现。它必须在可视化树中进行处理。我删除了否决票,因为是的,我想你在技术上可以这样做。:)但是使用UIElement的子类执行此操作要简单得多,然后将其粘贴到ProgressBar的模板中。仅仅因为某些东西看起来更简单并不意味着这就是应该做的。为内置控件创建一个新的自定义控件时,所需的只是原始控件的不同视觉表示形式,这对很多人来说都是非常难看的原因。这就像在不了解自定义控件的情况下构建WPF自定义控件。我不是这么说的。我同意圆形进度条应该是带有修改模板的ProgressBar类型。我们不同意的是该模板应该如何构造。我认为可重用的PieShape或任何有意义的东西。我t可以在图表、繁忙指示器中重复使用。但最终结果将是一个控制模板,可以应用于普通的旧progressbar。Josh,我明白你的意思。问题是,公认的答案建议不同。我有一个UI密集型应用程序,有许多自定义形状,我可以安全地告诉你如何使用值转换器将远比创建自定义形状简单和干净。在创建自定义形状类时,有许多与WPF布局引擎相关的事情需要了解,我认为OP不需要那么复杂。相关链接已断开。请在问题中插入图像。+1000。效果非常好。感谢发布。s为我节省了很多时间。是的,是的!!请告诉我如何缩小你粘贴的@Ali进度条的大小,用相同的代码问一个新问题更好吗?
 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));
}
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();
    }
}
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();
    }
}
<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>
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public class CircularProgressBar : Control
{
    #region Enums

    public enum _ProgressShape
    {
        Round,
        Flat
    }

    public enum _TextMode
    {
        None,
        Value,
        Percentage,
        Custom
    }

    #endregion

    #region Private Variables

    private long _Value;
    private long _Maximum = 100;
    private int _LineWitdh = 1;
    private float _BarWidth = 14f;

    private Color _ProgressColor1 = Color.Orange;
    private Color _ProgressColor2 = Color.Orange;
    private Color _LineColor = Color.Silver;
    private LinearGradientMode _GradientMode = LinearGradientMode.ForwardDiagonal;
    private _ProgressShape ProgressShapeVal;
    private _TextMode ProgressTextMode;

    #endregion

    #region Contructor

    public CircularProgressBar()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        SetStyle(ControlStyles.Opaque, true);
        this.BackColor = SystemColors.Control;
        this.ForeColor = Color.DimGray;

        this.Size = new Size(130, 130);
        this.Font = new Font("Segoe UI", 15);
        this.MinimumSize = new Size(100, 100);
        this.DoubleBuffered = true;

        this.LineWidth = 1;
        this.LineColor = Color.DimGray;

        Value = 57;
        ProgressShape = _ProgressShape.Flat;
        TextMode = _TextMode.Percentage;
    }

    #endregion

    #region Public Custom Properties

    /// <summary>Determina el Valor del Progreso</summary>
    [Description("Valor Entero que determina la posision de la Barra de Progreso."), Category("Behavior")]
    public long Value
    {
        get { return _Value; }
        set
        {
            if (value > _Maximum)
                value = _Maximum;
            _Value = value;
            Invalidate();
        }
    }

    [Description("Obtiene o Establece el Valor Maximo de la barra de Progreso."), Category("Behavior")]
    public long Maximum
    {
        get { return _Maximum; }
        set
        {
            if (value < 1)
                value = 1;
            _Maximum = value;
            Invalidate();
        }
    }

    [Description("Color Inicial de la Barra de Progreso"), Category("Appearance")]
    public Color BarColor1
    {
        get { return _ProgressColor1; }
        set
        {
            _ProgressColor1 = value;
            Invalidate();
        }
    }

    [Description("Color Final de la Barra de Progreso"), Category("Appearance")]
    public Color BarColor2
    {
        get { return _ProgressColor2; }
        set
        {
            _ProgressColor2 = value;
            Invalidate();
        }
    }

    [Description("Ancho de la Barra de Progreso"), Category("Appearance")]
    public float BarWidth
    {
        get { return _BarWidth; }
        set
        {
            _BarWidth = value;
            Invalidate();
        }
    }

    [Description("Modo del Gradiente de Color"), Category("Appearance")]
    public LinearGradientMode GradientMode
    {
        get { return _GradientMode; }
        set
        {
            _GradientMode = value;
            Invalidate();
        }
    }

    [Description("Color de la Linea Intermedia"), Category("Appearance")]
    public Color LineColor
    {
        get { return _LineColor; }
        set
        {
            _LineColor = value;
            Invalidate();
        }
    }

    [Description("Ancho de la Linea Intermedia"), Category("Appearance")]
    public int LineWidth
    {
        get { return _LineWitdh; }
        set
        {
            _LineWitdh = value;
            Invalidate();
        }
    }

    [Description("Obtiene o Establece la Forma de los terminales de la barra de progreso."), Category("Appearance")]
    public _ProgressShape ProgressShape
    {
        get { return ProgressShapeVal; }
        set
        {
            ProgressShapeVal = value;
            Invalidate();
        }
    }

    [Description("Obtiene o Establece el Modo como se muestra el Texto dentro de la barra de Progreso."), Category("Behavior")]
    public _TextMode TextMode
    {
        get { return ProgressTextMode; }
        set
        {
            ProgressTextMode = value;
            Invalidate();
        }
    }

    [Description("Obtiene el Texto que se muestra dentro del Control"), Category("Behavior")]
    public override string Text { get; set; }

    #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

    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.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
                graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                //graphics.Clear(Color.Transparent); //<-- this.BackColor, SystemColors.Control, Color.Transparent

                PaintTransparentBackground(this, e);

                //Dibuja el circulo blanco interior:
                using (Brush mBackColor = new SolidBrush(this.BackColor))
                {
                    graphics.FillEllipse(mBackColor,
                            18, 18,
                            (this.Width - 0x30) + 12,
                            (this.Height - 0x30) + 12);
                }
                // Dibuja la delgada Linea gris del medio:
                using (Pen pen2 = new Pen(LineColor, this.LineWidth))
                {
                    graphics.DrawEllipse(pen2, 
                        18, 18,
                      (this.Width - 0x30) + 12, 
                      (this.Height - 0x30) + 12);
                }

                //Dibuja la Barra de Progreso
                using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle, 
                    this._ProgressColor1, this._ProgressColor2, this.GradientMode))
                {
                    using (Pen pen = new Pen(brush, this.BarWidth))
                    {
                        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 realmente la Barra de 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)));
                    }
                }

                #region Dibuja el Texto de Progreso

                switch (this.TextMode)
                {
                    case _TextMode.None:
                        this.Text = string.Empty;
                        break;

                    case _TextMode.Value:
                        this.Text = _Value.ToString();
                        break;

                    case _TextMode.Percentage:
                        this.Text = Convert.ToString(Convert.ToInt32((100 / _Maximum) * _Value));
                        break;

                    default:
                        break;
                }

                if (this.Text != string.Empty)
                {
                    using (Brush FontColor = new SolidBrush(this.ForeColor))
                    {
                        int ShadowOffset = 2;
                        SizeF MS = graphics.MeasureString(this.Text, this.Font);
                        SolidBrush shadowBrush = new SolidBrush(Color.FromArgb(100, this.ForeColor));

                        //Sombra del Texto:
                        graphics.DrawString(this.Text, this.Font, shadowBrush,
                            Convert.ToInt32(Width / 2 - MS.Width / 2) + ShadowOffset,
                            Convert.ToInt32(Height / 2 - MS.Height / 2) + ShadowOffset
                        );

                        //Texto del Control:
                        graphics.DrawString(this.Text, this.Font, FontColor,
                            Convert.ToInt32(Width / 2 - MS.Width / 2),
                            Convert.ToInt32(Height / 2 - MS.Height / 2));
                    }
                }

                #endregion

                //Aqui se Dibuja todo el Control:
                e.Graphics.DrawImage(bitmap, 0, 0);
                graphics.Dispose();
                bitmap.Dispose();
            }
        }
    }

    private static void PaintTransparentBackground(Control c, PaintEventArgs e)
    {
        if (c.Parent == null || !Application.RenderWithVisualStyles)
            return;

        ButtonRenderer.DrawParentBackground(e.Graphics, c.ClientRectangle, c);
    }

    /// <summary>Dibuja un Circulo Relleno de Color con los Bordes perfectos.</summary>
    /// <param name="g">'Canvas' del Objeto donde se va a dibujar</param>
    /// <param name="brush">Color y estilo del relleno</param>
    /// <param name="centerX">Centro del Circulo, en el eje X</param>
    /// <param name="centerY">Centro del Circulo, en el eje Y</param>
    /// <param name="radius">Radio del Circulo</param>
    private void FillCircle(Graphics g, Brush brush, float centerX, float centerY, float radius)
    {
        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

        using (System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath())
        {
            g.FillEllipse(brush, centerX - radius, centerY - radius,
                      radius + radius, radius + radius);
        }
    }

    #endregion
}