Linking Problem since created MVVM

Hi there!

Since I removed all code from "WorkflowView.xaml.cs" to the "WorkflowViewModel.cs" (which was quite difficult) I can not make link's anymore. If I click on a port of a node and want do create a link, it doesn't create a link, but the node is moving when I drag
I found out, that the DoStart() Method of "WorkflowLinkingTool" isn't called anymore.
By setting the CurrentTool of the Diagram to WorkflowLinkingTool (I do this, when i click on a node) somehow i can make links, but it doesn't work as before MVVM.
The RelinkingTool work as intended, it doesn't changed.
Can anyone help me? :)
greetings
pfunz

You need to make sure that either or both of go:Node.LinkableFrom and go:Node.LinkableTo are set to true on the port element if go:Node.PortId has been set, or on the root element if no such element has go:Node.PortId set (i.e. if the whole node is the default port element).

What sample did you start from? I have no idea of what might be in WorkflowView.xaml.*.

The WorkflowView is nearly the same as the FlowChart Example of the GoSilverlightDemo4.

LinkableFrom and LinkableTo are set to true, where it should be.
The big difference is only that i had to bind many propertys to the ViewModel instead of assign it directly:
<go:Diagram x:Name="workflowDiagram" 
                DataContext="{Binding WorkflowViewModel}"
                Model="{Binding Path=WorkflowModel, Mode=TwoWay}"
                PartManager="{Binding Path=PartManager}"
                LinkingTool="{Binding Path=LinkingTool}"
                RelinkingTool="{Binding Path=RelinkingTool}"
is this a problem?
if not, Eventhandling is different like this:
....
<i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction CommandParameter="{Binding ElementName=workflowDiagram,Mode=OneWay}" Command="{Binding SelectStepCommand}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
</go:Diagram>
 
 
 

In general you should not try to data-bind the non-UI parts of the Diagram that are separate classes but are owned by the Diagram. That includes properties such as Diagram.PartManager and Diagram.LinkingTool.

The reason the functionality of the Diagram class has been split up into separate classes is to make them simpler and easier to customize and replace. (Subclassing controls is harder than just subclassing a Tool or the PartManager.)

But you can still make it work, I think. How do you initialize the data.PartManager and data.LinkingTool (et al) properties? Do you make sure you do not share any such instances between Diagrams? Does your LinkingTool’s CanStart method get called, and if so, does it return true?

The LinkingTool is a class named WorkflowLinkingTool, which is inherited by LinkingTool :) ( i know little confusing)

how I initialize? simple with

this.LinkingTool = new WorkflowLinkingTool

and I can’t share this instances, because there are only one Diagram^^

I didn’t know if CanStart is called, because i didn’t override this method from the base class. But what I know is, that the DoStart method isn’t called. therefore the CanStart method must return false, or there is a problem with the ToolManager because he can’t make LinkingTool its current one

I suggest that you confirm in the debugger that the Diagram.LinkingTool is indeed an instance of WorkflowLinkingTool.

If that is the case I suggest you then try an override of CanStart, just to debug whether it is called and what it returns. It requires that the LinkingTool.Diagram be your Diagram, that Diagram.isReadOnly is false, that Diagram.allowLink is true, that the Model.Modifiable is true, that the event be a left mouse button down and that FindLinkablePort() returns a port element. You can read the documentation for FindLinkablePort for more details.

i’ll try that, thank you

okay, i’ve tried it, but it doesn’t work.

Diagram.Linkingtool, is the instance of WorkflowLinkingTool.
CanStart returns false.
FindLinkablePort isn't called (but it should find a port, because when i drag the mousecurser over a port, then the "TheCurserIsOverAnLink" - curser is shown.)
Diagram.isReadOnly and Diagram.allowLink i've set in the Xaml.
Model.Modifiable is true, i've set this in the ViewModel constructor.
The documentation says that CanStart returns true, when FindLinkablePort() returns a valid port.
But how can i fix this, if FindLinkablePort() isn't called?
edit: I found out, that the FindLinkablePort Method is called after CanStart, if I simply returns true. After that i could make links as intended, but grading is working properly anymore. It only works, if am faster than the SetPortsVisible method which is called by MouseEnter event
edit2: okay, it isn't only dragging which doesn't work properly sind i let CanStart return always true, so I should find an other solution
edit3: I've looked around in the Documentation and found something interessting:
CanStart Method:
This tool can run when the diagram allows linking, the model is modifiable, the left-button mouse drag has moved far enough away to not be click, and when FindLinkablePort has returned a valid port.
FindLinkablePort:
Both CanStart and DoActivate call this method, although the latter only does so if StartPort is null.
FindLinkablePort returns some instance of Elipse and in fact, that it returns something, it HAS TO be a valid port! (if not it would return null)
AllowLink="True"
workflowModel.Modifiable = true;
this should be every condition, that CanStart returns "true", but it doesn't
Here is the definition of LinkingTool.CanStart:
public override bool CanStart() {
if (!base.CanStart()) return false; // this checks that this.IsMouseEnabled is true
Diagram diagram = this.Diagram;
if (diagram == null || diagram.IsReadOnly) return false;
if (!diagram.AllowLink) return false;
IDiagramModel model = diagram.Model;
if (model == null || !model.Modifiable) return false;
// require left button & that it has moved far enough away from the mouse down point, so it isn't a click
if (!IsLeftButtonDown()) return false;
// don't include the following check when this tool is running modally
if (diagram.CurrentTool != this) {
if (!IsBeyondDragSize()) return false;
}
FrameworkElement port = FindLinkablePort();
return (port != null);
}

