117 lines
3.8 KiB
C#
117 lines
3.8 KiB
C#
using Avalonia;
|
||
using Avalonia.Controls;
|
||
using Avalonia.Media;
|
||
using Avalonia.VisualTree;
|
||
|
||
|
||
namespace Plugin.Cowain.Wcs.Controls;
|
||
|
||
public class RgvArrowDecorator : Decorator
|
||
{
|
||
public static readonly StyledProperty<Point> ArrowEndCanvasPointProperty =
|
||
AvaloniaProperty.Register<RgvArrowDecorator, Point>(nameof(ArrowEndCanvasPoint));
|
||
|
||
public Point ArrowEndCanvasPoint
|
||
{
|
||
get => GetValue(ArrowEndCanvasPointProperty);
|
||
set => SetValue(ArrowEndCanvasPointProperty, value);
|
||
}
|
||
|
||
public static readonly StyledProperty<IBrush> ArrowBrushProperty =
|
||
AvaloniaProperty.Register<RgvArrowDecorator, IBrush>(nameof(ArrowBrush), Brushes.Red);
|
||
|
||
public IBrush ArrowBrush
|
||
{
|
||
get => GetValue(ArrowBrushProperty);
|
||
set => SetValue(ArrowBrushProperty, value);
|
||
}
|
||
|
||
public static readonly StyledProperty<bool> ArrowDirectionProperty =
|
||
AvaloniaProperty.Register<RgvArrowDecorator, bool>(nameof(ArrowDirection), false);
|
||
|
||
public bool ArrowDirection
|
||
{
|
||
get => GetValue(ArrowDirectionProperty);
|
||
set => SetValue(ArrowDirectionProperty, value);
|
||
}
|
||
|
||
public RgvArrowDecorator()
|
||
{
|
||
this.LayoutUpdated += (s, e) => this.InvalidateVisual();
|
||
ArrowEndCanvasPointProperty.Changed.AddClassHandler<RgvArrowDecorator>((b, e) => b.InvalidateVisual());
|
||
ArrowBrushProperty.Changed.AddClassHandler<RgvArrowDecorator>((b, e) => b.InvalidateVisual());
|
||
ArrowDirectionProperty.Changed.AddClassHandler<RgvArrowDecorator>((b, e) => b.InvalidateVisual());
|
||
}
|
||
|
||
|
||
public override void Render(DrawingContext context)
|
||
{
|
||
base.Render(context);
|
||
|
||
var start = new Point(Bounds.Width / 2, Bounds.Height / 2);
|
||
var endCanvas = ArrowEndCanvasPoint;
|
||
|
||
var canvas = this.FindAncestorOfType<Canvas>();
|
||
if (canvas == null)
|
||
return;
|
||
|
||
var transform = this.TransformToVisual(canvas);
|
||
Point myLeftTop = default;
|
||
if (transform != null)
|
||
{
|
||
if (transform is Matrix matrix)
|
||
myLeftTop = matrix.Transform(new Point(0, 0));
|
||
}
|
||
|
||
// 转换为本地坐标
|
||
var end = new Point(endCanvas.X - myLeftTop.X, endCanvas.Y - myLeftTop.Y);
|
||
|
||
// 计算三段折线
|
||
double offset = 15; // 垂直偏移
|
||
bool isAbove = end.Y < start.Y;
|
||
|
||
// 第一拐点:起点垂直偏移
|
||
var first = new Point(start.X, start.Y + (isAbove ? -offset : offset));
|
||
// 第二拐点:目标X,起点Y偏移
|
||
var second = new Point(end.X, end.Y + (isAbove ? offset : -offset));
|
||
// 终点:目标点
|
||
var last = new Point(end.X, end.Y);
|
||
|
||
// 画三段折线
|
||
var pen = new Pen(ArrowBrush, 2);
|
||
context.DrawLine(pen, start, first);
|
||
context.DrawLine(pen, first, second);
|
||
context.DrawLine(pen, second, last);
|
||
|
||
// 画箭头头部
|
||
if (ArrowDirection)
|
||
{
|
||
DrawArrowHead(context, end, second, ArrowBrush);
|
||
}
|
||
else
|
||
{
|
||
DrawArrowHead(context, second, end, ArrowBrush);
|
||
}
|
||
|
||
}
|
||
|
||
private void DrawArrowHead(DrawingContext context, Point from, Point to, IBrush brush)
|
||
{
|
||
// 箭头头部为V型
|
||
double arrowSize = 10;
|
||
double angle = Math.Atan2(to.Y - from.Y, to.X - from.X);
|
||
double leftAngle = angle + Math.PI / 6;
|
||
double rightAngle = angle - Math.PI / 6;
|
||
|
||
var left = new Point(
|
||
to.X - arrowSize * Math.Cos(leftAngle),
|
||
to.Y - arrowSize * Math.Sin(leftAngle));
|
||
var right = new Point(
|
||
to.X - arrowSize * Math.Cos(rightAngle),
|
||
to.Y - arrowSize * Math.Sin(rightAngle));
|
||
|
||
var pen = new Pen(brush, 2);
|
||
context.DrawLine(pen, to, left);
|
||
context.DrawLine(pen, to, right);
|
||
}
|
||
} |