Main Site Documentation

A generic method for handleing button events


#1

In a previous thread concerning some inconsistencies in the NETMF docs Foekie and I went off on a tangent about good ways to handle button input events when the Button classes built into WPF are not available or are not what you need.

brett3 suggested starting a new thread to make the information easier to find in the future, which is a really good idea.

My initial response is quoted below and I’ll start my new ramblings in the next post.

[quote]
Generating the event is not too difficult. I set up an ExtendedTimer that calls this code every 100ms

          currentKey = FEZ_Shields.KeypadLCD.GetKey();
            if (currentKey != FEZ_Shields.KeypadLCD.Keys.None
                & lastKey == FEZ_Shields.KeypadLCD.Keys.None)
            {
                lastKey = currentKey;
            }
            else if (currentKey == FEZ_Shields.KeypadLCD.Keys.None
                & lastKey != FEZ_Shields.KeypadLCD.Keys.None)
            {
                keyArg.key = lastKey;
                lastKey = FEZ_Shields.KeypadLCD.Keys.None;
                OnKeyEvent(keyArg);
            }

Basically this looks to see if the state of FEZ_Shields.KeypadLCD.GetKey() has changed from returning ‘None’ to returning any key pressed, this signifies a key down and we save the ID of the key that is pressed. If the state of FEZ_Shields.KeypadLCD.GetKey() has changed from a key being down to ‘None’ then we know the key was released and we fire off the OnKeyEvent. Since the update rate of the Extended timer is a relatively slow 100ms it makes a pretty good debounce (glitch) filter for the keys as well.

This set up is nice and simple but only gives you a key up event. If you wanted to emit key down events and key held events it would have to be more elaborate (and the timer would need to be a little faster.)

If you are using GPIO pins that are interrupt capable then the job is much easier as you you have events already generated for both edges of the signal and a built in glitch filter.

What is going through my mind now is a better/smarter way to handle the events once you have them. You don’t always want to do the same thing when a given event fires and we don’t have all the goodies of a larger frame work to help us out.[/quote]


#2

Ramblings part 1:

So what I’m after is a generic and easy to use method of making use of button events. What is a button event you ask? Well, good question. Let’s say you have a small push button wired up to an interrupt capable pin.You could use an InterruptPort to configure the pin as an input and get glitch filtering and an event fired for each edge of the signal transition for free.

