WPF 径向进度条/仪表(即电池表) [英] WPF Radial Progressbar/Meter (i.e. Battery Meter)

查看:22
本文介绍了WPF 径向进度条/仪表(即电池表)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发适用于 Windows 8.1 和 Windows Phone 8.1 的统一健身应用.理想情况下,核心视图之一将具有每日进度表.问题是我还没有能够想出一个实际的仪表或仪表.我想要的只是一个径向进度条或与 Windows Phone 商店中常见电池应用程序中的电池电量计/仪表相当的东西.据我所知,WPF/VS 2013 没有提供这种开箱即用的组件.我知道 Telerik 和其他一些 3rd 方提供了类似的东西,但我更喜欢使用开源或自己构建的东西.

I'm working on an Unified fitness app for Windows 8.1 and Windows Phone 8.1. Ideally one of the core views would feature a daily progress meter. The problem is that I haven't been able to come up with an actual meter or gauge. What I'd like to have is simply a radial progress bar or something on par with the battery gauges/meters in common battery apps in the Windows Phone store. From what I can tell, WPF/VS 2013 doesn't offer this kind of component out of the box. I know that Telerik and a few other 3rd parties offer something similar, but I'd prefer to use something open source or build it myself.

有谁知道与 .NET 4.5 & 一起使用的较新的开源组件?WPF 或有关如何构建自己的组件的示例?

Does anyone know of newer opensource components that work with .NET 4.5 & WPF or have examples on how I could build my own component?

到目前为止,我发现的内容类似于此链接:WPF 仪表

So far what I have found are similar to this link: Gauges for WPF

但我希望使用类似的东西:

But I'm hoping to use something similar to this:

推荐答案

您可以自己构建类似的东西.首先,你需要一个 Arc:

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);
    }
}

这个弧有一个 StartAngle 和一个 EndAngle.要将进度条的进度转换为这些角度,您需要一个转换器:

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();
    }
}

好的.这就是你所需要的一切.现在您可以编写 XAML.那可能是这样的:

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>

现在只需使用 ProgressBarStyle,对其进行修改并将其应用于您喜欢的任何进度条.

Now just take the ProgressBarStyle, modify it and apply it to any progressbar you like.

最后,你会得到这样的东西.玩得开心!

Finally, you'll get something like this. Have fun!

您需要以下参考资料(我建议您创建一个新的空 WPF 项目):

You need the following references (I would recommend you, to just create a new and empty WPF project):

  • WindowsBase
  • PresentationCore
  • 演示框架

为了控制旋转方向以及开始进度的位置,我添加了两个依赖属性:方向OriginRotationDegrees

In order to control the rotation direction as well as the position to start the progress at, I added two dependency properties: Direction OriginRotationDegrees

这篇关于WPF 径向进度条/仪表(即电池表)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