graphgraph-layoutmsagl

MSAGL: Update visible graph (add edge)


I am building a simple sample application to test out MSAGL but am getting a KeyNotFoundException if I add an edge after the graph is shown.

I suppose this has to do with that I need to somehow refresh some data - or perhaps use another approach, but don't know how.

Sample:

private void MainWindow_Loaded(object sender, RoutedEventArgs e) {
    GraphViewer graphViewer = new GraphViewer();
    graphViewer.BindToPanel(Panel);
    Graph graph = new Graph();

    graph.AddEdge("A", "B");

    graphViewer.MouseUp += (s, ev) =>
    {
        if (graphViewer.ObjectUnderMouseCursor != null)
        {
            graph.AddEdge("A", "C"); // BOOM!
        }
    };

    graph.Attr.LayerDirection = LayerDirection.LR;
    graphViewer.Graph = graph;
}

Exception details:

System.Collections.Generic.KeyNotFoundException was unhandled
  HResult=-2146232969
  Message=The given key was not present in the dictionary.
  Source=mscorlib
  StackTrace:
       at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
       at Microsoft.Msagl.WpfGraphControl.GraphViewer.<CreateVNode>b__1d(Edge e) in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\tools\WpfGraphControl\GraphViewer.cs:line 1263
       at Microsoft.Msagl.WpfGraphControl.VNode.<get_OutEdges>b__10(Edge e) in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\tools\WpfGraphControl\VNode.cs:line 416
       at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
       at Microsoft.Msagl.Drawing.LayoutEditor.<Edges>d__20.MoveNext() in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\Drawing\LayoutEditing\LayoutEditor.cs:line 597
       at Microsoft.Msagl.Drawing.LayoutEditor.TheDefaultObjectDecoratorRemover(IViewerObject obj) in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\Drawing\LayoutEditing\LayoutEditor.cs:line 454
       at Microsoft.Msagl.Drawing.LayoutEditor.UnselectWithoutRemovingFromDragGroup(IViewerObject obj) in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\Drawing\LayoutEditing\LayoutEditor.cs:line 573
       at Microsoft.Msagl.Drawing.LayoutEditor.UnselectObjectForDragging(IViewerObject obj) in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\Drawing\LayoutEditing\LayoutEditor.cs:line 567
       at Microsoft.Msagl.Drawing.LayoutEditor.AnalyzeLeftMouseButtonClick() in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\Drawing\LayoutEditing\LayoutEditor.cs:line 498
       at Microsoft.Msagl.Drawing.LayoutEditor.HandleMouseUpOnLayoutEnabled(MsaglMouseEventArgs args) in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\Drawing\LayoutEditing\LayoutEditor.cs:line 865
       at Microsoft.Msagl.Drawing.LayoutEditor.ViewerMouseUp(Object sender, MsaglMouseEventArgs args) in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\Drawing\LayoutEditing\LayoutEditor.cs:line 858
       at System.EventHandler`1.Invoke(Object sender, TEventArgs e)
       at Microsoft.Msagl.WpfGraphControl.GraphViewer.OnMouseUp(MouseEventArgs e) in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\tools\WpfGraphControl\GraphViewer.cs:line 511
       at Microsoft.Msagl.WpfGraphControl.GraphViewer.GraphCanvasRightMouseUp(Object sender, MouseButtonEventArgs e) in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\tools\WpfGraphControl\GraphViewer.cs:line 183
       at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
       at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
       at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
       at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)
       at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
       at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
       at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
       at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
       at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
       at System.Windows.Input.InputManager.ProcessStagingArea()
       at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
       at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
       at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
       at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
       at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
       at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
       at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
       at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
       at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
       at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
       at System.Windows.Application.RunDispatcher(Object ignore)
       at System.Windows.Application.RunInternal(Window window)
       at System.Windows.Application.Run(Window window)
       at System.Windows.Application.Run()
       at MinWpfApp.App.Main() in c:\Users\br\Desktop\MSAGL\automatic-graph-layout-master\GraphLayout\Samples\MinWpfApp\obj\Debug\App.g.cs:line 0
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

Solution

  • The solution I found that works, is to detach the graph from the graph viewer before updating the graph and then reattaching it afterwards.

    private void MainWindow_Loaded(object sender, RoutedEventArgs e) {
        GraphViewer graphViewer = new GraphViewer();
        graphViewer.BindToPanel(Panel);
        Graph graph = new Graph();
    
        graph.AddEdge("A", "B");
    
        graphViewer.MouseUp += (s, ev) =>
        {
            if (graphViewer.ObjectUnderMouseCursor != null)
            {
                graphViewer.Graph = null;
                graph.AddEdge("A", "C");
                graphViewer.Graph = graph;
            }
        };
    
        graph.Attr.LayerDirection = LayerDirection.LR;
        graphViewer.Graph = graph;
    }
    

    That doesn't give any fancy animation effects but it appears to work.