SyncProxy, an implicit synchronizer

By Jerome at April 04, 2004 11:51 Tags: , ,

A few days ago, I ran into an MVP article on the MSDN talking about synchronizing asynchronous calls to WebServices heading back to the GUI. Since asynchronous calls are processed in separate threads, it is not safe to call GUI methods directly from these. The article was describing how to create some sort of helper class (some call this an Agent or Pattern) that allows to hide the call to BeginInvoke, freeing the developper from creating many small methods that would only contain calls to BeginInvoke.

Back in dotnetSoul, I did not have to call WebServices - at least not in a repetitive way - but I had to synchronize calls from events generated by the netsoul core. These events are fired from asynchronous read operations on a network stream, which implies that consuming these from the GUI requires to synchronize the call to update some controls.

The old fashion way is to register a "standard" method which only calls the synchronous method via BeginInvoke with the same parameters :

    _chatRoom.UserJoined += new EventHandler(OnChatRoomUserJoined);

...

    private void OnChatRoomUserJoined(object sender, EventArgs args)
    {
         BeginInvoke(new SyncEventHandler(SyncChatRoomUserJoined), new object[]{ sender, args });
    }

    private void OnChatRoomUserJoined(object sender, EventArgs args)
    {
         // call the UI from there...
    }

What a waste of time and error prone way of doing many GUI synchronous calls, since the netsoul core exposes about 20+ events to consumers. You might notice I'm using a SyncEventHandler delegate. I'm using this delegate to avoid the BeginMethod to change the parameters passed in.

So reminding the class used to synchronize the calls to the UI, I though I could create some sort of Proxy class that could be used to both register the event and create the instance of the proxy. I then came up with this :

using System;
using System.Windows.Forms;

namespace Epitech.NetSoul.UI
{
    public delegate void SyncEventHandler(object sender, EventArgs args);

    public class SyncProxy
    {
        private Control             _control;
        private SyncEventHandler    _syncHandler;
        private EventHandler        _asyncHandler;

        public SyncProxy(Control control, SyncEventHandler syncHandler)
        {
            _control        = control;
            _syncHandler    = syncHandler;
            _asyncHandler   = new EventHandler(AsyncHandler);
        }

        // Implicit operator to allow an easy registering on events
        public static implicit operator EventHandler(SyncProxy proxy)
        {
            return proxy._asyncHandler;
        }

        // The asynchronous delegate, which calls the synchronous delegate through
        // BeginInvoke, if required.

        private void AsyncHandler(object sender, EventArgs args)
        {
            if(_control.InvokeRequired)
            {
                _control.BeginInvoke(_syncHandler, new object[]{ sender, args });
            }
            else
            {
                _syncHandler(sender, args);
            }
        }
    }
}

Which can be used like this :

    _chatRoom.UserJoined += new SyncProxy(this, new SyncEventHandler(OnChatRoomUserJoined));

This call creates the proxy then registers the asynchronous delegate from the proxy through an implicit cast to EventHandler. This way, there is only one event handler to create inside the destination form per event handled.

blog comments powered by Disqus

About me

My name is Jerome Laban, I am a Software Architect, C# MVP and .NET enthustiast from Montréal, QC. You will find my blog on this site, where I'm adding my thoughts on current events, or the things I'm working on, such as the Remote Control for Windows Phone.