tingfungc
tingfungc

Reputation: 115

Event handler is null

I am currently playing with the LeapSDK. I want to make a self-defined class that encapsulates the LeapSDK so that I can simply import it into any apps I would made in the future.

The LeapSDK provide an interface for developers to add a listener to the controller. And we can define what to do inside the listener.

In fact my problem is not related to the LeapSDK, but I would like to tell what am I doing so that you would understand my needs. AsI am not sure if I am working in a good approach, you may have some suggestion like doing the same thing in a clever way. Please feel free to share.

So, here is a sample describing how it works(example provided by the SDK):

class Sample
{
    public static void Main ()
    {
        SampleListener listener = new SampleListener ();
        Controller controller = new Controller ();

        // Have the sample listener receive events from the controller
        controller.AddListener (listener);

        // Keep this process running until Enter is pressed
        Console.WriteLine ("Press Enter to quit...");
        Console.ReadLine ();

        // Remove the sample listener when done
        controller.RemoveListener (listener);
        controller.Dispose ();
    }
}
class SampleListener : Listener
{
    private Object thisLock = new Object ();

    private void SafeWriteLine (String line)
    {
        lock (thisLock) {
            Console.WriteLine (line);
        }
    }

    public override void OnInit (Controller controller)
    {
        SafeWriteLine ("Initialized");
    }

    public override void OnConnect (Controller controller)
    {
        SafeWriteLine ("Connected");
        controller.EnableGesture (Gesture.GestureType.TYPECIRCLE);
        controller.EnableGesture (Gesture.GestureType.TYPEKEYTAP);
        controller.EnableGesture (Gesture.GestureType.TYPESCREENTAP);
        controller.EnableGesture (Gesture.GestureType.TYPESWIPE);
    }

    public override void OnDisconnect (Controller controller)
    {
        //Note: not dispatched when running in a debugger.
        SafeWriteLine ("Disconnected");
    }

    public override void OnExit (Controller controller)
    {
        SafeWriteLine ("Exited");
    }
}

I have tested the above code, it works very well.

Now lets move to my problem. I create a class named LeapEventInterpretator, which has a event listener for the reaction with the events generated by the device just like the above approach. And I try to pass a event handler to the listener, so that the listener could call it for generating events to the main app thread.

Here is my codes:

    class LeapEventInterpretator
{
    private Controller controller;
    private DeviceEventListener deviceEventListener;

    public delegate void LeapEventHandler(object sender, Gesture args);
    public event LeapEventHandler GestureDetected;

    public LeapEventInterpretator()
    {
        Connect();    
    }
    private void Connect()
    {
        controller = new Controller();

        deviceEventListener = new DeviceEventListener(GestureDetected);
        controller.AddListener(deviceEventListener);

    }

    public enum Gesture { SwipeLeft, SwipeRight };
}

class DeviceEventListener : Listener
{
    LeapEventInterpretator.LeapEventHandler handler;
    public DeviceEventListener(LeapEventInterpretator.LeapEventHandler h)
    {
        this.handler = h;
    }
    public override void OnConnect(Controller arg0)
    {
        if (this.handler != null)
            handler(this, LeapEventInterpretator.Gesture.SwipeLeft);
    }
}

When OnConnect is raised, this.handler is null and then I can not fire my self-defined event to the app class. Any ideas?

Again, this may be a silly approach to solve the problem. Even for myself I don't feel good about passing a event handler to another class, but I just can't think of another way to do the job. So please feel free to comment.

Thank you very much for your help.

Upvotes: 0

Views: 873

Answers (1)

Mike Zboray
Mike Zboray

Reputation: 40838

You don't want pass the event as a parameter to the listener in this case. You want to pass a callback to a method (i.e. a delegate) that will raise the event.

In your sample, the line deviceEventListener = new DeviceEventListener(GestureDetected); is passing an uninitialized delegate (which is null) to the listener. This is because you are not passing the event as parameter just the value of the event's automatically generated backing field. An event is nothing more than a pair of methods, add and remove, that external code can use to say "subscribe me" or "unsubscribe me".

You want to do something more like this:

class LeapEventInterpretator
{
    private Controller controller;
    private DeviceEventListener deviceEventListener;

    public delegate void LeapEventHandler(object sender, Gesture args);
    public event LeapEventHandler GestureDetected;

    public LeapEventInterpretator()
    {
        Connect();    
    }
    private void Connect()
    {
        controller = new Controller();

        // pass a callback that will raise the event.
        deviceEventListener = new DeviceEventListener(RaiseGestureDetected);
        controller.AddListener(deviceEventListener);

    }

    // a method to raise the event
    private void RaiseGestureDetected(Gesture gesture)
    {
         var handler = GestureDetected;
         if (handler != null)
             handler(this, gesture);
    }

    public enum Gesture { SwipeLeft, SwipeRight };
}

class DeviceEventListener : Listener
{
    Action<LeapEventInterpretator.Gesture> handler;
    public DeviceEventListener(Action<LeapEventInterpretator.Gesture> h)
    {
        this.handler = h;
    }
    public override void OnConnect(Controller arg0)
    {
        if (this.handler != null)
            handler(LeapEventInterpretator.Gesture.SwipeLeft);
    }
}

I'm not sure that the OnConnect method makes sense to me, but now you should get an event fired when OnConnect is called.

Of course then you've got to sign something up for the event:

var interpreter = new LeapEventInterpretator();
intpreter.GestureDetected += (o, g) => { Console.WriteLine("Gesture detected: {0}", g); };
// do something to make the OnConnected get called.

Upvotes: 1

Related Questions