我正在开发适用于Windows 8.1和Windows Phone 8.1的Unified健身应用。理想情况下,其中一个核心视图将包含每日进度表。问题是我无法想出实际的仪表或仪表。我想要的只是一个径向进度条或类似于Windows Phone商店中常见电池应用中的电池电量表/米的东西。据我所知,WPF / VS 2013不提供开箱即用的这种组件。我知道Telerik和其他一些第三方提供类似的东西,但我更喜欢使用开源的东西或自己构建它。
是否有人知道适用于.NET 4.5和更新版本的较新的开源组件? WPF还是有关于如何构建自己的组件的示例?
到目前为止,我发现的内容与此链接类似:Gauges for WPF
但我希望使用类似的东西:
答案 0 :(得分:54)
你可以自己建造类似的东西。首先,你需要一个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项目):
编辑: 为了控制旋转方向以及开始进度的位置,我添加了两个依赖属性: 方向 OriginRotationDegrees