MS.COM Operations Tools Team WebLog

Hey - What does this button do?

Event Debouncing

I have recently encountered a situation where my code was receiving objects sent, via a scoping event, from code on another thread.  These "scope" objects require my code to change state to reflect the object received.  This resulted in my code receiving scoping events at inopportune times (such as in the middle of working on the previous scope change).  I needed a way to "de-bounce" the scope changes in order to ignore them while I was updating, and also check when I was finished updating to see what scope changes had occurred while I was processing.  Here is a simple solution that I put together to help solve the problem.  The first code block shows my usage of the class, and the second is the class itself.  I hope you find it interesting (and maybe useful).

    // Code Block #1: Usage
    private ScopeTracker _scopeTracker = new ScopeTracker( ScopeTracker.TrackingStyle.DiscardAllButLast );

    public override void OnScopeChanged(object scope)
    {
        _scopeTracker.TrackChange( scope );
        If( !updatingState )
        {
            if( _scopeTracker.IsNewScopeChange() )
            {
                this.UpdateState( _scopeTracker.ConsumeNextScopeChange() );
             }
        }
    }

    private void UpdateState ( object o )
    {
        if( updatingState )
        {
            return;
        }

        updatingState = true;

        // do work on another thread (possibly)…

        updatingState = false;

        if( _scopeTracker.IsNewScopeChange() )
        {
            this.UpdateState( _scopeTracker.ConsumeNextScopeChange() );
        }
    }


    // Code Block #2: Class definition.
    public class ScopeTracker
    {
        public enum TrackingStyle { QueueAll, DiscardAllButLast };
        public object LastScopeChange
        {
            get
            {
                return _lastScopeChange;
            }
        }

        private TrackingStyle _trackingStyle;
        private Queue _trackedScopeChanges = new Queue();
        private object _lastScopeChange = null;

        public ScopeTracker( TrackingStyle TrackingStyle )
        {
            _trackingStyle = TrackingStyle;
        }

        /// <summary>
        /// Returns true if the change is tracked, else returns false.
        /// </summary>
        /// <param name="ScopeObject"></param>
        /// <returns></returns>
        public bool TrackChange( object ScopeObject )
        {
            bool bTracked = false;

            // If we are only tracking the last scope change, then throw away any previous
            //    scope changes we are tracking.
            if( _trackingStyle == TrackingStyle.DiscardAllButLast )
            {
                _trackedScopeChanges.Clear();
            }

            // Only track changes if they are changes.
            if( 1 > _trackedScopeChanges.Count )
            {
                // Since we don't have any changes in the queue check to see if this new
                //    one is different than our last one.
                if( _lastScopeChange != ScopeObject )
                {
                    _trackedScopeChanges.Enqueue( ScopeObject );
                    bTracked = true;
                }
            }
            else
            {
                // We have changes in the queue, so see if this new one is the same as the last
                //     change we put in the queue.
                if( ScopeObject != _trackedScopeChanges.Peek() )
                {
                    _trackedScopeChanges.Enqueue( ScopeObject );
                    bTracked = true;
                }
            }

            return bTracked;
        }

        public object ConsumeNextScopeChange()
        {
            if( 1 > _trackedScopeChanges.Count )
            {
                return null;
            }

            _lastScopeChange = _trackedScopeChanges.Dequeue();
            return _lastScopeChange;
        }

        public bool IsNewScopeChange()
        {
            if( 1 > _trackedScopeChanges.Count )
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        public object SyncToValidScope()
        {
            if( this.IsNewScopeChange() )
            {
                return this.ConsumeNextScopeChange();
            }
            else
            {
                if( null != _lastScopeChange )
                {
                    return _lastScopeChange;
                }
                else
                {
                    return null;
                }
            }
        }
    }

Published Monday, November 08, 2004 1:33 PM by mscomts

Comments

No Comments
New Comments to this post are disabled

This Blog

Syndication

Tags

No tags have been created or used yet.

News

All opinions posted here are those of the author(s) and are in no way intended to represent the opinions of our employer. This is provided "AS IS" with no warranties, and confers no rights. Use of included code samples are subject to the terms specified in the Terms of Use.

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker