186 lines
7.5 KiB
C#
186 lines
7.5 KiB
C#
using System;
|
||
using System.Globalization;
|
||
using System.Windows;
|
||
using System.Windows.Data;
|
||
using System.Windows.Ink;
|
||
using System.Windows.Media;
|
||
|
||
namespace Wpf_AiSportsMicrospace.MyUserControl
|
||
{
|
||
/// <summary>
|
||
/// 将进度值转换为表示矩形边框部分路径的几何图形(StreamGeometry)的转换器。
|
||
/// 该转换器用于绘制一个带圆角的矩形边框,并根据进度值绘制其部分轮廓线,常用于进度条等UI控件中。
|
||
/// </summary>
|
||
public class ProgressToRectangleGeometryConverter : IValueConverter
|
||
{
|
||
/// <summary>
|
||
/// 将进度值转换为表示矩形边框部分路径的几何图形(StreamGeometry)对象
|
||
/// </summary>
|
||
/// <param name="value">当前进度值,应为 [0.0, 1.0] 范围内的 double 类型</param>
|
||
/// <param name="targetType">目标类型,应为 Geometry 类型</param>
|
||
/// <param name="parameter">参数字符串,格式为 "width,height,radius",分别表示矩形的宽度、高度和圆角半径</param>
|
||
/// <param name="culture">当前区域性信息</param>
|
||
/// <returns>返回一个 StreamGeometry 对象,表示根据进度绘制的部分矩形边框</returns>
|
||
// parameter: "width,height,radius"
|
||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||
{
|
||
double progress = (double)value;
|
||
string[] sizes = parameter.ToString()!.Split(',');
|
||
double width = double.Parse(sizes[0]);
|
||
double height = double.Parse(sizes[1]);
|
||
double radius = double.Parse(sizes[2]);
|
||
double stroke = sizes.Length > 3 ? double.Parse(sizes[3]) : 0;
|
||
|
||
double inset = stroke / 2.0;
|
||
double x = inset;
|
||
double y = inset;
|
||
double w = width - stroke;
|
||
double h = height - stroke;
|
||
// 保持原始圆角半径
|
||
double r = radius;
|
||
|
||
// 计算总周长
|
||
double perimeter = 2 * (w + h - 4 * r) + 2 * Math.PI * r;
|
||
double length = perimeter * progress;
|
||
|
||
var geometry = new StreamGeometry();
|
||
using (var ctx = geometry.Open())
|
||
{
|
||
// 从左上角圆角起点,顺时针
|
||
ctx.BeginFigure(new Point(x + r, y), false, false);
|
||
|
||
// top line
|
||
double top = Math.Min(length, w - 2 * r);
|
||
ctx.LineTo(new Point(x + r + top, y), true, false);
|
||
length -= top;
|
||
if (length <= 0) return geometry;
|
||
|
||
// top-right arc
|
||
double arc = Math.Min(length, Math.PI / 2 * r);
|
||
if (arc > 0)
|
||
{
|
||
double angle = arc / r;
|
||
double endX = x + w - r + Math.Sin(angle) * r;
|
||
double endY = y + r - Math.Cos(angle) * r;
|
||
if (arc < Math.PI / 2 * r)
|
||
{
|
||
ctx.ArcTo(
|
||
new Point(endX, endY),
|
||
new Size(r, r),
|
||
0, false, SweepDirection.Clockwise, true, false);
|
||
return geometry;
|
||
}
|
||
else
|
||
{
|
||
ctx.ArcTo(
|
||
new Point(x + w, y + r),
|
||
new Size(r, r),
|
||
0, false, SweepDirection.Clockwise, true, false);
|
||
length -= Math.PI / 2 * r;
|
||
}
|
||
}
|
||
if (length <= 0) return geometry;
|
||
|
||
// right line
|
||
double right = Math.Min(length, h - 2 * r);
|
||
ctx.LineTo(new Point(x + w, y + r + right), true, false);
|
||
length -= right;
|
||
if (length <= 0) return geometry;
|
||
|
||
// bottom-right arc
|
||
arc = Math.Min(length, Math.PI / 2 * r);
|
||
if (arc > 0)
|
||
{
|
||
double angle = arc / r;
|
||
double endX = x + w - r + Math.Cos(angle) * r;
|
||
double endY = y + h - r + Math.Sin(angle) * r;
|
||
if (arc < Math.PI / 2 * r)
|
||
{
|
||
ctx.ArcTo(
|
||
new Point(endX, endY),
|
||
new Size(r, r),
|
||
0, false, SweepDirection.Clockwise, true, false);
|
||
return geometry;
|
||
}
|
||
else
|
||
{
|
||
ctx.ArcTo(
|
||
new Point(x + w - r, y + h),
|
||
new Size(r, r),
|
||
0, false, SweepDirection.Clockwise, true, false);
|
||
length -= Math.PI / 2 * r;
|
||
}
|
||
}
|
||
if (length <= 0) return geometry;
|
||
|
||
// bottom line
|
||
double bottom = Math.Min(length, w - 2 * r);
|
||
ctx.LineTo(new Point(x + w - r - bottom, y + h), true, false);
|
||
length -= bottom;
|
||
if (length <= 0) return geometry;
|
||
|
||
// bottom-left arc
|
||
arc = Math.Min(length, Math.PI / 2 * r);
|
||
if (arc > 0)
|
||
{
|
||
double angle = arc / r;
|
||
double endX = x + r - Math.Sin(angle) * r;
|
||
double endY = y + h - r + Math.Cos(angle) * r;
|
||
if (arc < Math.PI / 2 * r)
|
||
{
|
||
ctx.ArcTo(
|
||
new Point(endX, endY),
|
||
new Size(r, r),
|
||
0, false, SweepDirection.Clockwise, true, false);
|
||
return geometry;
|
||
}
|
||
else
|
||
{
|
||
ctx.ArcTo(
|
||
new Point(x, y + h - r),
|
||
new Size(r, r),
|
||
0, false, SweepDirection.Clockwise, true, false);
|
||
length -= Math.PI / 2 * r;
|
||
}
|
||
}
|
||
if (length <= 0) return geometry;
|
||
|
||
// left line
|
||
double left = Math.Min(length, h - 2 * r);
|
||
ctx.LineTo(new Point(x, y + h - r - left), true, false);
|
||
length -= left;
|
||
if (length <= 0) return geometry;
|
||
|
||
// top-left arc
|
||
arc = Math.Min(length, Math.PI / 2 * r);
|
||
if (arc > 0)
|
||
{
|
||
double angle = arc / r;
|
||
double endX = x + r - Math.Cos(angle) * r;
|
||
double endY = y + r - Math.Sin(angle) * r;
|
||
if (arc < Math.PI / 2 * r)
|
||
{
|
||
ctx.ArcTo(
|
||
new Point(endX, endY),
|
||
new Size(r, r),
|
||
0, false, SweepDirection.Clockwise, true, false);
|
||
return geometry;
|
||
}
|
||
else
|
||
{
|
||
ctx.ArcTo(
|
||
new Point(x + r, y),
|
||
new Size(r, r),
|
||
0, false, SweepDirection.Clockwise, true, false);
|
||
}
|
||
}
|
||
}
|
||
geometry.Freeze();
|
||
return geometry;
|
||
}
|
||
|
||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||
=> throw new NotImplementedException();
|
||
|
||
}
|
||
} |