Search code examples
c#eventsevent-handlingbluetooth-lowenergy

Passing an object to an event with given signature


My goal is to pass an object to an event. The event has a given signature I can't change (so I can't define my own EventArgs). Specifically, I am talking about a passkey that is required for a Bluetooth pairing event, but the question is a general one. I subscribe to the event before starting the pairing procedure:

public async Task MyPairAsync(Device device)
{
    var bluetoothLEDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(device.Id);
    DeviceInformationCustomPairing customPairing = bluetoothLEDevice.DeviceInformation.Pairing.Custom;
    customPairing.PairingRequested += CustomPairing_PairingRequested;
    var result = await customPairing.PairAsync(DevicePairingKinds.ProvidePin);
    // Do more stuff (evaluate result etc.)...
}

The class "Device" contains the Id of the BLE device and the passkey:

public class Device
{
    public ulong Id { get; }
    public string Passkey { get; private set; }

    public Device(ulong id, string passkey)
    {
        Id = id;
        Passkey = passkey;
    }
}

The signature of the PairingRequested event is fixed, so CustomPairing_PairingRequested needs to follow that signature:

private void CustomPairing_PairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args)
{
    args.Accept(device.Passkey);  // This doesn't work, since device is unknown in this context.
}

The class DevicePairingRequestedEventArgs is sealed, so I can't extend a class from it that could contain a Device instance.

How can I pass the Device object (and with it, the passkey information) to the CustomPairing_PairingRequested event? I could of course define a global Device variable, set it to the current Device object in MyPairAsync() and read it in the event, but that seems very ugly. Is there a better option to do this?


Solution

  • What about wrapping all the publishing-subscribeing code along with the device instance?..

    Replace

    customPairing.PairingRequested += CustomPairing_PairingRequested;
    var result = await customPairing.PairAsync(DevicePairingKinds.ProvidePin);
    // Do more stuff (evaluate result etc.)...
    

    with

    var prWrapper = new PairingRequestWrapper(device);
    var result = await prWrapper.PairAsync(customPairing, () => { 
        // doing some stuff after `args.Accept(device.Passkey)`
    });
    // Do more stuff (evaluate result etc.)...
    

    Where PairingRequestWrapper is

    public class PairingRequestWrapper{
        
        private Device device;
        
        public PairingRequestWrapper(Device d){
            device = d;
        }
    
        private Action callbackAction;
    
        // I mean, the result type
        public async Task<Result> PairAsync(DeviceInformationCustomPairing customPairing, Action callbackAction = null){
            this.callbackAction = callbackAction;
            customPairing.PairingRequested += CustomPairing_PairingRequested;
            return await customPairing.PairAsync(DevicePairingKinds.ProvidePin);
        }
        
        // no need to `CustomPairing_PairingRequested` in the original code.
        private void CustomPairing_PairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args)
        {
            args.Accept(device.Passkey);
            callbackAction?.Invoke();
        }
    }