An event driven Phone Keypad library. Includes KeyDown, KeyUp and KeyHoldling events to which event handlers can be attached in apps that use this library. Also includes a mechanism to fire those events from keypad scanning software. No hardware specific code though. These events mirror those events in the previous blog for the Arduino Keypad Event Scanning Sketch.  The next blog hooks the two up.

 

This blog demonstrates exemplary code for for implementing event table mechanisms for custom events, particularly if the events are driven by custom hardware.

 

Links

 

This project, a Universal Windows Platform library ( Windows 10) provides a generic interface and implementation for three keypad events for a phone matrix keypad:

  • KeyDown
  • KeyUp
  • KeyHolding

These are defined as in the IKeypad interface:

namespace KeypadUWPLib
{
    public interface IKeypad
    {
         event EventHandler<KeypadEventArgs> KeyDown;
         event EventHandler<KeypadEventArgs> KeyUp;
         event EventHandler<KeypadEventArgs> KeyHolding;
    }
}

The Keypad Interface

 

Each event then has a table of event handlers (which may be null) that get called in turn when each event occurs. Higher level code would add handlers to the event tables (see later code).

When a key is pressed, KeyDown event fires. If it is held longer than a short period, the KeyHolding event is fired (once). It does not continually fire though. (Higher level code could implement that with a timer.)  When the key is released the KeyUp event fires.

 

The events require a tailored event args class KeypadEeventArgs. This is generated on an event and passed to the handler/s along with eth event source object. This is much the same as a XAML Command Button interface.

namespace KeypadUWPLib
{
    public enum KeypadActions { Down, Up, Holding }

    public  class KeypadEventArgs 
    {
        /// Validation list
List<char> validActions = new List<char>() { '+', '-', '@' }; public static List<char> ValidKeys = new List<char>() { '0', '1', '2', '3', '4', '5', '6', '7', '8','9', '#', '*' }; public KeypadEventArgs(KeypadActions action, char key) { if (!ValidKeys.Contains(key)) { } else { Key = key; Action = action; } } public char Key { get; internal set; } public KeypadActions Action { get; internal set; } }

The KeypadEventArgs  class.

 

The two properties, the key that is pressed and the event (as an enum) are passed to the event handler. Some validation of the key character passed to the constructor is performed. An alternative constructor is also implemented that takes two characters and validates the first as a validAction character:

        public KeypadEventArgs(char action, char key)
        {
            if (!ValidKeys.Contains(key))
            {
            }else
            {
                Key = key;
                if (!validActions.Contains(action))
                {
                }
                else
                {
                    switch (action)
                    {
                        case '+':
                            Action = KeypadActions.Down;
                            break;
                        case '-':
                            Action = KeypadActions.Up;
                            break;
                        case '@':
                            Action = KeypadActions.Holding;
                            break;
                    }
                }
            }
        }

The alternative event args constructor.

 

As the events are to be received as strings from the keypad hardware over the Bluetooth serial stream (next blog) it makes sense to encapsulate the conversion of the event character (+,- or @) into the event arg class.

 

The keypad class then implements the a table for each event. The KeyDown event mechanism is shown:

namespace KeypadUWPLib
{
    public class Keypad : IKeypad
    {
        private EventRegistrationTokenTable<EventHandler<KeypadEventArgs>>
                m_KeyDownTokenTable = null;

        public event EventHandler<KeypadEventArgs> KeyDown
        {
            add
            {
                /*return*/ EventRegistrationTokenTable<EventHandler<KeypadEventArgs>>
                    .GetOrCreateEventRegistrationTokenTable(ref m_KeyDownTokenTable)
                    .AddEventHandler(value);
            }
            remove
            {
                EventRegistrationTokenTable<EventHandler<KeypadEventArgs>>
                    .GetOrCreateEventRegistrationTokenTable(ref m_KeyDownTokenTable)
                    .RemoveEventHandler(value);
            }
        }
public event EventHandler<KeypadEventArgs> KeyUp {
. . . . .

}
public event EventHandler<KeypadEventArgs> KeyHolding {
. . . . .
}
}

The Keypad  class.

For simplicity, function are prototypes only shown for KeyUp and KeyHolding. They are embellished in the same manner as the Down mechanism.

Note that each event MUST have an Add and Remove option (compiler error otherwise.
Also, in Microsoft sample code (see Related MSDN Documentation above)  the Add returns the table but the compiler rejects that. The code works without it.

 

Raising an Event

When an event is received from the underlying hardware, a mechanism is needed to fire that event, whilst C++ and VB have explicit calls for raising or setting an event, C# does not. The handlers in the event table are actually just delegates, so its its just a matter to action this delegates.

 

The event table’s InvocationList (list of handlers that have been added) has an Invoke method that invokes all attached handlers to the event:

m_KeyDownTokenTable.InvocationList.Invoke(sender, e);

The RaiseEvent( ) method:

        public void RaiseEvent(object sender,   KeypadEventArgs e)
        {
            // Action relevant event
            switch (e.Action)
            {
                case KeypadActions.Down:
                    if (m_KeyDownTokenTable == null)
                        return;
                    m_KeyDownTokenTable.InvocationList.Invoke(sender, e);
                    break;
                case KeypadActions.Up:
                   ....
                    break;
                case KeypadActions.Holding:
                    ....
                    break;
            }
        }

The précised Keypad RaiseEvent( ) method.

An alternative method consumes a raw keypad string (as sent form the Arduino Keypad Sketch).:

        public void RaiseEvent(object sender, string keyString)
        {
            //Validate keyString
            if (keyString.Length != 2)
                return;

            KeypadEventArgs e = null;
            char action = keyString[0];
            char key = keyString[1];
            e = new KeypadEventArgs(action, key); //Note: (char, char) constructor

            RaiseEvent(sender, e);
        }

The alternative RaiseEvent( ) method. Note: (char, char) constructor  for event args.

 

The Keypad Class

image

The Keypad and related classes.

 

Using the Keypad Event Class.

After adding the library to a UWP project (as a reference) the steps for using are summarised thus:

// Declare the class
public KeypadUWPLib.Keypad Keypad { get; internal set; }

// Instantiate it
            Keypad = new KeypadUWPLib.Keypad();

// Provide and hookup handlers
            Keypad.KeyDown += Keypad_KeyDown;
            Keypad.KeyUp += Keypad_KeyUp;
            Keypad.KeyHolding += Keypad_KeyHolding;

// The KeyDown handler
        private void Keypad_KeyDown(object sender, KeypadUWPLib.KeypadEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("down " + e.Key + " " + e.Action.ToString());
        }

 

Comments

Yes, the code could be simplified a by having a common event table for all three events given that the event args actually pass the event. I wanted the events separate as the KeyDown event is the only event I want in the end game of this activity. Reminder that the end game is a selfie stick for a phone.

You can of course use a common handler method for all three events as they have the same signature.

 

The KeyHolding event could launch a timer that periodically calls the delegates in its event table until KeyUp event occurs.

 

This event mechanism is not specific to an underlying Bluetooth Serial hardware and thus could be used by any keypad hardware, supplying events over a stream such as RS232 or TCPIP sockets or a client-server mechanism.