New in GoXam

Hi, I just started with GoDiagram for Silverlight and I need some help.

I have solution which uses GoDiagram for Win.

There is an image loaded. Size of diagram is set to real resolution of the image. Predefined regions (rectangles or circles) are drawn on the image and then user by clicking on the diagram is allowed to add small pictures in place where he cliked. Of course whole diagram has to be zoomed in/out.



So my question is how to do something like this in GoXam? How to set image first? I tried to set background for diagram control but when I bind with model and try to zoom in, only elements from model are zoomed but background stay as it was. I need to zoom whole image with regions and images on it.

Could anyone help me? And suggestion or tips?

Thanks in advance.



Igor

Yes, the Diagram.Background is fixed as the background for the control. If you want something in the background of the diagram that is scrolled and zoomed along with everything else in the diagram, you should use a Node.

You probably want to use an “unbound node” – one that is not data-bound to your model data. Something like:

<go:Diagram x:Name="myDiagram" Padding="10" InitialPosition="0 0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"> <go:Node> <Image Source="Sample_Floorplan.jpg" Width="842" Height="569" go:Node.Location="0 0" go:Part.Selectable="False" go:Part.LayerName="Background" go:Part.LayoutId="None" go:Node.Avoidable="False" /> </go:Node> </go:Diagram>
This assumes that Sample_Floorplan.jpg is in your website.

Hi, I have diagram and Image as background as following your suggestion. And this works nice.

Now I added CustomPartManager to diagram, where I overide FindCategoryForNode method to determine apropriate Template for each node. It was not working until I added NodeTemplateDictionary="{StaticResource NodeTemplates}" to diagram in my XAML.

But because of this my background image stop loading. Only nodes from model shows up. I don’t know how to fix it. I think it somehow can not to determine correct template for this background node.

Could you help me to deal with this? Because I don’t want to move this background unbound node to model.



Thanks in advance

Igor

That unbound Node defined in XAML (in my previous reply) does not use any DataTemplate, because you have explicitly defined all of the elements that it uses.

I believe that any uses of templates will not affect that unbound node. Yes, as you have discovered, you do need to use a DataTemplateDictionary and set the Diagram.NodeTemplateDictionary in order to use multiple templates for nodes. But this will have no effect on any unbound nodes.

Maybe you changed something else that affected finding the image? Or did you move the node or change its visibility?

If you look at the visual tree in the debugger, can you see that there’s a single node in the background layer? You can also find the node programmatically, if that makes it easier to examine in the debugger.

I have this exact same issue. While your solution of inserting a node as background does work, it is no option as I use a click creating tool to place nodes and clicking somewhere on the background node now does not create nodes anymore. Do I have to catch the MouseLeftButtonDown event, identify the node being clicked on as background node and then call the node creation explicitly or is there a better way?

I think it would be easier to change your background node to have IsHitTestVisible (a UIElement property) set to false.

Where do I have to set this in order to make it work in a DataTemplate?

<DataTemplate x:Key="BackgroundNodeTemplate">
    <go:NodePanel go:Part.LayerName="{Binding Path=Data.Layer.Name}"
                  go:Part.Selectable="False"
                  go:Part.LayoutId="None"
                  go:Node.Location="0 0"
                  IsHitTestVisible="False">
        <Image Source="../Images/Test.png">
    </go:NodePanel>
</DataTemplate>

This doesn’t work. The IsHitTestVisible property in the Node itself is still set to true afterwards.

Ah, so you plan to have more than one of these special nodes.

I haven’t tried this, but I think it might be easier if you created a separate Layer to hold these nodes:

      myDiagram.TemplateApplied += (s, e) => {
        var lay = new NodeLayer();
        lay.Id = "Grid";
        lay.IsHitTestVisible = false;
        myDiagram.Panel.Children.Insert(0, lay);
      };

and then you can set go:Part.LayerName=“Grid” in the template.

But if you really expect to put these nodes in various layers, as you imply with the binding, using a single NodeLayer that is not hit-testable won’t work.

In fact I have multiple background layers, one for each of the background nodes, as I need to blend them in and out dynamically. On each of these layers there won’t be anything else than a single background-node. Unfortunately, setting every background layers’ IsHitTestVisible to false doesn’t work. I checked whether the property is set correctly and indeed it is but the ClickCreatingTool still does nothing upon double clicking on the background node.
I’m not sure what you’re trying to say with your last statement but I also tried it with only a single background layer (and node) without any luck.

I just tried this code in an app which did not otherwise use layers:

myDiagram.Panel.FindLayer<NodeLayer>("Background").IsHitTestVisible = false;

and I added a binding on go:Part.LayerName so that some nodes were put in the Background Layer.

I found that I was unable to click on or select any node in the Background Layer – even Buttons that are in the visual tree of the nodes acted as if they didn’t exist. Mouse events passed on to whatever was behind the node.

Imagine I have this structure:

Background-Basement: NodeLayer
Background-GroundFloor: NodeLayer
Apartment-Basement: NodeLayer
Apartment-GroundFloor: NodeLayer
Room-Basement: NodeLayer
Room-Basement: LinkLayer
Room-GroundFloor: NodeLayer
Room-GroundFloor: LinkLayer
Component-Basement: NodeLayer
Component-Basement: LinkLayer
Component-GroundFloor: NodeLayer
Component-GroundFloor: LinkLayer

Depending on the state of the application, I’m blending the layers in and out. Imagine I have selected Component-Basement, then the following layers are visible:

Background-Basement: NodeLayer
Apartment-Basement: NodeLayer
Room-Basement: NodeLayer
Component-Basement: NodeLayer
Component-Basement: LinkLayer

Being in the Component state I have a palette with different nodes which I drag on my diagram, so I have no trouble here.

Now, selecting Room-GroundFloor:

Background-GroundFloor: NodeLayer
Apartment-GroundFloor: NodeLayer
Room-GroundFloor: NodeLayer
Room-GroundFloor: LinkLayer

As you can see, there is always one background layer visible. This background layer contains a single background node, as mentioned before. All the background layers are set HitTestVisible=false and yet, I can manipulate the background node any way I want, drag, click, delete …

I’m creating the layers like this:

Layer diagramLayer = (Layer)Activator.CreateInstance(layer.LayerType);
diagramLayer.Id = "Background - Basement";
diagramLayer.IsHitTestVisible = false;
this._diagram.Panel.Children.Insert(0, diagramLayer);

I also tested it without adding any layer and just put the node into the initially existing background node and it did not work at all.

I can’t explain why it’s working for me but not for you.

What version of GoXam are you using? What version of .NET are you targeting?

I’m using Version 2.2.2.45, targeting .NET 4.5.2

So was I, with respect to version numbers.

I’ll try something closer to what you’re doing. Have you tried something like what I did?

I’m not sure what you exactly did. I commented out all lines of code in which I add layers and nodes to the diagram and then only added a single node to the default “background” layer and set that layer’s IsHitTestVisible to false. No difference.

When I tried several scenarios yesterday, I found that settng IsHitTestVisible to false usually did not work. I have not yet figured out what is going on.

Some web sites mentioned a bug with IsHitTestVisible. FYI, the DiagramPanel.FindElementAt and similar methods call VisualTreeHelper.HitTest.

So … it looks like I have to call the node creation explicitly? How would I go on with this, which function is being called in which way when creating a node with the ClickCreatingTool?

I think it would be best if you customized the ClickCreatingTool by overriding the FindPart method to ignore your “background” node.

I haven’t tried this, but something like:

    protected override Part FindPartAt(Point p, bool selectable) {
      Diagram diagram = this.Diagram;
      if (diagram == null) return null;
      if (selectable)
        return diagram.Panel.FindElementAt<Part>(p, Diagram.FindAncestor<Part>, x => x.CanSelect() && x.Layer.Id != "Background", SearchLayers.All);
      else
        return diagram.Panel.FindElementAt<Part>(p, Diagram.FindAncestor<Part>, x => x.Layer.Id != "Background", SearchLayers.All);
    }

You may need to fiddle with the predicate that is passed to FindElementAt to suit your needs.

Diagram.FindAncestor results in a compiler error “Cannot access internal method ‘FindAncestor’ here”

That is one of several internal static helper functions:

    internal static T FindParent<T>(DependencyObject v) where T : DependencyObject {
      if (v == null) return null;
      return VisualTreeHelper.GetParent(v) as T;
    }

    internal static T FindAncestor<T>(DependencyObject v) where T : DependencyObject {
      if (v == null) return null;
      DependencyObject p = VisualTreeHelper.GetParent(v);
      if (p == null) return null;
      T t = p as T;
      if (t != null) return t;
      return FindAncestor<T>(p);
    }

    internal static T FindAncestorOrSelf<T>(DependencyObject v) where T : DependencyObject {
      T t = v as T;
      if (t != null) return t;
      return FindAncestor<T>(v);
    }