shaigem / BlazorPanzoom

Blazor wrapper for timmywil's panzoom library that helps make zooming and panning of Blazor components and elements easier
https://shaigem.github.io/BlazorPanzoom/
MIT License
44 stars 12 forks source link

Exception thrown in Blazor Server .NET 8 when directly accessing a page containing <Panzoom> #25

Open fterrani opened 6 months ago

fterrani commented 6 months ago

Greetings,

I would like to use BlazorPanzoom in a Blazor Server .NET 8 project. The library seem to work properly most of the time, however I think I found a case where it doesn't.

When accessing directly a page that contains a <Panzoom> component, a NullReferenceException is thrown from the Panzoom.cs file:

public async ValueTask DisposeAsync()
{
    GC.SuppressFinalize(this);
    await _underlyingPanzoomInterop.DisposeAsync(); // <<< the line throwing the exception
}

For some reason, the _underlyingPanzoomInterop variable seems to be null... which means the component is, for some reason, disposed before it is even rendered at all? If so, I have no idea why. Maybe the fix is as simple as this?

    await _underlyingPanzoomInterop?.DisposeAsync(); 

But I don't know for certain and might be unaware of some indirect implications that you probably know about. You will find a very basic Blazor Server .NET 8 project (with global rendering config) in attachment of this issue that highlights the problem: BlazorPanzoomDotNet8.zip

Also, here are some useful links related to exception handling when disposing in .NET:

MaxDeVos commented 5 months ago

I have this issue as well, and it is blocking me from upgrading to .NET 8. Due to the unusual nature of the application I am using it for, I have spent a great deal of time sleuthing around inside of BlazorPanzoom (as well as the original panzoom), so I am going to take a look at it and see if I can't find the source of the issue. If I do, I'll submit a pull request with the fix.

MaxDeVos commented 5 months ago

Fortunately, the general cause of this problem is very clear: .NET 8's rendering modes. We know this because this behavior doesn't occur in WebAssembly environments, such as the included repository's included demos.

I created two absolutely minimal projects from a stripped down BlazorWebApp template, and set up one to render via InteractiveServer and the other to render via client-side WASM, and the issue only occurred in the server-rendered app. Here's the code from Home.razor in the InteractiveServer project which triggered the issue:

@page "/"
<PageTitle>Home</PageTitle>
@rendermode InteractiveServer

<div class="panzoom-parent">
    <Panzoom @ref="_panzoom">
        <div @ref="@context.ElementReference" class="panzoom">
            <img style="width: 400px; height: 400px" src="awesome_tiger.svg" alt="image"/>
        </div>
    </Panzoom>
</div>

@code 
{
    private Panzoom? _panzoom;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (_panzoom != null)
        {
            await _panzoom.ZoomAsync(1);
        }
    }
}

Specifically, the _underlyingPanzoomInterop object in Components/Panzoom.cs is null, and throws an exception here:

public async ValueTask DisposeAsync()
{
    GC.SuppressFinalize(this);
    if (_underlyingPanzoomInterop != null)
    {
        await _underlyingPanzoomInterop.DisposeAsync();
    }
}
PmE8HW0KRfqa commented 5 months ago

I think this related to issue #12 and unrelated to dotnet 8. The bug has been patched, unfortunately a new package has not been released.

Edit: It was able to easily clone the source from GH, build the project, and reference BlazorPanZoom from my blazor project (without the nuget reference). It works without throwing the error!