简体   繁体   中英

WCF, Accessing a windows forms controls from a service

I have a WCF service that is hosted inside a Windows Form.

How can I access the controls of the form from the methods in my service?

for example I have

public interface IService    {
    [ServiceContract]
    string PrintMessage(string message);
}

public class Service: IService    
{
    public string PrintMessage(string message)
    {
        //How do I access the forms controls from here?
        FormTextBox.Text = message;
    }
}

First of all, the ServiceContract attribute should be on the interface, not the PrintMessage() method.

Using a corrected version of your example, you can do it this way.

[ServiceContract]
public interface IService
{
    [OperationContract]
    string PrintMessage(string message);
}
public class Service : IService
{
    public string PrintMessage(string message)
    {
        // Invoke the delegate here.
        try {
            UpdateTextDelegate handler = TextUpdater;
            if (handler != null)
            {
                handler(this, new UpdateTextEventArgs(message));
            }
        } catch {
        }
    }
    public static UpdateTextDelegate TextUpdater { get; set; }
}

public delegate void UpdateTextDelegate(object sender, UpdateTextEventArgs e);

public class UpdateTextEventArgs
{
    public string Text { get; set; }
    public UpdateTextEventArgs(string text)
    {
        Text = text;
    }
}

public class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();

        // Update the delegate of your service here.
        Service.TextUpdater = ShowMessageBox;

        // Create your WCF service here
        ServiceHost myService = new ServiceHost(typeof(IService), uri);
    }
    // The ShowMessageBox() method has to match the signature of
    // the UpdateTextDelegate delegate.
    public void ShowMessageBox(object sender, UpdateTextEventArgs e)
    {
        // Use Invoke() to make sure the UI interaction happens
        // on the UI thread...just in case this delegate is
        // invoked on another thread.
        Invoke((MethodInvoker) delegate {
            MessageBox.Show(e.Text);
        } );
    }
}

This is essentially the solution suggested by @Simon Fox, ie, use a delegate. This hopefully just puts some flesh on the bones, so to speak.

The best way to handle such a scenario is to inject the Form into the service as a dependency. I would define some sort of interface that decouples the Form code from the WCF code:

public interface IFormService
{
    string Text { get; set; }
}

You can have your Form implement the IFormService interface by setting the real property you would like to update.

Your service would need an instance of IFormService to do its work:

public class Service : IService
{
    private readonly IFormService form;

    public Service(IFormService form)
    {
        this.form = form
    }

    public string PrintMessage(string message)
    {
        this.form.Text = message;
    }
}

Since the Service class now has no default constructor, you will also need to implement a custom ServiceHostFactory that can create instances of the Service class and inject the concrete implementation of IFormService.

Use a delegate. Create a delegate in the codebehind of your form which references a method that writes to the textbox and pass it to the Service, the service can then invoke the delegate when it wants to print a message.

You will have issues trying to update the textbox because the delegate will be invoked on a different thread to that which the textbox was created on. In the WPF world you would use Dispatcher.BeginInvoke to get around this, not sure what the WinForms equivalent is.

The service will need a reference to the instance of the form, not just the type of the form.

Further you will need a way to send values to the form controls from your service class. This can either be done by making the Controls themselves public or protected, or you can create properties on your form class that will set the properties on the Controls.

A simple approach would be the following:

  1. Add a constructor to your service class that takes a reference to your form instance. If you are hosting your service inside the form this should be easy.
  2. Add public properties for the control properties you want to set from the service. The properties should be public or internal and will set the values of the controls properties.
  3. In your service you set the values of the newly added properties.

For completeness, there is a simpler approach using the same solution.

You can directly invoke a delegate on the same thread using the invoke method.

Invoke(new MethodInvoker(delegate{FormTextBox.Text = message;});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM