WPF Windows Process Bar / Pie

时间:2016-05-03 21:58:24

标签: wpf vb.net charts

使用WPF - 如何创建一个看起来像Windows进度栏的图表 - 圆环图? https://guyterry.files.wordpress.com/2015/07/upgradingrelax.jpg

2 个答案:

答案 0 :(得分:0)

尝试使用可用拖放组件中的环组件将其设置为用户控件。添加标签并制作一些可以修改的属性以获得特定结果。 请参阅this帖子。 或者尝试Tutorial

答案 1 :(得分:0)

试试这个:

using System;
using System.Globalization;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApplication1
{
    public class CircleProgress : Canvas
    {
        public static readonly DependencyProperty ValueProperty =
             DependencyProperty.Register("Value", typeof(double),
             typeof(CircleProgress), new FrameworkPropertyMetadata(180d, OnValueChanged));

        public static readonly DependencyProperty MinimumProperty =
             DependencyProperty.Register("Minimum", typeof(double),
             typeof(CircleProgress), new FrameworkPropertyMetadata(0d, OnValueChanged));


        public static readonly DependencyProperty MaximumProperty =
             DependencyProperty.Register("Maximum", typeof(double),
             typeof(CircleProgress), new FrameworkPropertyMetadata(360d, OnValueChanged));

        public static readonly DependencyProperty BackgroundCircleStrokeProperty =
             DependencyProperty.Register("BackgroundCircleStroke", typeof(Brush),
             typeof(CircleProgress), new FrameworkPropertyMetadata(Brushes.Gray, OnStrokeChanged));

        public static readonly DependencyProperty BackgroundCircleStrokeThicknessProperty =
             DependencyProperty.Register("BackgroundCircleStrokeThickness", typeof(double),
             typeof(CircleProgress), new FrameworkPropertyMetadata(5d, OnStrokeChanged));

        public static readonly DependencyProperty MainCircleStrokeProperty =
             DependencyProperty.Register("MainCircleStroke", typeof(Brush),
             typeof(CircleProgress), new FrameworkPropertyMetadata(Brushes.DeepSkyBlue, OnStrokeChanged));

        public static readonly DependencyProperty MainCircleStrokeThicknessProperty =
             DependencyProperty.Register("MainCircleStrokeThickness", typeof(double),
             typeof(CircleProgress), new FrameworkPropertyMetadata(5d, OnStrokeChanged));

        public static readonly DependencyProperty TextStrokeProperty =
             DependencyProperty.Register("TextStroke", typeof(Brush),
             typeof(CircleProgress), new FrameworkPropertyMetadata(Brushes.Black, OnStrokeChanged));

        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public double Minimum
        {
            get { return (double)GetValue(MinimumProperty); }
            set { SetValue(MinimumProperty, value); }
        }
        public double Maximum
        {
            get { return (double)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }
        public Brush BackgroundCircleStroke
        {
            get { return (Brush) GetValue(BackgroundCircleStrokeProperty); }
            set { SetValue(BackgroundCircleStrokeProperty, value); }
        }
        public Brush MainCircleStroke
        {
            get { return (Brush)GetValue(MainCircleStrokeProperty); }
            set { SetValue(MainCircleStrokeProperty, value); }
        }
        public Brush TextStroke
        {
            get { return (Brush)GetValue(MainCircleStrokeProperty); }
            set { SetValue(MainCircleStrokeProperty, value); }
        }
        public double BackgroundCircleStrokeThickness
        {
            get { return (double)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }
        public double MainCircleStrokeThickness
        {
            get { return (double)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }

        private readonly Path _backEllipse = new Path()
        {
            StrokeThickness = 5,
            Stroke = Brushes.Gray
        };

        private readonly Path _mainEllipse = new Path()
        {
            StrokeThickness = 5,
            Stroke = Brushes.DeepSkyBlue
        };

        private readonly Path _text = new Path()
        {
            StrokeThickness = 1,
            Fill = Brushes.Black
        };

        private double _radius = 10;
        private Point _center = new Point(0,0);
        private Point _startPoint = new Point(0,0);


        public CircleProgress()
        {
            _backEllipse.Data = new EllipseGeometry(new Point(Width/2, Height/2), _radius, _radius);
            _mainEllipse.Data = new PathGeometry()
            {
                Figures = new PathFigureCollection()
                {
                    new PathFigure(new Point(Width/2, 0), new PathSegmentCollection()
                    {
                        new ArcSegment(
                            (new RotateTransform(Value*(360/(Maximum - Minimum)), _center.X, _center.Y)).Transform(
                                _startPoint), new Size(_radius*2, _radius*2), 0, true, SweepDirection.Clockwise, false)
                    }, false)
                }
            };
            var text = new FormattedText(Value.ToString(CultureInfo.CurrentCulture),
                CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface("Palatino"),
                0.8* _radius,
                Brushes.Black);

            _text.Data = text.BuildGeometry(new Point(_center.X - text.Width/2, _center.Y - text.Height/2));


            SizeChanged += OnSizeChanged;
            Children.Add(_backEllipse);
            Children.Add(_mainEllipse);
            Children.Add(_text);
        }

        private void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            var quadSize = e.NewSize.Height <= e.NewSize.Width ? e.NewSize.Height : e.NewSize.Width;
            _radius = quadSize / 2;
            _center = new Point(e.NewSize.Width/2, e.NewSize.Height / 2);
            _startPoint = _center - new Vector(0, _radius);

            UpdateCircle(this);
        }

        /// <summary>
        /// Action when Value, Minimum or Maximum changed.
        /// </summary>
        /// <param name="d">Dependecy object.</param>
        /// <param name="e">EventArgs.</param>
        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var circle = d as CircleProgress;

            UpdateCircle(circle);   
        }

        private static void OnStrokeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var circle = d as CircleProgress;
            circle._backEllipse.Stroke = circle.BackgroundCircleStroke;
            circle._backEllipse.StrokeThickness = circle.BackgroundCircleStrokeThickness;

            circle._mainEllipse.Stroke = circle.BackgroundCircleStroke;
            circle._mainEllipse.StrokeThickness = circle.BackgroundCircleStrokeThickness;

            circle._text.Fill = circle.TextStroke;
        }

        /// <summary>
        /// Update Background and Main circles.
        /// </summary>
        /// <param name="circle">Reference to CircleProgress control.</param>
        private static void UpdateCircle(CircleProgress circle)
        {
            circle._backEllipse.Data = new EllipseGeometry(circle._center, circle._radius, circle._radius);

            if (Math.Abs(circle.Value*(360/(circle.Maximum - circle.Minimum)) - 360) < 0.0001)
                circle._mainEllipse.Data = new EllipseGeometry(circle._center, circle._radius, circle._radius);
            else
            {

                circle._mainEllipse.Data = new PathGeometry()
                {
                    Figures = new PathFigureCollection()
                    {
                        new PathFigure(circle._startPoint, new PathSegmentCollection()
                        {
                            new ArcSegment(
                                (new RotateTransform(circle.Value*(360/(circle.Maximum - circle.Minimum)),
                                    circle._center.X, circle._center.Y)).Transform(
                                        circle._startPoint), new Size(circle._radius, circle._radius), 0,
                                !(circle.Value*(360/(circle.Maximum - circle.Minimum)) <= 180), SweepDirection.Clockwise,
                                true)
                        }, false)
                    }
                };
            }
            var text = new FormattedText($"{circle.Value:##}",
                CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface("Palatino"),
                0.8 * circle._radius,
                Brushes.Black);

            circle._text.Data = text.BuildGeometry(new Point(circle._center.X - text.Width / 2, circle._center.Y - text.Height / 2));
        }
    }
}

XAML用法:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525" x:Name="Main">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="472*"/>
            <ColumnDefinition Width="45*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="27"/>
        </Grid.RowDefinitions>
        <local:CircleProgress Grid.Row="0" Grid.Column="0" Margin="75" Minimum="{Binding ElementName=MinTextBox, Path=Text}" Maximum="{Binding ElementName=MaxTextBox, Path=Text}" Value="{Binding ElementName=slider, Path=Value}"/>
        <Slider x:Name="slider" Grid.Column="1" Grid.Row="0" Orientation="Vertical" TickPlacement="BottomRight" Minimum="{Binding ElementName=MinTextBox, Path=Text}" Maximum="{Binding ElementName=MaxTextBox, Path=Text}"/>
        <StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal">
            <Label Content="Minimum" />
            <TextBox Width="200" x:Name="MinTextBox"/>
            <Label Content="Maximum"/>
            <TextBox  Width="150" x:Name="MaxTextBox"/>
        </StackPanel>
    </Grid>
</Window>

这是我得到的: PIE Progress bar