Bound node location incorrect on drop from palette

Hello,

I have the following location binding on the View for my nodes:

go:Node.Location="{Binding Data.Location, Mode=TwoWay}"

Where Data is a ViewModel deriving from GraphLinksModelNodeData.

Here is my problem:

  1. I start dragging a node in the Palette.
  2. A clone is created automatically by GoXam. The GraphLinksModelNodeData.Location of the clone is (0,0)
  3. I drop the node onto the Diagram
  4. GoXam moves the node to (0,0) to match the GraphLinksModelNodeData.Location. I want GoXam instead to set the GraphLinksModelNodeData.Location to the position at which the user dropped the node.

What is the best way to fix this?

Thanks!

That’s surprising. If you look at the samples that are similar to what you are describing, such as the FlowChart or the DraggableLink samples, I think they do exactly what you are expecting.

Could you help explain how I could reproduce the behavior you describe?

I tried to reproduce the problem in a simple test project. Everything worked as desired until I created my own GraphLinksModel. Once the Diagram Model was bound to the TestModel, I could no longer drop nodes onto the diagram. I ran into this problem in my main project, but I don’t remember how I fixed it. I assume that I am doing something wrong and wonder if perhaps this isn’t the root of my Location binding problem.

Could you take a look?

Thanks,

-Erin


<Window.Resources>

<GoXamMVVMTest:NodeView />

</Window.Resources>

<Grid.ColumnDefinitions>


</Grid.ColumnDefinitions>
<go:Palette Background=“LightGray” NodesSource="{Binding PaletteNodes}" NodeTemplate="{StaticResource NodeTemplate}" />

<go:Diagram
AllowDrop=“True”
IsManipulationEnabled=“True”
AllowInsert=“True”
AllowEdit=“True”
AllowMove=“True”
IsReadOnly=“False”
Grid.Column=“1”
NodeTemplate="{StaticResource NodeTemplate}"
Model="{Binding DiagramModel}"
HorizontalContentAlignment=“Stretch”
VerticalContentAlignment=“Stretch”/>

public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new MainViewModel();
InitializeComponent();
}
}
public class MainViewModel
{
private TestModel _diagramModel = new TestModel();

    public ObservableCollection<TestNode> PaletteNodes { get; private set; }
    public TestModel DiagramModel
    {
        get { return _diagramModel; }
    }

    public MainViewModel()
    {
        PaletteNodes = new ObservableCollection<TestNode>();
        
        PaletteNodes.Add(new TestNode("One"));
        PaletteNodes.Add(new TestNode("Two"));
        PaletteNodes.Add(new TestNode("Three"));
    }
}

public class TestModel : GraphLinksModel<TestNode, String, String, UniversalLinkData>
{
public ObservableCollection DiagramNodes { get; private set; }
public ObservableCollection DiagramLinks { get; private set; }

    public TestModel()
        : base()
    {
        DiagramLinks = new ObservableCollection<UniversalLinkData>();
        DiagramNodes = new ObservableCollection<TestNode>();
        DiagramNodes.Add(new TestNode("A"));

        NodesSource = DiagramNodes;
        LinksSource = DiagramLinks;
        NodeKeyPath = "Key";
        Modifiable = true;
        HasUndoManager = true;
        LinkFromPath = "From.Key";
        LinkToPath = "To.Key";
    }

    protected override void InsertNode(TestNode nodedata)
    {
        DiagramNodes.Add(nodedata);
    }
}

public class TestNode : GraphLinksModelNodeData
{
public string Label { get; private set; }

    public TestNode(string label)
        : base(Guid.NewGuid().ToString())
    {
        Label = label;
    }

    public override object Clone()
    {
        return new TestNode(Label);
    }
}






GraphLinksModel already includes the implementation of the NodesSource and LinksSource collections (ObservableCollections), so I’m wondering if the use of your own collections is causing the problem, even though you seem to be sharing the references with the NodesSource and LinksSource properties.

I remember now that the drop doesn’t work unless the Palette and the Diagram have the same Model type. I changed the following code, and now everything in the test project is working as desired. I am still trying to reproduce the problem in the main project.

Thanks for your help.

<go:Palette Background=“LightGray” Model="{Binding PaletteModel}" NodeTemplate="{StaticResource NodeTemplate}" />

public class MainViewModel
{
private TestModel _diagramModel = new TestModel(true);
private TestModel _paletteModel = new TestModel(false);

    public TestModel DiagramModel
    {
        get { return _diagramModel; }
    }

    public TestModel PaletteModel
    {
        get { return _paletteModel; }
    }

    public MainViewModel()
    {
        
    }
}

public class TestModel : GraphLinksModel<TestNode, String, String, UniversalLinkData>
{
public ObservableCollection DiagramNodes { get; private set; }
public ObservableCollection DiagramLinks { get; private set; }

