Blazor-Diagrams / Blazor.Diagrams

A fully customizable and extensible all-purpose diagrams library for Blazor
https://blazor-diagrams.zhaytam.com
MIT License
889 stars 169 forks source link

Replacing DragNewBehavior causes RenderLinkTree NullReferenceError #389

Open tarp-jusf opened 6 months ago

tarp-jusf commented 6 months ago

I have tried making a custom DragNewBehavior, because I only want to be able to drag links from outgoing Ports. This results in the RenderLinkTree call to experience a NullReferenceError.

I have tried copying the DragNewBehavior code to check if it was my code, but it also breaks. I traced some code while debugging and copied the tracelog up until the error.

dbug: Microsoft.AspNetCore.Components.RenderTree.Renderer[3]
      Rendering component 82 of type Blazor.Diagrams.Components.Renderers.PortRenderer
dbug: Microsoft.AspNetCore.Components.RenderTree.Renderer[3]
      Rendering component 59 of type Blazor.Diagrams.Components.DiagramCanvas
dbug: Microsoft.AspNetCore.Components.RenderTree.Renderer[1]
      Initializing component 90 (Blazor.Diagrams.Components.Renderers.LinkRenderer) as child of 59 (Blazor.Diagrams.Components.DiagramCanvas)
dbug: Microsoft.AspNetCore.Components.RenderTree.Renderer[3]
      Rendering component 90 of type Blazor.Diagrams.Components.Renderers.LinkRenderer
dbug: Microsoft.AspNetCore.Components.RenderTree.Renderer[1]
      Initializing component 91 (Blazor.Diagrams.Components.LinkWidget) as child of 90 (Blazor.Diagrams.Components.Renderers.LinkRenderer)
dbug: Microsoft.AspNetCore.Components.RenderTree.Renderer[3]
      Rendering component 91 of type Blazor.Diagrams.Components.LinkWidget
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Object reference not set to an instance of an object.
System.NullReferenceException: Object reference not set to an instance of an object.
   at Blazor.Diagrams.Components.LinkWidget.BuildRenderTree(RenderTreeBuilder __builder)
   at Microsoft.AspNetCore.Components.ComponentBase.<.ctor>b__6_0(RenderTreeBuilder builder)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
tarp-jusf commented 6 months ago

After looking into this, it seems that DragNewLinkBehavior is being used multiple places. So replacing this behavior will make the whole thing break. image

A solution could be to change the RegisterBehavior function replace the class associtated with a certain type of behavior.

That way when you use e.g GetBehavior, you could get a return of type CustomDragNewLinkBehavior, which can be checked in a if-statement. image

tarp-jusf commented 6 months ago

After even more investigation, the LinkWidget, which uses DragNewLinkBehavior, is used by the LinkRenderer. The LinkRenderer is not replaceable, unlike other controls that are.

I think there's some architecture design that's gone awry, and trying to change things to fit, becomes a cascade of changes.

There might be some ground-up work that needs to be rethought.

zHaytam commented 6 months ago

Hello, thank you for opening this issue.

First of all, regarding your specific need, wouldn't using the Options.Links.Factory (https://github.com/Blazor-Diagrams/Blazor.Diagrams/blob/master/src/Blazor.Diagrams.Core/Options/DiagramLinkOptions.cs#L30) option solve your issue? If the factory returns null (instead of a link instance), then nothing happens. You can simply check whether it's an outgoing port and response accordingly.

Secondly, you are right. The fact that other components use behaviors directly is an issue since that would stop users from essentially replacing said behavior, and that's not what I want the library to do, since I emphasize heavily on customizability.

I was thinking for a while about creating interfaces or abstract classes for the base behaviors, so that:

  1. Other parts of the library use that instead of the implementation directly
  2. Give users some default or helpful methods so that they don't really have to copy/paste the whole class from the library and change what they want

I'll probably start working on that soon, let me know what you think!

SergeyIlyin commented 4 months ago

I'm using an external service to create connections. It would be interesting to customize existing behavior.

vbalestone commented 1 month ago

@zHaytam thanks for this great library.

I have created some derived behaviors and, to not break so much your implementation I changed the GetBehavior function and, if the asked one not found, check if has a derived one.