Search code examples
c#windows-phone-7accelerometer

Accelerometer troubles in WP7


Im trying to use a shaking motion to clear the content of an inkpresenter, however shaking is just crashing my application. Here is the code:

    public class ShakeDetect
{
    private Accelerometer _accelerometer = null;
    object SyncRoot = new object();
    private int _minimumShakes;
    ShakeRecord[] _shakeRecordList;
    private int _shakeRecordIndex = 0;
    private const double MinimumAccelerationMagnitude = 1.2;
    private const double MinimumAccelerationMagnitudeSquared = MinimumAccelerationMagnitude * MinimumAccelerationMagnitude;
    private static readonly TimeSpan MinimumShakeTime = TimeSpan.FromMilliseconds(500);

    public event EventHandler<EventArgs> ShakeEvent = null;


    protected void OnShakeEvent()
    {
        if (ShakeEvent != null)
        {
            ShakeEvent(this, new EventArgs());
        }
    }



    [Flags]
    public enum Direction
    {
        None = 0,
        North = 1,
        South = 2,
        East = 8,
        West = 4,
        NorthWest = North | West,
        SouthWest = South | West,
        SouthEast = South | East,
        NorthEast = North | East
    } ;

    public struct ShakeRecord
    {
        public Direction ShakeDirection;
        public DateTime EventTime;
    }


    public ShakeDetect()
        : this(2)
    {

    }
    public ShakeDetect(int minShakes)
    {
        _minimumShakes = minShakes;
        _shakeRecordList = new ShakeRecord[minShakes];
    }

    public void Start()
    {
        lock (SyncRoot)
        {
            if (_accelerometer == null)
            {
                _accelerometer = new Accelerometer();
                _accelerometer.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(_accelerometer_ReadingChanged);
                _accelerometer.Start();
            }
        }
    }

    public void Stop()
    {
        lock (SyncRoot)
        {
            if (_accelerometer != null)
            {
                _accelerometer.Stop();
                _accelerometer.ReadingChanged -= _accelerometer_ReadingChanged;
                _accelerometer = null;
            }
        }
    }

    Direction DegreesToDirection(double direction)
    {
        if ((direction >= 337.5) || (direction <= 22.5))
            return Direction.North;
        if ((direction <= 67.5))
            return Direction.NorthEast;
        if (direction <= 112.5)
            return Direction.East;
        if (direction <= 157.5)
            return Direction.SouthEast;
        if (direction <= 202.5)
            return Direction.South;
        if (direction <= 247.5)
            return Direction.SouthWest;
        if (direction <= 292.5)
            return Direction.West;
        return Direction.NorthWest;
    }

    void CheckForShakes()
    {
        int startIndex = (_shakeRecordIndex - 1);
        if (startIndex < 0) startIndex = _minimumShakes - 1;
        int endIndex = _shakeRecordIndex;

        if ((_shakeRecordList[endIndex].EventTime.Subtract(_shakeRecordList[startIndex].EventTime)) <= MinimumShakeTime)
        {
            OnShakeEvent();
        }
    }
    void _accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
    {

        if ((e.X * e.X + e.Y * e.Y) > MinimumAccelerationMagnitudeSquared)
        {

            double degrees = 180.0 * Math.Atan2(e.Y, e.X) / Math.PI;
            Direction direction = DegreesToDirection(degrees);


            if ((direction & _shakeRecordList[_shakeRecordIndex].ShakeDirection) != Direction.None)
                return;
            ShakeRecord record = new ShakeRecord();
            record.EventTime = DateTime.Now;
            record.ShakeDirection = direction;
            _shakeRecordIndex = (_shakeRecordIndex + 1) % _minimumShakes;
            _shakeRecordList[_shakeRecordIndex] = record;

            CheckForShakes();

        }
    }

}

And then in my app I use:

        private ShakeDetect _shakeDecetor;

    // Constructor
    public MainPage()
    {
        InitializeComponent();
        _shakeDecetor = new ShakeDetect();
        _shakeDecetor.ShakeEvent += new EventHandler<EventArgs>(_shakeDetect_ShakeEvent);
        _shakeDecetor.Start();

Finally when the shake is detected:

void _shakeDetect_ShakeEvent(object sender, EventArgs e)
    {
        CanvasIP.Strokes.Clear();
        CanvasIP.Background = new SolidColorBrush(Colors.Black);
    }

I'm guessing that the problem is somewhere in my ShakeDetect class, or in the ShakeEvent.

Ok So from Hans' advice my code should be:

void _shakeDetect_ShakeEvent(object sender, EventArgs e)
    {
        this.Dispatcher.BeginInvoke(() =>
            {
                CanvasIP.Strokes.Clear();
                CanvasIP.Background = new SolidColorBrush(Colors.Black);
            });
    }

Is this correct now?


Solution

  • The ReadingChanged event is raised on another thread. You can not touch UI components directly, you must marshal a call back to the main thread. Use Dispatcher.BeginInvoke(), it is well described in this MSDN library article.