WPF Radial Progressbar/Meter (i.e. Battery Meter) [closed] WPF Radial Progressbar/Meter (i.e. Battery Meter) [closed] wpf wpf

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

enter image description here