Condividi tramite


Using Indigo to communicate with Windows Forms applications, Part III

In Part I and Part II of this series, I showed how to implement a service within a Form and how to use Indigo’s singleton model to ensure that the form is the target of the message rather than having a new instance of the form created for each message.  In this part, I will begin the discussion of implementing a service in a class outside of the form and show one of ways for your service implementation to interact with the form.  Subsequent parts in this series will expand upon ways for the service implementation to interact with the form.

I have refactored the service implementation of IFooService from Form1 into a class named Service, and the form now instantiates a new ServiceHost<Service> rather than ServiceHost<Form1>:

     public partial class Form1 : Form
    {
        ServiceHost<Service> s;

        public Form1()
        {
            s = new ServiceHost<Service>();
            s.Open();

            InitializeComponent();
        }
    }
     [System.ServiceModel.ServiceContract]
    interface IFooService
    {
        [System.ServiceModel.OperationContract(IsOneWay=true)]
        void BarMethod();
    }

    class Service : IFooService
    {
        public void BarMethod()
        {
        }
    }

Indigo will now create a new instance of the Service class for each incoming message, but we are left with a new challenge.  How does each instance of Service communicate with the form?  If you recall from Part I, we are updating the contents of a textbox within the form.  It would be bad design for Service to have internal knowledge of Form1’s implementation (i.e., to access Form1.textBox1), furthermore Form1.textBox1 is private and inaccessible to classes outside of Form1, so another method must be sought.

One way to deal with this communication requirement is to use the .NET Framework’s eventing model.  Service can provide an event that Form1 can subscribe to.  When Indigo delivers a message to Service.BarMethod, BarMethod can raise the event and notify its subscribers of the activity.  To evolve the design, I’ll define a delgate, MessageReceivedEventHandler and an EventArgs-derived class, MessageReceivedEventArgs to communicate the text of the message:

     delegate void MessageReceivedEventHandler(object sender, MessageReceivedEventArgs e);

    public class MessageReceivedEventArgs : EventArgs
    {
        private string _messageText;
        public string Text
        {
            get { return _messageText; }
        }

        public MessageReceivedEventArgs(string MessageText)
        {
            _messageText = MessageText;
        }
    }

Service may now define an event, MessageReceived, based upon the MessageReceivedEventHandler delegate and raise the event every time Service.BarMethod is called:

     class Service : IFooService
    {
        public event MessageReceivedEventHandler MessageReceived;

        public void BarMethod()
        {
            MessageReceivedEventArgs e = new MessageReceivedEventArgs(System.DateTime.Now.ToString());
            MessageReceived(this, e);
        }
    }

It is here that the complexity of implementing the service outside of the form raises its first issue. Remember that Indigo will create a new instance of Service for each incoming message.  This creates a challenge for Form1 in that it cannot subscribe to the event for a given instance of Service because Form1 itself does not create any instances of Service.  As you may recall from Part II, it is possible for Form1 to create an instance of a class and pass this instance to the ServiceHost<T>() constructor, thereby creating an Indigo singleton.  With this created instance, Form1 could then subscribe to the event.

While you can create a singleton, Indigo’s activation model of creating an instance-per-message provides us with greater scalabilty and frees us from having to worry about things like thread safety.  Therefore, we can make the event static to Service and notify all subscribers any time any instance of Service receives a message:

     class Service : IFooService
    {
        static public event MessageReceivedEventHandler MessageReceived;

        public void BarMethod()
        {
            MessageReceivedEventArgs e = new MessageReceivedEventArgs(System.DateTime.Now.ToString());
            MessageReceived(this, e);
        }
    }

With this model in place, we can complete Form1 by subscribing to Service.MessageReceived and providing our event handler to update textBox1 every time a message is received by Service.BarMethod:

     public partial class Form1 : Form
    {
        ServiceHost<Service> s;

        public Form1()
        {
            Service.MessageReceived += OnMessageReceived;

            s = new ServiceHost<Service>();
            s.Open();

            InitializeComponent();
        }

        protected void OnMessageReceived(object sender, MessageReceivedEventArgs e)
        {
            textBox1.Text = e.Text;
        }
    }

This article has explored one design that enables a service implementation to communicate with a Windows Form.  In Part IV, I will discuss some challenges with this design and explore an alternate implementation.