private void DoUpdateNodesAndLinks() { VerifyAccess(); this.UpdateNodesAndLinksNeeded = false; Diagram observed = this.Observed; if (observed == null) { return; } PartManager obmgr = observed.PartManager; if (obmgr == null) { return; } // make sure each Node and Link have the same locations and routes and visibility as those in the Observed diagram bool usevis = this.UsesObservedPartVisible; if (this.UsesObservedNodeLocation) { foreach (Node overviewnode in this.Nodes) { Node observednode = obmgr.FindNodeForData(overviewnode.Data, this.Model); if (observednode != null) { Point ovloc = overviewnode.Location; Point obloc = observednode.Location; if (!Geo.IsApprox(ovloc, obloc)) { overviewnode.Location = obloc; } if (usevis) { overviewnode.Visible = observednode.Visible; } } } } if (this.UsesObservedLinkRoute) { foreach (Link overviewlink in this.Links) { Link observedlink = obmgr.FindLinkForData(overviewlink.Data, this.Model); if (observedlink != null) { overviewlink.Route.Points = observedlink.Route.Points; if (usevis) { overviewlink.Visible = observedlink.Visible; } } } } // afterwards, Node.Location is updated dynamically in Overview.ObservedPanel_PartBoundsChanged // and Part.Visible is updated dynamically in Overview.ObservedPanel_PartVisibleChanged }
private void Panel_ViewportBoundsChanged(Object sender, RoutedPropertyChangedEventArgs <Rect> e) { // only call LayoutDiagram when the size of the viewport has changed (in model coordinates), // because the panel changed size or because the panel.Scale changed if (!Geo.IsApprox(e.OldValue.Width, e.NewValue.Width) || !Geo.IsApprox(e.OldValue.Height, e.NewValue.Height)) { LayoutDiagram(); } }
private void ObservedPanel_PartBoundsChanged(object sender, EventArgs e) { Diagram observed = this.Observed; if (observed == null) { return; } // don't let animated movements mess up data via TwoWay data-bindings LayoutManager observedlayout = observed.LayoutManager; if (observedlayout != null && observedlayout.IsAnimating) { return; } PartManager partmgr = this.PartManager; if (partmgr == null) { return; } Node observednode = sender as Node; if (observednode != null && this.UsesObservedNodeLocation) { // find the corresponding node in this Overview Node overviewnode = partmgr.FindNodeForData(observednode.Data, observed.Model); if (overviewnode != null) { // need to remove any Binding for Node.Location on this node, // in case it is Mode=TwoWay, trying to update the data FrameworkElement ve = overviewnode.EnsuredVisualElement; if (ve != null) { //var be0 = ve.GetBindingExpression(Node.LocationProperty); ve.ClearValue(Node.LocationProperty); //var be1 = ve.GetBindingExpression(Node.LocationProperty); //var locval = ve.ReadLocalValue(Node.LocationProperty); //var val = ve.GetValue(Node.LocationProperty); } // the observed node has moved -- also move this overview's node Point ovloc = overviewnode.Location; Point obloc = observednode.Location; if (!Geo.IsApprox(ovloc, obloc)) { overviewnode.Location = obloc; } } } else { Link observedlink = sender as Link; if (observedlink != null && this.UsesObservedLinkRoute) { Link overviewlink = partmgr.FindLinkForData(observedlink.Data, observed.Model); if (overviewlink != null) { // the observed link has moved or changed size -- replace this overview's link's route // with that of the observed link's, so it doesn't need to be recomputed //??? doesn't handle case where the points have changed, but the Bounds haven't overviewlink.Route.Points = observedlink.Route.Points; } } } }
/// <summary> /// Modify the <see cref="Route"/> of the <see cref="AdornedLink"/> to a new point, /// considering also which reshape handle is being dragged and whether the route /// is <see cref="Route.Orthogonal"/>. /// </summary> /// <param name="newPoint"></param> /// <remarks> /// If the <see cref="Route"/> is <see cref="Route.Orthogonal"/> this /// modifies the adjacent points as well in order to keep adjacent segments orthogonal. /// For handles that are near either end of the route, the movement may be constrained /// to be only vertical or only horizontal, in order to maintain orthogonality. /// The decision is based on the value of <see cref="ReshapingBaseTool.GetReshapeBehavior"/> /// on the <see cref="ReshapingBaseTool.Handle"/>. /// This method is called during a "ReshapeLink" transaction. /// </remarks> protected virtual void DoReshape(Point newPoint) { Link link = this.AdornedLink; Route route = link.Route; ReshapeBehavior behavior = GetReshapeBehavior(this.Handle); if (route.Orthogonal) // need to adjust adjacent points as well { if (this.HandleIndex == route.FirstPickIndex + 1) { int midfirst = route.FirstPickIndex + 1; if (behavior == ReshapeBehavior.Vertical) { // move segment vertically route.SetPoint(midfirst, new Point(route.GetPoint(midfirst - 1).X, newPoint.Y)); route.SetPoint(midfirst + 1, new Point(route.GetPoint(midfirst + 2).X, newPoint.Y)); } else if (behavior == ReshapeBehavior.Horizontal) { // move segment horizontally route.SetPoint(midfirst, new Point(newPoint.X, route.GetPoint(midfirst - 1).Y)); route.SetPoint(midfirst + 1, new Point(newPoint.X, route.GetPoint(midfirst + 2).Y)); } } else if (this.HandleIndex == route.LastPickIndex - 1) { int midlast = route.LastPickIndex - 1; if (behavior == ReshapeBehavior.Vertical) { // move segment vertically route.SetPoint(midlast - 1, new Point(route.GetPoint(midlast - 2).X, newPoint.Y)); route.SetPoint(midlast, new Point(route.GetPoint(midlast + 1).X, newPoint.Y)); } else if (behavior == ReshapeBehavior.Horizontal) { // move segment horizontally route.SetPoint(midlast - 1, new Point(newPoint.X, route.GetPoint(midlast - 2).Y)); route.SetPoint(midlast, new Point(newPoint.X, route.GetPoint(midlast + 1).Y)); } } else { // can move anywhere, but need to keep adjacent segments orthogonal int i = this.HandleIndex; Point oldpt = route.GetPoint(i); Point before = route.GetPoint(i - 1); Point after = route.GetPoint(i + 1); if (Geo.IsApprox(before.X, oldpt.X) && Geo.IsApprox(oldpt.Y, after.Y)) { route.SetPoint(i - 1, new Point(newPoint.X, before.Y)); route.SetPoint(i + 1, new Point(after.X, newPoint.Y)); } else if (Geo.IsApprox(before.Y, oldpt.Y) && Geo.IsApprox(oldpt.X, after.X)) { route.SetPoint(i - 1, new Point(before.X, newPoint.Y)); route.SetPoint(i + 1, new Point(newPoint.X, after.Y)); } else if (Geo.IsApprox(before.X, oldpt.X) && Geo.IsApprox(oldpt.X, after.X)) { route.SetPoint(i - 1, new Point(newPoint.X, before.Y)); route.SetPoint(i + 1, new Point(newPoint.X, after.Y)); } else if (Geo.IsApprox(before.Y, oldpt.Y) && Geo.IsApprox(oldpt.Y, after.Y)) { route.SetPoint(i - 1, new Point(before.X, newPoint.Y)); route.SetPoint(i + 1, new Point(after.X, newPoint.Y)); } route.SetPoint(this.HandleIndex, newPoint); } } else // no Orthogonal constraints, just set the new point { route.SetPoint(this.HandleIndex, newPoint); } }
/// <summary> /// Show an <see cref="Adornment"/> with reshape handles at each of the interesting points of the link's <see cref="Route"/>, /// if the link is selected and visible and if <see cref="Northwoods.GoXam.Part.CanReshape"/> is true. /// </summary> /// <param name="part"></param> /// <remarks> /// <para> /// This produces reshape handles at each point of the route, starting with /// <see cref="Northwoods.GoXam.Route.FirstPickIndex"/> and ending with /// <see cref="Northwoods.GoXam.Route.LastPickIndex"/>. /// Depending on whether <see cref="Northwoods.GoXam.Route.Orthogonal"/> is true, /// this will call <see cref="ReshapingBaseTool.SetReshapeBehavior"/> and the <c>Cursor</c> to /// limit the directions in which the user may drag the handle. /// </para> /// <para> /// You can change the template used to create each reshape handle by setting <see cref="Link.LinkReshapeHandleTemplate"/>. /// If that property is null, this uses a default template produces a small square. /// </para> /// </remarks> public override void UpdateAdornments(Part part) { Link link = part as Link; if (link == null) { return; // no Nodes } Adornment adornment = null; if (link.IsSelected) { FrameworkElement selelt = link.Path; if (selelt != null && link.CanReshape() && Part.IsVisibleElement(selelt)) { adornment = link.GetAdornment(ToolCategory); if (adornment == null) { Route route = link.Route; if (route != null) { IEnumerable <Point> pts = route.Points; int numpts = route.PointsCount; bool ortho = route.Orthogonal; if (pts != null && numpts > 2) { // LinkReshapeHandleTemplate: for each reshape handle, not for whole adornment DataTemplate template = link.LinkReshapeHandleTemplate; if (template == null) { template = Diagram.FindDefault <DataTemplate>("DefaultLinkReshapeHandleTemplate"); } LinkPanel panel = new LinkPanel(); int firstindex = route.FirstPickIndex; int lastindex = route.LastPickIndex; // don't bother creating handles for firstindex or lastindex for (int i = firstindex + 1; i <= lastindex - 1; i++) { // expand the DataTemplate to make a copy of a reshape handle FrameworkElement h = template.LoadContent() as FrameworkElement; // needs to be a FrameworkElement so we can set its Cursor if (h == null) { continue; } // identify this particular handle within the LinkPanel LinkPanel.SetIndex(h, i); // now determines its reshape behavior and cursor, depending on whether Orthogonal et al. if (i == firstindex) { // default ReshapeBehavior.None } else if (i == firstindex + 1 && ortho) { Point a = route.GetPoint(firstindex); Point b = route.GetPoint(firstindex + 1); if (Geo.IsApprox(a.X, b.X)) { SetReshapeBehavior(h, ReshapeBehavior.Vertical); h.Cursor = Part.SizeNSCursor; } else if (Geo.IsApprox(a.Y, b.Y)) { SetReshapeBehavior(h, ReshapeBehavior.Horizontal); h.Cursor = Part.SizeWECursor; } } else if (i == lastindex - 1 && ortho) { Point a = route.GetPoint(lastindex - 1); Point b = route.GetPoint(lastindex); if (Geo.IsApprox(a.X, b.X)) { SetReshapeBehavior(h, ReshapeBehavior.Vertical); h.Cursor = Part.SizeNSCursor; } else if (Geo.IsApprox(a.Y, b.Y)) { SetReshapeBehavior(h, ReshapeBehavior.Horizontal); h.Cursor = Part.SizeWECursor; } } else if (i == lastindex) { // default ReshapeBehavior.None } else { SetReshapeBehavior(h, ReshapeBehavior.All); h.Cursor = Part.SizeAllCursor; } panel.Children.Add(h); } adornment = new Adornment(); // for LinkReshapingTool.UpdateAdornments adornment.AdornedElement = selelt; adornment.Category = ToolCategory; adornment.Content = panel; // just provide the FrameworkElement as the Content and as the Visual Child adornment.LocationSpot = Spot.TopLeft; } } } if (adornment != null) { Point loc = link.GetElementPoint(selelt, Spot.TopLeft); adornment.Location = loc; adornment.RotationAngle = link.GetAngle(selelt); adornment.Remeasure(); } } } link.SetAdornment(ToolCategory, adornment); }