Search code examples
c#eventssystem.reactive

Convert event without EventArgs using Observable.FromEvent


I'm struggling with converting the following event to an IObservable:

public delegate void _dispSolutionEvents_OpenedEventHandler();
event _dispSolutionEvents_OpenedEventHandler Opened;

The event comes from a library so I can't change it. The overload of IObservable.FromEvent that should do it has the following signature:

public static IObservable<Unit> FromEvent
    ( Action<Action> addHandler
    , Action<Action> removeHandler
    )

So I tried converting the event like this:

var opened = Observable.FromEvent
    ( h => _SolutionEvents.Opened += h
    , h => _SolutionEvents.Opened -= h
    );

But the compiler doesn't like _SolutionEvents.Opened += h and _SolutionEvents.Opened += h because

Cannot implicitly convert type 'System.Action' to 'EnvDTE._dispSolutionEvents_OpenedEventHandler'.

I don't think that I can just say_SolutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(h) because then removal won't work because I have a different instance, right?

There is another overload of Observable.FromEvent with the following signature:

public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>
    ( Func<Action<TEventArgs>, TDelegate> conversion
    , Action<TDelegate> addHandler
    , Action<TDelegate> removeHandler
    )

This one allows to convert the action to an event handler, but it seems to only work with TEventArgs.

Is Rx missing an appropriate overload or am I missing something?


Solution

  • This turns out that it is very easy to use the FromEvent pattern.

    Just do this:

    var opened = Observable.FromEvent<_dispSolutionEvents_OpenedEventHandler, Unit>(
        h => () => h(Unit.Default),
        h => _SolutionEvents.Opened += h,
        h => _SolutionEvents.Opened -= h);
    

    I've tested the observable with this code:

    void Main()
    {
        var _SolutionEvents = new Foo();
    
        var opened = Observable.FromEvent<_dispSolutionEvents_OpenedEventHandler, Unit>(
            h => () => h(Unit.Default),
            h => _SolutionEvents.Opened += h,
            h => _SolutionEvents.Opened -= h);
    
        opened.Subscribe(x => Console.WriteLine("Opened"));
    
        _SolutionEvents.OnOpened();
    }
    
    public delegate void _dispSolutionEvents_OpenedEventHandler();
    
    public class Foo
    {
        public event _dispSolutionEvents_OpenedEventHandler Opened;
    
        public void OnOpened()
        {
            this.Opened?.Invoke();
        }
    }
    

    It produces the following expected output:

    Opened
    

    It's worth noting that there is no IObservable interface, but only an IObservable<T> so you must return something. The trick here is to convert delegate void _dispSolutionEvents_OpenedEventHandler() into an IObservable<Unit> to make it work and that's what the h => () => h(Unit.Default) does.