gpt4 book ai didi

c# - WPF donut 进度条

转载 作者:太空狗 更新时间:2023-10-30 01:16:05 25 4
gpt4 key购买 nike

我正在尝试调整 WPF 4 Unleashed 一书中的饼图 ProgressBar 使其看起来像一个 donut 。我觉得我已经完成了一半,但我不知道如何解决最后一个问题。

这是一张说明我想要什么以及我已经设法实现的图片:

enter image description here

  1. 这就是我想要的样子。
  2. 这是使用下面的代码的样子。
  3. 我在 stackoverflow 上的另一个问题中找到了一个建议,即在路径上使用剪裁并将笔划粗细加倍。如您所见,路径现在已正确定位,但如您所见,任何低于 50% 的进度都未正确绘制。

所以我的问题是,我怎样才能让它看起来像我想要的那样?

下面是我正在使用的相关 xaml:

<ControlTemplate x:Key="DonutProgressBar" TargetType="{x:Type ProgressBar}">
<ControlTemplate.Resources>
<conv:ValueMinMaxToIsLargeArcConverter x:Key="ValueMinMaxToIsLargeArcConverter" />
<conv:ValueMinMaxToPointConverter x:Key="ValueMinMaxToPointConverter" />
</ControlTemplate.Resources>
<Grid>
<Viewbox>
<Grid Width="20" Height="20">
<Ellipse x:Name="Background"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Top}"
Width="20"
Height="20"
Fill="{TemplateBinding Background}" />
<Path x:Name="Donut"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Top}">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="10,0">
<ArcSegment Size="10,10" SweepDirection="Clockwise">
<ArcSegment.Point>
<MultiBinding Converter="{StaticResource ValueMinMaxToPointConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Minimum" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Maximum" />
</MultiBinding>
</ArcSegment.Point>
<ArcSegment.IsLargeArc>
<MultiBinding Converter="{StaticResource ValueMinMaxToIsLargeArcConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Minimum" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Maximum" />
</MultiBinding>
</ArcSegment.IsLargeArc>
</ArcSegment>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Grid>
</Viewbox>
</Grid>
</ControlTemplate>

...
<ProgressBar Width="70" Height="70" Value="40" Template="{StaticResource DonutProgressBar}" Background="{x:Null}" BorderBrush="#1F000000" BorderThickness="6,6,1,1" />

...和转换器:

public class ValueMinMaxToPointConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double value = (double)values[0];
double minimum = (double)values[1];
double maximum = (double)values[2];

double current = (value / (maximum - minimum)) * 360;

if (current == 360)
current = 359.999;

current = current - 90;

current = current * (Math.PI / 180.0);

double x = 10 + 10 * Math.Cos(current);
double y = 10 + 10 * Math.Sin(current);

return new Point(x, y);
}

public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

public class ValueMinMaxToIsLargeArcConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double value = (double)values[0];
double minimum = (double)values[1];
double maximum = (double)values[2];

return ((value * 2) >= (maximum - minimum));
}

public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

最佳答案

这里真正的问题是 WPF 中缺少 Arc 控件。与其试图硬塞现有的框架来满足您的要求,为什么不自己添加呢?网上有许多 WPF Arc 实现,它们看起来都非常相似,只要确保您选择一个在角度 DP 更改时更新视觉效果的实现即可。这应该可以很好地满足您的目的:

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 PropertyMetadata(0.0, AnglesChanged));

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 PropertyMetadata(0.0, AnglesChanged));


protected override Geometry DefiningGeometry
{
get
{
return GetArcGeometry();
}
}

private static void AnglesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var arc = d as Arc;
if (arc != null)
arc.InvalidateVisual();
}

private Geometry GetArcGeometry()
{
Point startPoint = PointAtAngle(Math.Min(StartAngle, EndAngle));
Point endPoint = PointAtAngle(Math.Max(StartAngle, EndAngle));
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,
SweepDirection.Counterclockwise, true, false);
}
geom.Transform = new TranslateTransform(StrokeThickness / 2, StrokeThickness / 2);
return geom;
}

private Point PointAtAngle(double angle)
{
double radAngle = angle * (Math.PI / 180);
double xRadius = (RenderSize.Width - StrokeThickness) / 2;
double yRadius = (RenderSize.Height - StrokeThickness) / 2;
double x = xRadius + xRadius * Math.Cos(radAngle);
double y = yRadius - yRadius * Math.Sin(radAngle);
return new Point(x, y);
}
}

为了维护一个干净的架构,我更喜欢将自定义形状放在一个单独的类库中,并引用 PresentationFramework,这样做还允许您通过在项目中放置以下行来删除命名空间,如 this page 中所述:

[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "YourNamespace")]

您现在有一个可重复使用的 Arc 形状,您可以像使用椭圆一样使用它,因此请将您的整个路径 XAML 替换为如下内容:

<Arc
Stroke="{TemplateBinding Foreground}"
StrokeThickness="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Top}"
StartAngle="90" EndAngle="-45" />

结果:

enter image description here

显然,我在这里对开始和结束角度进行了硬编码,但是根据您已经完成的工作,我相信您可以轻松编写一个简单的多转换器来计算 value/min 的角度/最大。

关于c# - WPF donut 进度条,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36752183/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com