【问题标题】:WPF Radial Progressbar/Meter (i.e. Battery Meter) [closed]WPF径向进度条/仪表(即电池仪表)[关闭]
【发布时间】:2015-04-09 05:32:01
【问题描述】:

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

有人知道与 .NET 4.5 和 WPF 一起使用的较新的开源组件,或者有关于如何构建自己的组件的示例吗?

到目前为止,我发现的内容与此链接相似:Gauges for WPF

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

【问题讨论】:

    标签: c# wpf windows-8.1 .net-4.5 windows-phone-8.1


    【解决方案1】:

    您可以自己构建类似的东西。首先,你需要一个 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。要将进度条的进度转换为这些角度,您需要一个转换器:

    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。可能是这样的:

    <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,对其进行修改并将其应用于您喜欢的任何进度条。

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

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

    • WindowsBase
    • PresentationCore
    • 演示框架

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

    【讨论】:

    • 感谢您详细的回答!在大多数情况下,我发现它易于理解且很有帮助。不幸的是,我在实施解决方案时遇到了困难。我无法“解决”或找到 StreamGeometry、Pen、Point、UIPropertyMetadata 和 Media.DrawingContext 的命名空间。系统中未找到 DrawingContext 是由于尝试使用导入时未找到 System.Windows.Media 造成的。我发现的建议建议引用 PresentationCore.dll,但这并没有解决我的问题。有什么建议吗?
    • 我已经添加了必要的命名空间。
    • 感谢您添加导入。我使用的一些导入不正确,并且缺少一些参考:PresentationFramework、PresentationCore、WindowsBase。一切都很好,谢谢! :)
    • 正是我的设想,非常感谢!
    • 干得好!不难实现,并且通过玩弄风格很容易定制。
    猜你喜欢
    • 1970-01-01
    • 2011-02-09
    • 1970-01-01
    • 1970-01-01
    • 2011-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多