    public TestModel(bool isDiagramModel)
        : base()
    {
        DiagramLinks = new ObservableCollection<UniversalLinkData>();
        DiagramNodes = new ObservableCollection<TestNode>();

        NodesSource = DiagramNodes;
        LinksSource = DiagramLinks;
        if (isDiagramModel)
        {
            InsertNode(new TestNode("A"));
            NodeKeyPath = "Key";
            Modifiable = true;
            HasUndoManager = true;
            LinkFromPath = "From.Key";
            LinkToPath = "To.Key";
        }
        else
        {
            InsertNode(new TestNode("One"));
            InsertNode(new TestNode("Two"));
            InsertNode(new TestNode("Three"));
        }
    }
}

Hello again,

I am unfortunately still seeing this problem. I have been unable to reproduce it in my test project, but I have begun to narrow the problem down in my main project.

Here some print statements showing changes to the node Location as I drag it from the palette onto the diagram and then drop it onto the diagram:

Node property changed: Location, 70,180
Node property changed: Location, 100,180
Node property changed: Location, 110,180
Node property changed: Location, 130,180
Node property changed: Location, 140,180
Node property changed: Location, 150,180
Node property changed: Location, 0,0

Here is the stack trace on GraphLinksModelNodeData.PropertyChanged where the property name is “Location”, and the value of location is (0,0):
My.dll!mynamespace.NodeViewModel.OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs propertyChangedEventArgs) Line 64<span =“Apple-tab-span” style=“white-space:pre”> C#
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.Model.GraphLinksModelNodeData.OnPropertyChanged(Northwoods.GoXam.Model.ModelChangedEventArgs e) + 0x7c bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.Model.GraphLinksModelNodeData.RaisePropertyChanged(string pname, object oldval, object newval) + 0x76 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.Model.GraphLinksModelNodeData.Location.set(System.Windows.Point value) + 0x250 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> [Native to Managed Transition]<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.SetValue(object item, object value) + 0xed bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!MS.Internal.Data.ClrBindingWorker.UpdateValue(object value) + 0x97 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Data.BindingExpression.UpdateSource(object value) + 0x92 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.UpdateValue() + 0xb6 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Data.BindingExpression.UpdateOverride() + 0x3d bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Update() + 0x20 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.ProcessDirty() + 0x2f bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Dirty() + 0x40 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.SetValue(System.Windows.DependencyObject d, System.Windows.DependencyProperty dp, object value) + 0x24 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, object value, System.Windows.PropertyMetadata metadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal) + 0x3c4 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.DependencyObject.SetValue(System.Windows.DependencyProperty dp, object value) + 0x35 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.Node.SetLocation(System.Windows.DependencyObject d, System.Windows.Point v) + 0x83 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.Node.Location.set(System.Windows.Point value) + 0xc6 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.Node.Position.set(System.Windows.Point value) + 0x3ac bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.LayoutManager.MoveAnimated(Northwoods.GoXam.Node node, System.Windows.Point newpos) + 0x903 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.Layout.DiagramLayout.DoLayout(System.Collections.Generic.IEnumerable<Northwoods.GoXam.Node> nodes, System.Collections.Generic.IEnumerable<Northwoods.GoXam.Link> links) + 0x43f bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.LayoutManager.PerformLayout() + 0x3c1 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Northwoods.GoWPF.dll!Northwoods.GoXam.LayoutManager.#Xf() + 0x1ba bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) + 0xbd bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) + 0x3a bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() + 0xac bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object state) + 0x38 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0xa7 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x16 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x41 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() + 0x5b bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() + 0x16b bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x5a bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x9b bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) + 0x6b bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) + 0x56 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) + 0x3a bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) + 0x10e bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) + 0xf1 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> [Native to Managed Transition]<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> [Managed to Native Transition]<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) + 0xae bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4b bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) + 0x17 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x6f bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) + 0x26 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> PresentationFramework.dll!System.Windows.Application.Run() + 0x1b bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> my.exe!My.App.Main() + 0x59 bytes<span =“Apple-tab-span” style=“white-space:pre”> C#
<span =“Apple-tab-span” style=“white-space:pre”> [Native to Managed Transition]<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> [Managed to Native Transition]<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) + 0x6b bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x27 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x6f bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0xa7 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x16 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x41 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes<span =“Apple-tab-span” style=“white-space:pre”>
<span =“Apple-tab-span” style=“white-space:pre”> [Native to Managed Transition]<span =“Apple-tab-span” style=“white-space:pre”>

It appears from the stack trace that it is the LayoutManager that is changing the position of the node when it is dropped onto the diagram. I tried setting go:Diagram.Layout to to disable the layout on NodeAdded, but that caused the node not to appear on the diagram at all after drop.

Do you know how to fix this?

Thanks a lot,

-Erin

It appears that the Diagram.Layout, an instance of DiagramLayout, is setting the Node.Location. It is not an instance of TreeLayout. It is the default layout that is only responsible for making sure that each Node has a Location, as opposed to being (NaN, NaN). Apparently that was the case, and it decided to assign the location (0,0) to the Node.

Now the default value for GraphLinksModelNodeData.Location is in fact (NaN, NaN). However it is normally the case that the DraggingTool will assign the location according to the mouse point as the user is dragging.

A better question is why a layout is being performed at all. I cannot tell from the stack trace why a layout is happening. Normally the temporary Node that is created as the drag-and-drop enters the Diagram is not included in the layout at all, because it is in a Layer whose IsTemporary property is true.