Blazor-Diagrams / Blazor.Diagrams

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

Trying to prevent a user to remove a node using an async pattern #337

Closed PedroAbreu1976 closed 9 months ago

PedroAbreu1976 commented 10 months ago

Hi :)

In a webassembly application, I have this code that sets up the diagram options in a way that the user is asked to confirm the removing of a node:

private DiagramOptions GetDiagramOptions()
{
    return new DiagramOptions
    {
        DeleteKey = "Delete", // What key deletes the selected nodes/links
        Constraints = new DiagramConstraintsOptions
        {
            ShouldDeleteNode = node => OnNodeRemoving(node).Result
        }
    };
}

private async Task<bool> OnNodeRemoving(NodeModel node)
{
    return await _matDialogService.ConfirmAsync($"Remove {node.Title} from the model?");
}

And this produces the following error: _blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Cannot wait on monitors on this runtime. System.PlatformNotSupportedException: Cannot wait on monitors on this runtime. at System.Threading.Monitor.ObjWait(Int32 millisecondsTimeout, Object obj) at System.Threading.Monitor.Wait(Object obj, Int32 millisecondsTimeout) at System.Threading.ManualResetEventSlim.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.SpinThenBlockingWait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.InternalWaitCore(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task1[[System.Boolean, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task1[[System.Boolean, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].get_Result() at WGK.CCUS.Web.Services.DiagramService.b12_1(NodeModel node) in C:\DevOps\PDAT\ACES\WGK.CCUS.Web\Services\DiagramService.cs:line 86 at Blazor.Diagrams.Core.Behaviors.DeleteSelectionBehavior.b1_0() at Blazor.Diagrams.Core.Diagram.Batch(Action action) at Blazor.Diagrams.Core.Behaviors.DeleteSelectionBehavior.DiagramKeyDown(KeyboardEventArgs e) at Blazor.Diagrams.Core.Diagram.OnKeyDown(KeyboardEventArgs e) at Blazor.Diagrams.Components.DiagramCanvas.OnKeyDown(KeyboardEventArgs e) at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task) at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

I there a way to overcome this problem?

Cheers :)

zHaytam commented 10 months ago

Hello, in the new version (3.0.0), you can use async functions in the constraints. Maybe you can give that a try?

PedroAbreu1976 commented 10 months ago

Oh... I just read that :) Meanwhile I was implementing a behaviour to handle that:

public class DeleteSelectionAsyncBehavior : Behavior
{
    private Action<KeyboardEventArgs> onKeyDown;

    public DeleteSelectionAsyncBehavior(
        Diagram diagram) : base(diagram)
    {
        onKeyDown = async (e) => await Diagram_KeyDown(e);
        Diagram.KeyDown += onKeyDown;
    }

    public override void Dispose()
    {
        Diagram.KeyDown -= onKeyDown;
        onKeyDown = null;
    }

    private async Task Diagram_KeyDown(KeyboardEventArgs e)
    {
        if (e.AltKey || e.CtrlKey || e.ShiftKey || e.Code != Diagram.Options.DeleteKey)
            return;

        Diagram.Batch(async () =>
        {
            foreach (var sm in Diagram.GetSelectedModels().ToList())
            {
                if (sm.Locked)
                    continue;

                if (sm is GroupModel group && await ShouldDeleteGroup(group))
                {
                    Diagram.RemoveGroup(group);
                }
                else if (sm is NodeModel node && await ShouldDeleteNode(node))
                {
                    Diagram.Nodes.Remove(node);
                }
                else if (sm is BaseLinkModel link && await ShouldDeleteLink(link))
                {
                    Diagram.Links.Remove(link);
                }
            }
        });
    }

    public Func<GroupModel, Task<bool>> ShouldDeleteGroup { get; set; } = async (group) => await Task.Run(() => true);

    public Func<NodeModel, Task<bool>> ShouldDeleteNode { get; set; } = async (node) => await Task.Run(() => true);

    public Func<BaseLinkModel, Task<bool>> ShouldDeleteLink { get; set; } = async (link) => await Task.Run(() => true);
}
PedroAbreu1976 commented 10 months ago

I have to say, that you have done an amazing job with this framework 👍

Is there a list of breaking changes and what's new in version 3? Also do you have a time line for the alpha release?