okay, when im debugging

if (diagram.CurrentTool != this)
{
if (!IsBeyondDragSize()) return false;
}
this returns the false.
in the GoSilverlightDemo4 (diagram.CurrentTool != this) is also true as in my Code, so I need to focus on IsBeyondDragSize()
Documentation says:
"true if the first and last mouse points are more than two pixels apart in either axis"
also the Difference of
FirstMousePointInModel
and
LastMousePointInModel
I analyzed this: I dragged veeeeeery slowly and at 3 pixels, the the node suddently is selected.
By writing somethin in the Output, i found out, that CanStart is called ONLY twice (once per pixel I moved the cursor). So exactly at the moment where IsBeyondDragSize() returns true, CanStart isn't called anymore and the node is selected, because it thinks, it only was a click
in the GoSilverlightDemo4 CanStart is called three times and of course at the third pixel, the arrow is shown.
Do you have any hints, why CanStart is Called only twice in my Code?
edit:
The CanStart method is called by ToolManager.DoMouseMove. By using Reflector I can see, that if an other tool CanStart then no other tool calls the CanStart anymore:
foreach (IDiagramTool tool in diagram.MouseMoveTools)
                    {
                        if (tool.CanStart())
                        {
			some code..
                        }
                        break;
so the problem is the Tool which is responsable for selecting nodes

Look at the Diagram.MouseMoveTools list – your custom linking tool should be the first one on the list.

haha, I came exactly to the same conclusion at this moment :)

GoSilverlightDemo4:
this.Diagram.MouseMoveTools
Count = 4
[0]: {FlowChart.MyLinkingTool} [1]: {Northwoods.GoXam.Tool.DraggingTool} [2]: {Northwoods.GoXam.Tool.DragSelectingTool} [3]: {Northwoods.GoXam.Tool.PanningTool}
My Code:
this.Diagram.MouseMoveTools Count = 4 [0]: {Northwoods.GoXam.Tool.DraggingTool} [1]: {Northwoods.GoXam.Tool.DragSelectingTool} [2]: {Northwoods.GoXam.Tool.PanningTool} [3]: {Roche.InCore.DS.UI.WorkflowComponents.WorkflowLinkingTool}
okay, how can I fix this, without workaround (In which I only put the WorkflowLinkingTool at the first place) ?

okay, Workaround doesn’t work, because the ToolManager (I’m not sure) don’t want that I make changes in the Diagram.MouseMoveTools list.

If I initialize WorkflowLinkingTool in the constructor of the WorkflowView.xaml.cs (code behind) everything works fine. But this isn't the purpose of MVVM

You said you did:
this.LinkingTool = new WorkflowLinkingTool
Where is this code? In a Diagram-derived class constructor?
If so, that ought to work.

But the normal way to do this is in XAML:

<go:Diagram …>
go:Diagram.LinkingTool
<local:WorkflowLinkingTool />
</go:Diagram.LinkingTool>
</go:Diagram>

