GoWPF 3.0.4 with .NET6 - Exception

Hello,

we switched our application from .NET 4.8 to .NET 6 and your GoWPF from 2.2.2 to Version 3.0.4.
Since this change, one of our tests failes with the following exception:

Must disconnect specified child from current parent Visual before attaching to new parent Visual.

StackTrace
at System.Windows.Media.Visual.AddVisualChild(Visual child)
at Northwoods.GoXam.LinkLayer.InternalAdd(Link part)
at Northwoods.GoXam.DiagramPanel.UpdateVisuals(Rect viewb, Boolean remove)
at Northwoods.GoXam.DiagramPanel.UpdateScrollTransform(Point pos, Double scale, Size panelsize, Boolean clipandscroll)
at Northwoods.GoXam.DiagramPanel.UpdateScrollTransform()
at Northwoods.GoXam.DiagramPanel.OnSizeChanged(Size oldsize, Size newsize)
at Northwoods.GoXam.DiagramPanel.DiagramPanel_SizeChanged(Object sender, SizeChangedEventArgs e)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.FrameworkElement.OnRenderSizeChanged(SizeChangedInfo sizeInfo)
at System.Windows.ContextLayoutManager.fireSizeChangedEvents()
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
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.DispatcherOperation.InvokeImpl()
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
— End of stack trace from previous location —
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(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.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run()
at <OurApplication.Main()>

I can not give you an exact function that we call and end up in the problem, but somewere after we do the following stuff:

            LayeredDigraphLayout layoutEngine = new LayeredDigraphLayout
            {
                SetsPortSpots = false,
                AggressiveOption = LayeredDigraphAggressive.None,
                PackOption = LayeredDigraphPack.None,
                LayeringOption = LayeredDigraphLayering.OptimalLinkLength,
                LayerSpacing = 150
            };

            this.compositionDiagramView.Layout = layoutEngine;
            this.compositionDiagramView.LayoutManager.LayoutDiagram(LayoutInitial.InvalidateAll, true);
            this.compositionDiagramView.Layout = new DiagramLayout();
            this.compositionDiagramView.LayoutCompleted += this.OnAutolayoutCompleted;

The “compositionDiagramView” is the WPF View which drives from your class “Diagram”.

We would be happy if you have any ideas for this problem.

Thanks and regards,
Sven

Hmmm. I wouldn’t have thought that replacing the Diagram.Layout would cause any problems.

Still, I have to ask why you are doing that. If you want to perform an initial layout and then never do an automatic layout again, why not set DiagramLayout.Conditions to LayoutChange.InitialOnly? That way you wouldn’t need to replace the Diagram.Layout.

What does your OnAutolayoutCompleted function do?

  1. We allow our users to trigger the “Re-Layout” manually in workflow.
  2. The given event handler is not the problem. The crash happens before the event handler is called. I already removed the event handler without success.
  3. I tried to use different Layouting strategies like TreeLayout or GridLyout. Both ended with the same crash.
  4. I also simplified the layouting implementation in our code (without success):

Init (Constructor):

        public CompositionDiagram(IGraphicalCompositionDiagram graphicalCompositionDiagram, IBaseElement contextObject, IWindow window)
        {
            this.InitializeComponent();

            this.window = window;
            ..
            this.compositionDiagramView.AllowUndo = false;
            ..
            this.compositionDiagramView.PartManager = new CompositionDiagramPartManager();
            this.compositionDiagramView.LayoutManager = new LayoutManager();
            this.compositionDiagramView.LinkingTool = new CompositionDiagramLinkingTool(this);
            this.compositionDiagramView.RelinkingTool = new CompositionDiagramRelinkingTool();
            this.compositionDiagramView.DraggingTool = new CompositionDiagramDraggingTool(this);
            this.compositionDiagramView.PanningTool = new CompositionDiagramPanningTool();
            this.compositionDiagramView.ClickSelectingTool = new CompositionDiagramClickSelectingTool();
            this.compositionDiagramView.DragSelectingTool = new CompositionDiagramDragSelectingTool();
            this.compositionDiagramView.TextEditingTool = new CompositionDiagramTextEditingTool(this);
            this.compositionDiagramView.LinkReshapingTool = new CompositionDiagramLinkReshapingTool(this);
            this.compositionDiagramView.ResizingTool = new CompositionDiagramResizingTool(this);

            // Initialize Layouter
            LayeredDigraphLayout layoutEngine = new LayeredDigraphLayout
                                                    {
                                                        SetsPortSpots = false,
                                                        AggressiveOption = LayeredDigraphAggressive.None,
                                                        PackOption = LayeredDigraphPack.None,
                                                        LayeringOption = LayeredDigraphLayering.OptimalLinkLength,
                                                        LayerSpacing = 150,
                                                        //Conditions = LayoutChange.InitialOnly
                                                    };

            this.compositionDiagramView.Layout = layoutEngine;

            ..
        }
        public void ExecuteLayeredDigraphLayout()
        {
            foreach (SwConnectorViewModel swConnectorViewModel in this.compositionDiagramView.LinksSource.OfType<SwConnectorViewModel>())
            {
                swConnectorViewModel.SwapSwConnectoDirectionIfNeeded();
            }

            this.compositionDiagramView.LayoutManager.LayoutDiagram(LayoutInitial.InvalidateAll, true);
            //this.compositionDiagramView.LayoutCompleted += this.OnAutolayoutCompleted;
        }

OK, so it probably has nothing to do with layouts.

I’m surprised you have so many custom classes. What does the CompositionDiagramPartManager do?

I’m suspecting that the problem has something to do with the virtualization that the DiagramPanel implements. Could you try this initialization?

myDiagram.InitialLayoutCompleted += (s, e) => {
    myDiagram.Panel.Unsupported(23, false);
};

I tried your suggestion, but had to use reflection to set IsVirtualization to false, because it is internal:

...
            this.compositionDiagramView.Layout = layoutEngine;
            this.compositionDiagramView.TemplateApplied += (send, evt) => {

                    if (this.virtualizationPropertyInfo == null)
                    {
                        this.virtualizationPropertyInfo = typeof(DiagramPanel).GetProperty("IsVirtualizing", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                    }
                    this.virtualizationPropertyInfo.SetValue(this.compositionDiagramView.Panel, false);
            };

First it looks like the crash disapears. But after several test runs the crash happens again. But not as often as before. The runtime of our tests increases after this setting, but i think that was to be expected.

Ah, you’re right. There’s a more official way to set that internal property. See my updated code above.

Now I have no idea of what might be causing the problem.