0
votes

In Blazor, the EventCallback struct is a bit different from standard events, being single-cast instead of multi-cast, etc. For that reason, I don't think this is a duplicate.

I am trying to create a set of components that all have the same [parameter] EventCallback, and it seems reasonable to use an interface to enforce that... So far this doesn't seem to be possible.

Things I've tried:

Treating this like EventHandler

public interface IResolver
{
    event EventCallback Done;
}

This raises the error: Event must be a delegate type.

...And other variations on trying to make this work like an EventHandler.

Trying to use IEventCallback

EventCallback implements IEventCallback, so this seemd promising. However, it is an internal interface, and is not available to me.

Using a base class instead

This seems like it might work, but I am curious why I can't do this with an interface.

Also found:

  • Event must be of delegate type?
  • Various articles about implementing event-like constructs using interfaces, or help with EventHandler, which is not the same thing.

None of that seems to be related to this problem.

1
I assume you don't need the multicast aspect, so why not declare the EventCallback as an ordinary property on the interface? - Kirk Woll
Have you tried declaring it like public EventCallback MyCallBack { get; set; } in your interface? - MrC aka Shaun Curtis
@ShaunCurtis No, I didn't try that because I don't normally declare EventCallbacks as properties - it does seem to work as far as the interface goes, though! I would need to test more to see if everything works in the implementation. You feel like it will? I am not sure how these are implemented, but I do see a lot of notes that they are "special". - Brian MacKay
@BrianMacKay. Yes it works, see my answer below. It has to be declared as a property to work with the component builder "factory" and the Renderer. Note the added [Parameter] in the implementation class. - MrC aka Shaun Curtis
NP. Get back if you need to. - MrC aka Shaun Curtis

1 Answers

1
votes

Declare the Callback using the correct syntax and it will work.

Here's my test code.

using Microsoft.AspNetCore.Components;

namespace TestBlazorServer
{
    public interface ICallbackComponent
    {
        public EventCallback MyCallBack { get; set; }
    }
}
// TestCallback.razor
@namespace  TestBlazorServer

@implements ICallbackComponent
<h3>TestCallback</h3>

<button class="btn btn-danger" @onclick="() => this.CallCallback()">Test Me</button>

We now declare MyCallBack with [Parameter].

using Microsoft.AspNetCore.Components;

namespace TestBlazorServer
{
    public partial class TestCallback : ComponentBase, ICallbackComponent
    {
        [Parameter] public EventCallback MyCallBack { get; set; }

        private void CallCallback()
        {
            MyCallBack.InvokeAsync();
        }
    }
}

And we can declare multiple instances in index.razor.

// index.razor
<TestCallback MyCallBack="Callback1"></TestCallback>

<TestCallback MyCallBack="Callback2"></TestCallback>

<TestCallback MyCallBack="Callback3"></TestCallback>

<div>Test:@called</div>

@code {
    private string called;

    private void Callback1()
        => called = "First";

    private void Callback2()
        => called = "Second";

    private void Callback3()
        => called = "Third";