In the Code behind:
 public partial class WorkflowView : UserControl
{
          <SPAN style="COLOR: blue">public</SPAN> WorkflowView()
    {
        <SPAN style="COLOR: blue">this</SPAN>.InitializeComponent();
        <SPAN style="COLOR: blue">this</SPAN>.workflowDiagram.LinkingTool = <SPAN style="COLOR: blue">new</SPAN> <SPAN style="COLOR: #2b91af">WorkflowLinkingTool</SPAN>();
    }      

}</PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: ">This works, But this isn't MVVM.</PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: ">View: </PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "><SPAN style="COLOR: blue"><</SPAN><SPAN style="COLOR: #a31515">go</SPAN><SPAN style="COLOR: blue">:</SPAN><SPAN style="COLOR: #a31515">Diagram</SPAN><SPAN style="COLOR: red"> x</SPAN><SPAN style="COLOR: blue">:</SPAN><SPAN style="COLOR: red">Name</SPAN><SPAN style="COLOR: blue">=</SPAN><SPAN style="COLOR: blue">"workflowDiagram"</SPAN> 
           <SPAN style="COLOR: red"> DataContext</SPAN><SPAN style="COLOR: blue">="{</SPAN><SPAN style="COLOR: #a31515">Binding</SPAN><SPAN style="COLOR: red"> WorkflowViewModel</SPAN><SPAN style="COLOR: blue">}</SPAN><SPAN style="COLOR: blue">"</SPAN>
           <SPAN style="COLOR: red"> Model</SPAN><SPAN style="COLOR: blue">="{</SPAN><SPAN style="COLOR: #a31515">Binding</SPAN><SPAN style="COLOR: red"> Path</SPAN><SPAN style="COLOR: blue">=</SPAN><SPAN style="COLOR: blue">WorkflowModel</SPAN><SPAN style="COLOR: blue">,</SPAN><SPAN style="COLOR: red"> Mode</SPAN><SPAN style="COLOR: blue">=</SPAN><SPAN style="COLOR: blue">TwoWay</SPAN><SPAN style="COLOR: blue">}</SPAN><SPAN style="COLOR: blue">"</SPAN>
           <SPAN style="COLOR: red"> PartManager</SPAN><SPAN style="COLOR: blue">="{</SPAN><SPAN style="COLOR: #a31515">Binding</SPAN><SPAN style="COLOR: red"> Path</SPAN><SPAN style="COLOR: blue">=</SPAN><SPAN style="COLOR: blue">PartManager</SPAN><SPAN style="COLOR: blue">}</SPAN><SPAN style="COLOR: blue">"</SPAN>
           <SPAN style="COLOR: red"> LinkingTool</SPAN><SPAN style="COLOR: blue">="{</SPAN><SPAN style="COLOR: #a31515">Binding</SPAN><SPAN style="COLOR: red"> Path</SPAN><SPAN style="COLOR: blue">=</SPAN><SPAN style="COLOR: blue">LinkingTool</SPAN><SPAN style="COLOR: blue">}</SPAN><SPAN style="COLOR: blue">"</SPAN></PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "><SPAN style="COLOR: blue">	       .....</SPAN></PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "><SPAN style="COLOR: blue">ViewModel:</SPAN></PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "><SPAN style="COLOR: blue"></SPAN> </PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "><SPAN style="COLOR: blue"><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "><SPAN style="COLOR: blue">private</SPAN> <SPAN style="COLOR: #2b91af">WorkflowLinkingTool</SPAN> linkingTool = <SPAN style="COLOR: blue">new</SPAN> <SPAN style="COLOR: #2b91af">WorkflowLinkingTool</SPAN>();
public WorkflowLinkingTool LinkingTool
{
get
{
return this.linkingTool;
}
        <SPAN style="COLOR: blue">set</SPAN>
        {
            <SPAN style="COLOR: blue">this</SPAN>.linkingTool = <SPAN style="COLOR: blue">value</SPAN>;
            <SPAN style="COLOR: blue">this</SPAN>.OnPropertyChanged(() => <SPAN style="COLOR: blue">this</SPAN>.LinkingTool);
        }
    }</PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "> </PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: ">this doesn't work, but this is MVVM</PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: ">The Condition for MVVM is, that the View doesn't handle any Methods or contains any Properties</PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: ">So you mustn't initialize the WorkflowLinkingTool in the View.</PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "><img src="http://www.silverlightblog.net/wp-content/uploads/2011/01/MVVM_Relation.jpg" height="299" width="411" border="0" /></PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "> </PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: ">so I'm looking for a solution, where MVVM doesn't get hurt</PRE></PRE></SPAN></PRE><PRE style="FONT-FAMILY: Consolas; COLOR: black; FONT-SIZE: 13px; white: "><SPAN style="COLOR: blue"></SPAN> </PRE></PRE>

Problem Solved!

this.Diagram.MouseMoveTools Count = 4 [0]: {Roche.InCore.DS.UI.WorkflowComponents.WorkflowLinkingTool} [1]: {Roche.InCore.DS.UI.WorkflowComponents.WorkflowDraggingTool} [2]: {Roche.InCore.DS.UI.WorkflowComponents.WorkflowDragSelectingTool} [3]: {Roche.InCore.DS.UI.WorkflowComponents.WorkflowPanningTool}
I did the same initializing process with the three other tools in the MouseMoveTools List so the same thing happens to this tools as with the LinkingTool, they were added at the end of the list :)