У меня есть метод, работающий с использованием ограничительной рамки элемента. Это не идеально, так как мои элементы не идеально прямоугольные, но выглядит нормально.
Обычно я нахожу ограничивающую рамку элемента в координатах Canvas по:
private static Rect GetBounds(FrameworkElement element, UIElement visual)
{
return new Rect(
element.TranslatePoint(new Point(0, 0), visual),
element.TranslatePoint(new Point(element.ActualWidth, element.ActualHeight), visual));
}
Затем я нахожу пересечение линии от центра к центру напротив каждой из четырех сторон ограничительной рамки и использую эту точку пересечения для соединения двух элементов в форме линии.
Я нашел код пересечения у сторонних ниндзя:
http://thirdpartyninjas.com/blog/2008/10/07/line-segment-intersection/
private void ProcessIntersection()
{
float ua = (point4.X - point3.X) * (point1.Y - point3.Y) - (point4.Y - point3.Y) * (point1.X - point3.X);
float ub = (point2.X - point1.X) * (point1.Y - point3.Y) - (point2.Y - point1.Y) * (point1.X - point3.X);
float denominator = (point4.Y - point3.Y) * (point2.X - point1.X) - (point4.X - point3.X) * (point2.Y - point1.Y);
intersection = coincident = false;
if (Math.Abs(denominator) <= 0.00001f)
{
if (Math.Abs(ua) <= 0.00001f && Math.Abs(ub) <= 0.00001f)
{
intersection = coincident = true;
intersectionPoint = (point1 + point2) / 2;
}
}
else
{
ua /= denominator;
ub /= denominator;
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)
{
intersection = true;
intersectionPoint.X = point1.X + ua * (point2.X - point1.X);
intersectionPoint.Y = point1.Y + ua * (point2.Y - point1.Y);
}
}
}
И вуаля! Теперь линии нарисованы так, как будто они идут от центра каждого узла к другому, но останавливаются приблизительно на краю узла, поэтому конец стрелки виден.
Усовершенствованием этого метода было бы тестирование по фактическому краю самого узла, например, по эллиптическим узлам, но мне еще предстоит найти метод WPF, который предоставил бы мне геометрию или путь, по которым я мог бы проверить.