Adornments issues for Rotation

Hi Walter,

I was trying to implement Rotation of a node and I am doing something like node.RotationAngle +=90
and seems to be working properly but, there are some issues I am facing related to it

  1. For testing I just enabled Rotatable = True in the xaml for one of our nodes and using the rotating handle I tried to rotate. the aggregate rotates but the adornments are haphazard like below
    image

  2. When I do undo I am facing the same issue

  3. Also while deserializing I face the same issue but along with that I see that all the nodes come 180 rotated even though the value of node.RotationAngle is 90. For serializing we also have a additional property in NodeData which is binded to xaml template

I have tried doing
node.UpdateLayout();
node.UpdateAdornments();

but nothing worked. Also I am not binding Rotating tool as we do not need the rotating handle. From our UI there will be a button in the menu which will be used to rotate the aggregate by 90 degs.

Am I missing something here for the implementation?

Interactive rotation does work, as you can see in samples such as DraggableLink. Maybe the node template doesn’t have the LocationSpot == Spot.Center to match the (default) RotationSpot.

But you don’t want users to use the RotatingTool, do you?

You have a TwoWay Binding on Node.RotationAngle ?

I do not need the Interactive rotation. The requirement is to have only the standard 90 deg everytime on click of a UI button.

            if (isClockwiseRotation)
            {
                node.RotationAngle += 90;
            }
            else
            {
                node.RotationAngle -= 90;
            }
            node.LinksConnected.ToList().ForEach(link => { link.Route.UpdateLayout(); link.Route.RecomputePoints(); });
            node.UpdateLayout();
            node.UpdateAdornments();

This is my code block for rotate and it works fine in this sequence but I have issues with the above 3 scenarios

I do have two way binding for RotationAngle

go:Node.RotationAngle =“{Binding Path=Data.RotationAngle, Mode=TwoWay}”
go:Node.LocationSpot=“Center”

I defined a UI button to do this:

          myDiagram.StartTransaction("rot90");
          n.RotationAngle += 90;
          myDiagram.CommitTransaction("rot90");

and that seems to work well, except for the Adornments. It seems the default adornment templates don’t detect changes to the angle, and I’m not sure why it works correctly with the RotatingTool.

A work-around for undo/redo problems:

      myDiagram.Model.Changed += (object sndr, ModelChangedEventArgs e) => {
        if (e.Change == ModelChange.FinishedUndo || e.Change == ModelChange.FinishedRedo) {
          foreach (var n in myDiagram.Nodes) {
            n.InvalidateRelationships();
            if (n.IsSelected) { n.IsSelected = false; n.IsSelected = true; }
          }
        }
      };

Alas, the proper method to call is internal. Setting IsSelected to false and then back to true is just a hack that seems to work.

I tried adding the RotatingTool and following code I added as part of it ( the code is borrowed from DraggableLink example from Demo application

 class CustomRotatingTool: RotatingTool
 {
    private const String ToolCategory = "Resize";
 
    public override void UpdateAdornments(Part part)
    {
        if (part == null || part is Link) return;  // this tool never applies to Links
        Adornment adornment = null;
        if (part.IsSelected)
        {
            FrameworkElement selelt = part.SelectionElement;
            if (selelt != null && part.CanRotate() && Part.IsVisibleElement(selelt))
            {
                adornment = part.GetAdornment(ToolCategory);
                if (adornment != null)
                {
                        var nodeData = part.Data as NodeData;
                        Node node = Diagram.PartManager.FindNodeForData(nodeData, Diagram.Model);
                        adornment.Location = node.Location;
                }
            }
        }
        part.SetAdornment(ToolCategory, adornment);
    }

    protected override void DoRotate(double newangle)
    {
        base.DoRotate(newangle + 90);
    }

    public override void DoCancel()
    {
        base.DoRotate(this.OriginalAngle);
        StopTool();
    }

After the above change I see all my nodes are getting adornments like this irrespective of rotation done on them or not. But the adornment are relatively correct. I mean they don’t go in random way on undo redo or for any number of rotation or loading a model.
image
How can I make to the center of the node ?

Do you have a custom ResizeAdornmentTemplate? The problem is occurring with what appears to be the resize adornment for the rotated node, not the rotate adornment.

We do not have any custom resize adornment templates. Also we are not making use of rotateTemplate also.

Yes you are right the issue is with the resize adornments. It is not getting the location properly

I just tried a sample. Here’s my node template:

    <DataTemplate x:Key="DataObjectNodeTemplate1">
      <go:NodePanel Height="{Binding Path=Data.Height, Mode=TwoWay}"
                    Width="{Binding Path=Data.Width, Mode=TwoWay}"
                    go:Part.SelectionAdorned="True"
                    go:Part.Resizable="True"
                    go:Node.RotationAngle="{Binding Path=Data.Angle, Mode=TwoWay}"
                    go:Part.Rotatable="True"
                    go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">
        <Path x:Name="Shape" Stretch="Fill"
              Stroke="Gray" StrokeThickness="1"
              Fill="{Binding Path=Data.Color}"
              Data="F1 M0 0 L0 8 8 8 8 2 6 0 0 0Z M6 0 L6 2 8 2" />
        <TextBlock Text="{Binding Data.Key}"
                   HorizontalAlignment="Center" VerticalAlignment="Center" />
      </go:NodePanel>
    </DataTemplate>

The data class:

  public class NodeInfo : GraphLinksModelNodeData<String> {
    public String Color {
      get { return _Color; }
      set { if (_Color != value) { String old = _Color; _Color = value; RaisePropertyChanged("Color", old, value); } }
    }
    private String _Color = "Fuchsia";

    public double Width {
      get { return _Width; }
      set { if (_Width != value) { double old = _Width; _Width = value; RaisePropertyChanged("Width", old, value); } }
    }
    private double _Width = 60;

    public double Height {
      get { return _Height; }
      set { if (_Height != value) { double old = _Height; _Height = value; RaisePropertyChanged("Height", old, value); } }
    }
    private double _Height = 60;

    public double Angle {
      get { return _Angle; }
      set { if (_Angle != value) { double old = _Angle; _Angle = value; RaisePropertyChanged("Angle", old, value); } }
    }
    private double _Angle = 0;
  }

The XAML:

  <Grid>
    <go:Diagram x:Name="myDiagram" NodeTemplate="{StaticResource DataObjectNodeTemplate1}">
      <go:Diagram.Layout>
        <go:ForceDirectedLayout />
      </go:Diagram.Layout>
    </go:Diagram>
    <Button Click="Button_Click" HorizontalAlignment="Right" VerticalAlignment="Bottom">Rotate Selected Node</Button>
  </Grid>

And the button’s click handler:

    private void Button_Click(object sender, RoutedEventArgs e) {
      var node = myDiagram.SelectedParts.First() as Node;
      if (node != null) {
        myDiagram.StartTransaction("rotated node");
        node.RotationAngle += 90;
        myDiagram.CommitTransaction("rotated node");
      }
    }

Everything worked well, including undo/redo and selection and resize and rotate adornments.

But I have seen cases where the adornments were not updating as expected. There must be some case that I’m missing to account for the behavior.