/// <summary> /// Arrange the child elements of this panel and of any label nodes. /// </summary> /// <param name="finalSize"></param> /// <returns></returns> /// <remarks> /// This positions each child element based on the attached property values of /// <see cref="GetIndex"/>, <see cref="GetFraction"/>, <see cref="GetAlignment"/>, /// and <see cref="GetOrientation"/>. /// This also positions any <see cref="Link.LabelNode"/>, /// using the <see cref="LinkPanel"/> attached properties on the node's <see cref="Part.VisualElement"/>, /// even though such a node is not within the visual tree of this panel. /// If the label node is a <see cref="Group"/>, this will instead position all of the /// group's <see cref="Group.MemberNodes"/>. /// </remarks> protected override Size ArrangeOverride(Size finalSize) { Part part = Diagram.FindAncestor <Part>(this); if (part == null) { return(new Size()); } Link link = part as Link; if (link == null) { Adornment node = part as Adornment; if (node != null) { link = node.AdornedPart as Link; } } if (link != null) { //Diagram.Debug(" LinkPanelA- " + (link.Data != null ? link.Data.ToString() : "")); Shape stroke = this.Path; // may be null Route route = link.Route; Rect routeBounds = route.RouteBounds; IList <Point> pts = (List <Point>)route.Points; int nPoints = pts.Count; int childidx = 0; if (stroke != null) { if (childidx < childrenBounds.Count) { stroke.Arrange(childrenBounds[childidx++]); } } foreach (UIElement e in this.Children) { if (e == stroke) { continue; } if (childidx < childrenBounds.Count) { //if (e.GetType().Name.Contains("Expander")) Diagram.Debug(e.ToString() + " arranged: " + Diagram.Str(childrenBounds[childidx])); e.Arrange(childrenBounds[childidx++]); } } //Diagram.Debug(" LinkPanelA+ " + (link.Data != null ? link.Data.ToString() : "")); Node label = link.LabelNode; if (label != null) { Group labelgroup = label as Group; foreach (Node m in (labelgroup != null ? labelgroup.MemberNodes : new Node[1] { label })) { if (nPoints < 2) { continue; } UIElement e = m.VisualElement; Size sz = new Size(m.Bounds.Width, m.Bounds.Height); int index = GetIndex(e); double frac = ComputeFraction(GetFraction(e)); Spot align = GetAlignment(e); if (align.IsNoSpot) { align = Spot.Center; } LabelOrientation orient = GetOrientation(e); Point nodept; // model coordinates if (index < -nPoints || index >= nPoints) // beyond range? assume at the MidPoint, with the MidAngle { Point mid = route.MidPoint; double segangle = route.MidAngle; // maybe rotate the label double labelangle = 0; if (orient != LabelOrientation.None) { labelangle = ComputeAngle(e, orient, segangle); m.SetAngle(e, labelangle, align); } // maybe the alignment point is away from the line nodept = mid; // model coordinates Point offset = ComputeOffset(e, index, segangle, sz, labelangle); nodept = Geo.Add(nodept, offset); } else // on a particular segment, given by Index, at a point given by Fraction // negative index means start from last point, going "backwards" { Point a, b; if (index >= 0) { a = pts[index]; b = (index < nPoints - 1) ? pts[index + 1] : a; } else { int i = nPoints + index; // remember that index is negative here a = pts[i]; b = (i > 0) ? pts[i - 1] : a; } // compute the fractional point along the line, in model coordinates nodept = new Point(a.X + (b.X - a.X) * frac, a.Y + (b.Y - a.Y) * frac); double segangle = (index >= 0 ? Geo.GetAngle(a, b) : Geo.GetAngle(b, a)); // maybe rotate the label double labelangle = 0; if (orient != LabelOrientation.None) { labelangle = ComputeAngle(e, orient, segangle); m.SetAngle(e, labelangle, align); } // maybe the alignment point is away from the line Point offset = ComputeOffset(e, index, segangle, sz, labelangle); nodept = Geo.Add(nodept, offset); } Rect hb = align.RectForPoint(nodept, sz); m.Position = new Point(hb.X, hb.Y); } } } return(finalSize); }
/// <summary> /// Determine the size of the union of the bounds of the positioned and rotated child elements. /// </summary> /// <param name="availableSize"></param> /// <returns></returns> protected override Size MeasureOverride(Size availableSize) { Part part = Diagram.FindAncestor <Part>(this); if (part == null) { return(new Size()); } if (!part.IsMeasuringArranging) { return(new Size()); } Link link = part as Link; if (link == null) { Adornment ad = part as Adornment; if (ad != null) { link = ad.AdornedPart as Link; } if (link == null) { return(new Size()); } } //Diagram.Debug(" LinkPanelM- " + (link.Data != null ? link.Data.ToString() : "")); Shape stroke = this.Path; // may be null link.Path = stroke; // the Link caches what the Path really is Route route = link.Route; Rect routeBounds = route.RouteBounds; // in model coordinates Rect linkBounds = routeBounds; // includes all labels childrenBounds = new List <Rect>(); // in local coordinates if (stroke != null) { stroke.Measure(Geo.Unlimited); Size sz = stroke.DesiredSize; linkBounds.Width = Math.Max(linkBounds.Width, sz.Width); linkBounds.Height = Math.Max(linkBounds.Height, sz.Height); childrenBounds.Add(new Rect(0, 0, linkBounds.Width, linkBounds.Height)); } IList <Point> pts = (List <Point>)route.Points; int nPoints = pts.Count; foreach (UIElement e in this.Children) { if (e == stroke) { continue; // already measured the stroke, above } e.Measure(Geo.Unlimited); //if (e.GetType().Name.Contains("Expander")) Diagram.Debug(e.ToString() + " measured: " + Diagram.Str(e.DesiredSize)); if (nPoints < 2) { continue; } Size sz = e.DesiredSize; int index = GetIndex(e); double frac = ComputeFraction(GetFraction(e)); Spot align = GetAlignment(e); if (align.IsNoSpot) { align = Spot.Center; } LabelOrientation orient = GetOrientation(e); Point eltpt; // local coordinates if (index < -nPoints || index >= nPoints) // beyond range? assume at the MidPoint, with the MidAngle { Point mid = route.MidPoint; if (this.Implementation == LinkPanelImplementation.Stretch) { Point p0 = pts[0]; Point pn = pts[nPoints - 1]; sz.Width = Math.Sqrt(Geo.DistanceSquared(pn, p0)); } double segangle = route.MidAngle; // maybe rotate the label double labelangle = 0; if (orient != LabelOrientation.None) { labelangle = ComputeAngle(e, orient, segangle); link.SetAngle(e, labelangle, align); } // maybe the alignment point is away from the line eltpt = new Point(mid.X - routeBounds.X, mid.Y - routeBounds.Y); // local coordinates Point offset = ComputeOffset(e, index, segangle, sz, labelangle); eltpt = Geo.Add(eltpt, offset); } else // on a particular segment, given by Index, at a point given by Fraction // negative index means start from last point, going "backwards" { Point a, b; if (index >= 0) { a = pts[index]; b = (index < nPoints - 1) ? pts[index + 1] : a; } else { int i = nPoints + index; // remember that index is negative here a = pts[i]; b = (i > 0) ? pts[i - 1] : a; } // compute the fractional point along the line, in local coordinates eltpt = new Point(a.X + (b.X - a.X) * frac - routeBounds.X, a.Y + (b.Y - a.Y) * frac - routeBounds.Y); double segangle = (index >= 0 ? Geo.GetAngle(a, b) : Geo.GetAngle(b, a)); // maybe rotate the label double labelangle = 0; if (orient != LabelOrientation.None) { labelangle = ComputeAngle(e, orient, segangle); link.SetAngle(e, labelangle, align); } // maybe the alignment point is away from the line Point offset = ComputeOffset(e, index, segangle, sz, labelangle); eltpt = Geo.Add(eltpt, offset); } Rect cb = align.RectForPoint(eltpt, sz); childrenBounds.Add(cb); // local coordinates linkBounds.Union(new Rect(cb.X + routeBounds.X, cb.Y + routeBounds.Y, cb.Width, cb.Height)); // model coordinates } // if this panel is the "whole" link, update the link's Bounds if (link.VisualElement == this) { //Diagram.Debug(" LinkPanelM+ " + (link.Data != null ? link.Data.ToString() : "") + " " + Diagram.Str(routeBounds) + Diagram.Str(linkBounds)); link.Bounds = new Rect(routeBounds.X, routeBounds.Y, linkBounds.Width, linkBounds.Height); } return(new Size(routeBounds.Width, routeBounds.Height)); }