MJay
MJay

Reputation: 537

Custom event and invocation on main thread

I was given a generic API class, that contains a custom event which always needs to be invoked by the main UI thread. My job is to banish these invocation call from the custom class, to make it "painless".

It should be synchronized like the default events in WinForms (eg the Timer "Elapsed" event, which also needs no invocation when it published values to a text box)

Is it possible to solve this, since the custom class needs to know where to invoke?

Here's the (important part of the) code:

public class ContactSensorHelper
{
    public event OnReleaseStateChanged ReleaseStateChanged;
    public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);

    private ContactSensorEventArgs.ReleaseState recentReleaseState;

    public void ReportStateChanged()
    {
        if (ReleaseStateChanged != null)
            ReleaseStateChanged(new ContactSensorEventArgs()
            {
                State = recentReleaseState
            });
    }

    public class ContactSensorEventArgs : EventArgs
    {
        //......

        public ReleaseState State { get; set; }

        //......

        public enum ReleaseState
        {
            FullReleased,
            PartlyReleased,
            NotReleased
        }
    }
}

The call from main UI:

public void SensorInit()
{
    //....
    sensorHelper.ReleaseStateChanged += releaseStateChanged;
    //....
}

private void releaseStateChanged(ContactSensorEventArgs e)
{
    //example
    textBox1.Text = e.State.ToString();   // Thread exception (obviously)
}

Does anybody have me a hint to start?

Upvotes: 0

Views: 167

Answers (1)

Me.Name
Me.Name

Reputation: 12544

You could do this by using your own event calling, and storing a reference to the thread, when the event is attached.

With the event add/remove syntax, you can have the caller attach to the event like before, but internally you store a list, with a reference to the thread (using an AsyncOperation) and the delegate to be called (used a Tuple containing both in the example)

Below is an example. I tested it, and it worked as expected when testing, but you might have to add some locking of the list to make it thread safe in case events are added/removed simultaneously.

    public class ContactSensorHelper:IDisposable
    {

        public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);

        private ContactSensorEventArgs.ReleaseState recentReleaseState;

        public void ReportStateChanged()
        {
            if (statechangedList.Count > 0)
            {
                var e = new ContactSensorEventArgs()
                {
                    State = recentReleaseState
                };

                statechangedList.ForEach(t =>
                    t.Item1.Post(o => t.Item2((ContactSensorEventArgs)o), e));
            }
        }            

        List<Tuple<AsyncOperation, OnReleaseStateChanged>> statechangedList = new List<Tuple<AsyncOperation,OnReleaseStateChanged>>();
        public event OnReleaseStateChanged ReleaseStateChanged
        {
            add
            {
                var op = AsyncOperationManager.CreateOperation(null);
                statechangedList.Add(Tuple.Create(op, value));                    
            }
            remove
            {
                var toremove = statechangedList.Where(t => t.Item2 == value).ToArray();
                foreach (var t in toremove)
                {
                    t.Item1.OperationCompleted();
                    statechangedList.Remove(t);
                }
            }
        }

        public void Dispose()
        {
            statechangedList.ForEach(t => t.Item1.OperationCompleted());
            statechangedList.Clear();
        }

        public class ContactSensorEventArgs : EventArgs
        {
            //......

            public ReleaseState State { get; set; }

            //......

            public enum ReleaseState
            {
                FullReleased,
                PartlyReleased,
                NotReleased
            }
        }

    }

Upvotes: 1

Related Questions