InterruptPort button1 = new InterruptPort(pin#, glitch filter, resistor mode, interrupt type);

If your using some sort of ‘non-standard’ buttons, like the LCD Shield buttons, which use an A2D input with a different resistance value in series with each button, then you have to do something like I described in the first post to get events fired for switch state changes.

[italic]So, a ‘Button Event’ is an event that is fired to let us know that some switch we have hooked up to our FEZ for user input has changed states (been pressed and/or released).[/italic]

A graphical operating system makes use of event routing to capture an event where it was generated (say by clicking a button on a form) and then sending that event up and/or down the correct hierarchy to all the event handlers that have signed up to receive that type of event.

Even though there may be many event handlers that have signed up to receive a particular event, let’s say a key stroke, generally the application window that is in focus will have first dibs as handling the event and can mark it handled so it is not forwarded on down the line to other event handlers. Why do this. Well if your typing your password in to log into the TinyCLR forum you don’t want the same text to also appear in the open Word document or chat window you have going in the back ground.

So the OS gives us not only events and ways to sign up to make use of them but it also takes care of deciding the proper order of which event handlers get to see the event and in which order.


#3

Ramblings part 2:

So if we have some simple gadget built with a FEZ Mini or Domino why would we care about all that jazz? I mean we don’t have any windows or multiple applications running at the same time!

In my case I’m building a gadget with a FEZ Domino and an LCD Shield. I have some fancy code for the LCD shield that will let me scroll the text around, but I may not want to be able to do that all the time. There are also places in the code where I want to pause until the user hits a certain key. So how can I make sure that the bits of code that handle the scrolling goodness get the events only when I want to enable scrolling? How do I pause the program until a certain key is pressed and not let any scrolling happen? And more importantly how can I do all of this is code that is easy to reuse in other programs I might want to write?

What do I want?

[ulist]A sort of simple routed event system that will allow Button events to be sent out to a list of subscribed handlers in a certain order.

The subscribed handlers should have the ability to mark the event ‘handled’ and choose not to pass it on to any event handler further down the list.

It needs to be simple, light weight code that can be reused for multiple applications. I only want to write this once but I want to be able to use the same code in many different programs.[/ulist]


#4

Ramblings part 3 - The idea

After thinking about this for a while this is what seems like a good start. First instead of adding our event handlers to the event generator directly lets add them to an ArrayList that keeps track of all the handlers that we want to sign up for a certain event.


     // typical way to add an event handler
            liveText.ButtonEventHandlerList.Add(new KeyEventHandler(handled, buttonPressed));

     // new way
            //liveText.KeyEvent += new ButtonEventHandler(buttonPressed);

This requires that the same class that generates the event register a ‘generic’ event handler (a redneck event router) , using the regular method as shown first above. This event router looks at the ArrayList of event handlers signed up for this event and starts calling them from the bottom of the list to the top. If a handler returns true it is telling the router that the event has been handled and to discontinue passing the event on up the line.

How could one make use of this? Let’s say you have an event handler all set up to handling scrolling a LCD display. Register this as the very first handler in our ArrayList. Now let’s say we get to a place in our program where we want to pause until the user hits the Select button. We can call a new function that just waits for what ever button we tell it to wait for.

bool waitForButton(Button buttonWeWant, int TimeToWait);

Our handy waitForButton method will adds itself to the botton of our ButtonEventHandlerList. Since it is at the bottom it will be called first the next time a button event is fired.

If the next button happens to be the Select button then waitForButton will say ‘Great!’ mark the event handled and then remove itself from the ButtonEventHandlerList.

If the next button happened to be anything but Select then our waitForButton method can say, ‘That is not what I was waiting for.’ and mark the event as not handled and the event will then be forwarded onto the next handler up the list.

Does this all make any sense? I don’t know. Will it even work? Again, I don’t know. I took the week off of my day job so I’ll have some free time to try it out using my LCDLIveText class as a test platform.

Any input is welcome.


#5

I could not sleep so I tried it out. The Button event router using an ArrayList works great. I found that implementing a ‘WaitForKey’ type function requires two parts. The first is the actual ‘WaitForKey’ method that is called. Its job is to set things up (cleaer some flags, set some variables) and then add the second part a ButtonEvent handler (a listener) to the ArrayList when ‘WaitForKey’ is called. After settign thigns up the ‘WaitForKey’ method just sets in a While loop (with a Thread.Sleep for good measure) until it times out or the button is seen.

The ‘listener’, being at the bottom of the ArrayList, gets called first, if it sees the Button event was the key being waited for it marks it handled and sets a flag to say the button being waited for was seen.

I’ll get the code cleaned up after I get some sleep and update the project on CodePlex.


#6

It is always a good practice to create an standard interface to something and then the underlaying code can change to fit the standard interface. This way your higher level code wouldn’t need to change if you change the hardware. Now, on embedded system this may not always be the right option. On small systems, you want to use less resources and customize the code to get higher performance.There are still cases where layering code will not use much resources so layering is still desirable.
I am not talking about buttons, just general thoughts.


#7

I agree Gus. On low horsepower controllers you have to balance the memory footprint and performance with good coding style. The neat thing about how this worked out is that it is a very lightweight solution.

I just uploaded my changes to the CodePlex repository but here is the basic idea. An event handler registers itself like this:

liveText.ButtonEventHandlerList.Add(new ButtonEventHandler(keyPressed));

The ‘ButtonEventRouter’ below is what actually receives the ‘raw’ button events and it forwards them to each handler that is in the array list, working from the bottom of the list up.

    
        /// <summary>
        /// Button Pressed Event router
        /// </summary>
        /// <param name="sender">Object firing event</param>
        /// <param name="e">Button event</param>
        /// <returns>Bool, true == handled</returns>
        /// Iterates through list of registered Button event handlers from
        /// the bottom up. If a handler returns true then the event is 
        /// considedered handled and is not sent on up the list. 
        /// This provides a very simple routing scheme.
        private bool ButtonEventRouter(object sender, ButtonEventArgs e)
        {
            int count = ButtonEventHandlerList.Count;
            bool handled = false;
            //Debug.Print("Handlers = " + count.ToString());
            for (int i = count; i > 0 & handled == false; i--)
            {
                handled = (ButtonEventHandlerList[i - 1] as ButtonEventHandler)(sender, e);
            }
            
            return true;
        }

If a handler returns ‘True’ then the event is considered handled and is not forwarded to any other handlers up the list. This allows you to have a default behavior that can be superseded as you desire.

Implementing the ‘WaitForBUtton’ was a bit trickier. First you call the actual wait method:

       /// <summary>
        /// Pause execution of a thread until a given button is pressed
        /// or a timeout occures.
        /// </summary>
        /// <param name="button">The button to wait for</param>
        /// <param name="time">The amount of time to wait, in update scans (100ms)</param>
        /// <returns>True if button pressed, flase if timeout</returns>
        public bool WaitForButton(FEZ_Shields.KeypadLCD.Keys button, int time)
        {
            // reset variables and add listener to handlerm list
            WaitButtonFlag = false;
            WaitTimeOutFlag = false;
            WaitButton = button;
            WaitTimeout = time;
            ButtonEventHandlerList.Add(new ButtonEventHandler(WaitForListener));

            while (!WaitButtonFlag & !WaitTimeOutFlag)
            {
                Thread.Sleep(100);
            }

            // assume no other handler has been added while we were waiting
            ButtonEventHandlerList.RemoveAt(ButtonEventHandlerList.Count - 1);
            return WaitButtonFlag ? true : false;
        }

It sets up a listener that gets added to our ArrayList and sets a flag if the button is seen.

        /// <summary>
        /// A ButtonEvent handler that we add to tail end of
        /// ButtonEventHandlerList to listen for the a given button event.
        /// </summary>
        /// <param name="sender">Object firing event</param>
        /// <param name="e">Button event</param>
        /// <returns>True == handled</returns>
        private bool WaitForListener(object sender, ButtonEventArgs e)
        {
            if (e.button == WaitButton)
            {
                WaitButtonFlag = true;
                return true;
            }

            return false;
        }

All-in-not a lot of overhead and it seems to provide the functionality I was after,