使用WPF - 如何创建一个看起来像Windows进度栏的图表 - 圆环图? https://guyterry.files.wordpress.com/2015/07/upgradingrelax.jpg
答案 0 :(得分:0)
答案 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>