WPF Radial Progressbar/Meter (i.e. Battery Meter) [closed]
You can build something like that yourself. First of all, you need an Arc:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows.Shapes;using System.Windows.Media;using System.Windows;...public class Arc : Shape{ public double StartAngle { get { return (double)GetValue(StartAngleProperty); } set { SetValue(StartAngleProperty, value); } } // Using a DependencyProperty as the backing store for StartAngle. This enables animation, styling, binding, etc... public static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register("StartAngle", typeof(double), typeof(Arc), new UIPropertyMetadata(0.0, new PropertyChangedCallback(UpdateArc))); public double EndAngle { get { return (double)GetValue(EndAngleProperty); } set { SetValue(EndAngleProperty, value); } } // Using a DependencyProperty as the backing store for EndAngle. This enables animation, styling, binding, etc... public static readonly DependencyProperty EndAngleProperty = DependencyProperty.Register("EndAngle", typeof(double), typeof(Arc), new UIPropertyMetadata(90.0, new PropertyChangedCallback(UpdateArc))); //This controls whether or not the progress bar goes clockwise or counterclockwise public SweepDirection Direction { get { return (SweepDirection) GetValue(DirectionProperty); } set { SetValue(DirectionProperty, value);} } public static readonly DependencyProperty DirectionProperty = DependencyProperty.Register("Direction", typeof (SweepDirection), typeof (Arc), new UIPropertyMetadata(SweepDirection.Clockwise)); //rotate the start/endpoint of the arc a certain number of degree in the direction //ie. if you wanted it to be at 12:00 that would be 270 Clockwise or 90 counterclockwise public double OriginRotationDegrees { get { return (double) GetValue(OriginRotationDegreesProperty); } set { SetValue(OriginRotationDegreesProperty, value);} } public static readonly DependencyProperty OriginRotationDegreesProperty = DependencyProperty.Register("OriginRotationDegrees", typeof (double), typeof (Arc), new UIPropertyMetadata(270.0, new PropertyChangedCallback(UpdateArc))); protected static void UpdateArc(DependencyObject d, DependencyPropertyChangedEventArgs e) { Arc arc = d as Arc; arc.InvalidateVisual(); } protected override Geometry DefiningGeometry { get { return GetArcGeometry(); } } protected override void OnRender(System.Windows.Media.DrawingContext drawingContext) { drawingContext.DrawGeometry(null, new Pen(Stroke, StrokeThickness), GetArcGeometry()); } private Geometry GetArcGeometry() { Point startPoint = PointAtAngle(Math.Min(StartAngle, EndAngle), Direction); Point endPoint = PointAtAngle(Math.Max(StartAngle, EndAngle), Direction); Size arcSize = new Size(Math.Max(0, (RenderSize.Width - StrokeThickness) / 2), Math.Max(0, (RenderSize.Height - StrokeThickness) / 2)); bool isLargeArc = Math.Abs(EndAngle - StartAngle) > 180; StreamGeometry geom = new StreamGeometry(); using (StreamGeometryContext context = geom.Open()) { context.BeginFigure(startPoint, false, false); context.ArcTo(endPoint, arcSize, 0, isLargeArc, Direction, true, false); } geom.Transform = new TranslateTransform(StrokeThickness / 2, StrokeThickness / 2); return geom; } private Point PointAtAngle(double angle, SweepDirection sweep) { double translatedAngle = angle + OriginRotationDegrees; double radAngle = translatedAngle * (Math.PI / 180); double xr = (RenderSize.Width - StrokeThickness) / 2; double yr = (RenderSize.Height - StrokeThickness) / 2; double x = xr + xr * Math.Cos(radAngle); double y = yr * Math.Sin(radAngle); if (sweep == SweepDirection.Counterclockwise) { y = yr - y; } else { y = yr + y; } return new Point(x, y); }}
This arc has an StartAngle and an EndAngle. To convert from a progress of a progressbar to these angles, you need a converter:
using System;using System.Collections.Generic;using System.Linq;using System.Text;...public class ProgressToAngleConverter : System.Windows.Data.IMultiValueConverter{ public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { double progress = (double)values[0]; System.Windows.Controls.ProgressBar bar = values[1] as System.Windows.Controls.ProgressBar; return 359.999 * (progress / (bar.Maximum - bar.Minimum)); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); }}
Ok fine. That was everything you need. Now you can write your XAML. That could be something like that:
<Window x:Class="WPFTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPFTest" Title="MainWindow" Height="525" Width="525"> <Window.Resources> <local:ProgressToAngleConverter x:Key="ProgressConverter"/> <Style TargetType="{x:Type ProgressBar}" x:Key="ProgressBarStyle"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ProgressBar}"> <Grid> <Ellipse Stroke="Black" Fill="{TemplateBinding Background}"/> <Ellipse Stroke="Black" Margin="40" Fill="White"/> <local:Arc StrokeThickness="30" Stroke="{TemplateBinding BorderBrush}" Margin="5"> <local:Arc.StartAngle> <MultiBinding Converter="{StaticResource ProgressConverter}"> <Binding Path="Minimum" RelativeSource="{RelativeSource TemplatedParent}"/> <Binding Path="." RelativeSource="{RelativeSource TemplatedParent}"/> </MultiBinding> </local:Arc.StartAngle> <local:Arc.EndAngle> <MultiBinding Converter="{StaticResource ProgressConverter}"> <Binding Path="Value" RelativeSource="{RelativeSource TemplatedParent}"/> <Binding Path="." RelativeSource="{RelativeSource TemplatedParent}"/> </MultiBinding> </local:Arc.EndAngle> </local:Arc> <TextBlock Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, StringFormat=\{0:0\}}" Foreground="{TemplateBinding Background}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="72" FontWeight="Bold"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Viewbox> <ProgressBar Style="{StaticResource ProgressBarStyle}" Width="300" Height="300" Value="{Binding ElementName=sliderValue, Path=Value}"/> </Viewbox> <Slider Grid.Row="1" Name="sliderValue" Maximum="100" Value="50" /> </Grid></Window>
Now just take the ProgressBarStyle
, modify it and apply it to any progressbar you like.
Finally, you'll get something like this. Have fun!
EDIT:You need the following references (I would recommend you, to just create a new and empty WPF project):
- WindowsBase
- PresentationCore
- PresentationFramework
EDIT:In order to control the rotation direction as well as the position to start the progress at, I added two dependency properties:DirectionOriginRotationDegrees