Sorry for posting the same question again, but kinds didn't get it right the first time. :)
Here's the code of the server application, that can receive connection and should send messages to the client.
As you can see I have the "class Form1 : Form" for the GUI stuff and "class ServerWCallbackImpl : IServerWithCallback" with the server functions.
The problem is that this classes can not communicate, i.e. I can not induce a server function from a form event, such as buttonClick, nor can I change something within the form, say TextBox1.Text += ... , from the server function.
Maybe someone could please explain, how should the code look like, so that it would work?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
namespace server
{
[ServiceContract(Namespace = "server", CallbackContract = typeof(IDataOutputCallback), SessionMode = SessionMode.Required)]
public interface IServerWithCallback ///// what comes from the client to the server.
{
[OperationContract(IsOneWay = true)]
void StartConnection(string clientName);
[OperationContract(IsOneWay = true)]
void Message_Cleint2Server(string msg);
}
public interface IDataOutputCallback ///// what goes from the sertver, to the client.
{
[OperationContract(IsOneWay = true)]
void AcceptConnection();
[OperationContract(IsOneWay = true)]
void Message_Server2Client(string msg);
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) /// once the form loads, create and open a new ServiceEndpoint.
{
ServiceHost duplex = new ServiceHost(typeof(ServerWCallbackImpl));
duplex.AddServiceEndpoint(typeof(IServerWithCallback), new NetTcpBinding(), "net.tcp://localhost:9080/service");
duplex.Open();
this.Text = "SERVER *on-line*";
}
private void buttonSendMsg_Click(object sender, EventArgs e)
{
/// callback.Message_Server2Client(textBox2.Text);
/// The name 'Message_Server2Client' does not exist in the current context :(
}
}
class ServerWCallbackImpl : IServerWithCallback /// NEED TO SOMEHOW MERGE THIS ONE WITH THE FORM1 CLASS
{
IDataOutputCallback callback = OperationContext.Current.GetCallbackChannel<IDataOutputCallback>();
public void StartConnection(string name)
{
/// TextBox1.text += name + " has connected!";
/// 'TextBox1' does not exist in the current context :(
/// can't reach form1 components. :/
MessageBox.Show( name + " has connected!");
Message2Client("Welcome, "+name+"!");
}
public void Message_Cleint2Server(string msg)
{
/// TextBox1.text += msg;
/// 'TextBox1' does not exist in the current context :(
/// can't reach form1 components. :/
}
public void Message2Client(string msg)
{
callback.Message_Server2Client(msg);
}
}
}
The lifetime of the duplex variable is limited to the Form1_Load method. That means your host will terminate when the method is finished. To keep the host running declare the duplex variable outside the method and just instantiate it in the method.
Like so:
public partial class Form1 : Form {
//Declare the variable in the class, not the method body
private ServiceHost duplex;
public Form1() {
InitializeComponent();
}
// once the form loads open a new ServiceEndpoint.
private void Form1_Load(object sender, EventArgs e) {
duplex = new ServiceHost(typeof(ServerWCallbackImpl));
duplex.AddServiceEndpoint(typeof(IServerWithCallback), new NetTcpBinding(), "net.tcp://localhost:9080/service");
duplex.Open();
this.Text = "SERVER *on-line*";
}
private void buttonSendMsg_Click(object sender, EventArgs e) {
/// callback.Message_Server2Client(textBox2.Text);
/// The name 'Message_Server2Client' does not exist in the current context :(
}
}
EDIT1:
If ServiceHost is instantiated with a type like in your example an instance of that type will be created for each connection. You can set the lifetime of the instance to session, call or connection I believe. I'm not sure how that object would access your form though. However, if you instantiate the ServerWCallbackImpl class yourself and pass a reference to the host that instance will be used.
public partial class Form1 : Form {
//Declare the variable in the class, not the method body
private ServiceHost duplex;
private ServerWithCallbackImpl localInstance;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
localInstance = new ServerWithCallbackImpl();
NetTcpBinding binding = new NetTcpBinding ();
duplex = new ServiceHost(localInstance, new Uri[] { new Uri("net.tcp://localhost:9080") });
duplex .AddServiceEndpoint(typeof(IServerWithCallback), binding, "service");
duplex .Open();
this.Text = "SERVER *on-line*";
}
}
The ServerWithCallbackImpl object will then have to keep track of it's client(s). To have the server update the GUI you might want to pass a reference to the Form as parameter into the ServerWithCallbackImpl constructor. Take a look at the Publish/Subscriber pattern to get a deeper understanding of callbacks.
Related
I am modifying to first question attempt.
I need help passing the data from a listbox and pass it to another method so every time the listbox gets data add it from the tread, it should also send that data to my new method and it to the my list because in that method I will be doing some parsing because the data from the listbox is a long string barcode but I don't need help on parsing the data because I can do that, the part I need help only is to pass the data from thread that is passing it to the listbox it should also be send to my method ReadAllData() so in that method I will received the data and then I will do the parsing the make a return.
Here is the method where the listbox is stored and receives the data from a thread from telnet port 23
Here is the code I need to send the data from the lst_BarcodeScan to the method ReadAllData method and store to my list.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using System.Security.Authentication.ExtendedProtection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BarcodeReceivingApp
{
public class TelnetConnection
{
private Thread _readWriteThread;
private TcpClient _client;
private NetworkStream _networkStream;
private string _hostname;
private int _port;
private BarcodeReceivingForm _form;
private bool _isExiting = false;
public TelnetConnection(string hostname, int port)
{
this._hostname = hostname;
this._port = port;
}
public TelnetConnection()
{
}
public void ServerSocket(string ip, int port, BarcodeReceivingForm f)
{
this._form = f;
try
{
_client = new TcpClient(ip, port);
}
catch (SocketException)
{
MessageBox.Show(#"Failed to connect to server");
return;
}
_networkStream = _client.GetStream();
_readWriteThread = new Thread(ReadWrite);
//_readWriteThread = new Thread(() => ReadWrite(f));
_readWriteThread.Start();
}
public void Exit()
{
_isExiting = true;
}
public void ReadWrite()
{
var received = "";
do
{
received = Read();
if (received == null)
break;
if (_form.lst_BarcodeScan.InvokeRequired)
{
_form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScan.Items.Add(received + Environment.NewLine);
}));
}
} while (!_isExiting);
CloseConnection();
}
public List<string> ReadAllData()
{
var obtainData = new List<string>();
return obtainData;
}
public string Read()
{
var data = new byte[1024];
var received = "";
var size = _networkStream.Read(data, 0, data.Length);
if (size == 0)
return null;
received = Encoding.ASCII.GetString(data, 0, size);
return received;
}
public void CloseConnection()
{
MessageBox.Show(#"Closed Connection",#"Important Message");
_networkStream.Close();
_client.Close();
}
}
}
Main class that will call the methods from the telnetconnection class or any other classes I will add.
using System;
using System.Windows.Forms;
namespace BarcodeReceivingApp
{
public partial class BarcodeReceivingForm : Form
{
//GLOBAL VARIABLES
private const string Hostname = "myip";
private const int Port = 23;
private TelnetConnection _connection;
public BarcodeReceivingForm()
{
InitializeComponent();
//FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
}
private void btn_ConnectT_Click(object sender, EventArgs e)
{
_connection = new TelnetConnection(Hostname, Port);
_connection.ServerSocket(Hostname, Port, this);
}
private void btn_StopConnection_Click(object sender, EventArgs e)
{
//_connection = new TelnetConnection(Hostname, Port);
//_connection.ServerSocket(Hostname, Port, this);
_connection.Exit();
}
private void btn_RemoveItemFromListAt_Click(object sender, EventArgs e)
{
for (var i = lst_BarcodeScan.SelectedIndices.Count - 1; i >= 0; i--)
{
lst_BarcodeScan.Items.RemoveAt(lst_BarcodeScan.SelectedIndices[i]);
}
}
private void BarcodeReceivingForm_Load(object sender, EventArgs e)
{
lst_BarcodeScan.SelectionMode = SelectionMode.MultiSimple;
}
private void btn_ApplicationSettings_Click(object sender, EventArgs e)
{
var bcSettingsForm = new BarcodeReceivingSettingsForm();
bcSettingsForm.Show();
}
private void btn_ClearBarcodeList_Click(object sender, EventArgs e)
{
lst_BarcodeScan.Items.Clear();
}
}
}
The easiest way to implement without adding more complexity to the thread is to simply raise an event on the listbox when and item is added to it. The problem is that the listbox do no have any events that allow to do that but you can create your own version with a dozen lines of code.
The goal is to create a derived control of the listbox then you add to it a method to trigger a custom event when an item is added and bingo.
Here's the custom listbox class with the custom EventArgs.
// custom override class over the list box so we can create an event when items are added
public class ListBoxWithEvents : ListBox
{
// the event you need to bind to know when items are added
public event EventHandler<ListBoxItemEventArgs> ItemAdded;
// method to call to add items instead of lst.Items.Add(x);
public void AddItem(object data)
{
// add the item normally to the internal list
var index = Items.Add(data);
// invoke the event to notify the binded handlers
InvokeItemAdded(index);
}
public void InvokeItemAdded(int index)
{
// invoke the event if binded anywhere
ItemAdded?.Invoke(this, new ListBoxItemEventArgs(index));
}
}
// basic event handler that will hold the index of the item added
public class ListBoxItemEventArgs : EventArgs
{
public int Index { get; set; } = -1;
public ListBoxItemEventArgs(int index)
{
Index = index;
}
}
Now you need to change your listbox on your form with the ListBoxWithEvents instead. You have 2 ways to do this but i'll give you the easiest. Compile your code and go in the design window for the form. In your toolbox you should have the ListBoxWithEvents control now and you can simply drag and drop in your form and replace the one you have. Since it derive from the listbox all it has is extra features. It didn't lose anything it had before.
Now you need to change 2 things. In your form select the new ListBoxWithEvents control and go in the properties events and you will find the new event called ItemAdded you can double click that and it should create an event like the following. I have also thrown in a sample MessageBox that display all you will need.
private void ListBox1_ItemAdded(object sender, ListBoxItemEventArgs e)
{
MessageBox.Show("Item was added at index " + e.Index + " and the value is " + listBox1.Items[e.Index].ToString());
}
Finally in order to trigger that event you need to use the new method lst.AddItem(object); instead of lst.Items.Add(object); so according to your sample code you need to change this :
_form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScan.Items.Add(received + Environment.NewLine);
}));
to this :
_form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScan.AddItem(received + Environment.NewLine);
}));
Try it and now you should have that event fire every time something is added to the list.
Since you are pretty new to programming i find it important to mention that this event will trigger on the UI thread and not the thread you created. This mean it behave normally like clicking on a button triggers a button_click(object sender, EventArgs e) event. No special thread involved whatsoever.
I have a question about callback.
I have two forms(VS2010).
I have made a class that raise an event when the ''valueIN'' change.
I set a delegate in my class so that any Form can get the ValueIN when it changes.
The problem
I create the Form2 object and link a callback to it so it is able to get ''valueIN''. but if the form2 object is not instantiated the run time tells me that there is no instantiation of the object.
So I would like to know how do I know that Form2 exists in my WorkingStation.
This line: SetValueINValCallback(value_received);
should looks like something (In workstationlooking at Form2): if(SetValINCallbackFn.exists) SetValueINValCallback(value_received);
Form2
namespace DelegateTest
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public delegate void DEL_SetValIN(Single value);//test event
public partial class Form1 : Form
{
IBAAdsServer _adsServer;
WorkingStation WorkStation;
public Form1()
{
InitializeComponent();
WorkingStation WorkStation = new WorkingStation(_adsServer);
}
private void btn_Frm2Open_Click(object sender, EventArgs e)
{
Form2 Form2Test = new Form2();
WorkStation.SetValueINValCallback += new DEL_SetValIN(Form2Test.SetValINCallbackFn);
Form2Test.Show();
}
}
}
namespace DelegateTest
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public void SetValINCallbackFn(Single Value_received)//refresh valueIN
{
label1.Text = Value_received.ToString("F1");
}
}
}
WorkStation
namespace DelegateTest
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;//msgbox
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
public class WorkingStation
{//working station class
public DEL_SetValIN SetValueINValCallback;
private static IBAAdsCncClient _cncClient;
private static IBAAdsPlcClient _plcClient;
public static List<IBAAdsNotification> _notifications;
//no need public event System.EventHandler e_RefreshValueIN;//Input value has changed
public WorkingStation(IBAAdsServer _adsServer) //constructor
{
try
{
_cncClient = _adsServer.GetAdsClient<IBAAdsCncClient>("CNC");
_plcClient = _adsServer.GetAdsClient<IBAAdsPlcClient>("PLC");
_notifications = new List<IBAAdsNotification>();
var _refreshValueIN = new OnChangeDeviceNotification("GlobalVar.PLC.valueInput", ValINHasChanged);//event handler value
_plcClient.AddPlcDeviceNotification(_refreshValueIN);
_notifications.Add(_refreshValueIN);
}
catch (Exception Except)
{ MessageBox.Show("Error init object:" + Except.Message); }
}
~WorkingStation()//destructor
{
foreach (var notify in _notifications)
{
_plcClient.RemovePlcDeviceNotification(notify);
}
}
protected virtual void ValINHasChanged(object sender, BAAdsNotificationItem notification)//get Input value
{//event method
Single value_received = 0;
try
{
value_received = _plcClient.ReadSymbol<Single>("valueInput");
SetValueINValCallback(value_received);
//no need EventHandler E_VIChange = e_RefreshValueIN;
//no need if (E_VIChange != null)
//no need E_VIChange(this, EventArgs.Empty);
}
catch (Exception except)
{
MessageBox.Show("bad event (valueIN): " + except.Message);
}
}
}
}
or does it exist another way to pass event from a class to multiple Forms?
I need those values to draw a chart in Form2 for an example.
Thanks in advance for your help!
Sorry because I didn't see that I was raising an event.
If I've made a delegate is to subscribe to the event, so I don't need to had a raising event, I just need to subscribe.
The error raised because if you code an event like:
protected virtual void ValINHasChanged(object sender, BAAdsNotificationItem notification)//get Input value
{//event method
{
EventHandler E_VIChange = e_RefreshValueIN;
if (E_VIChange != null)
E_VIChange(this, EventArgs.Empty);
}
You need to have a subscriber like
subscriber += new EventHandler... and so on
And you don^t need it anymore because you have the delegate that make the job. so you just have to subscribe to the delegate callback to have your info.
Sorry for the wasting of time.
Best regards
I'm a bit new to C#, and not quite sure how to call a subroutine. Here's what I'm trying to do:
private void button1_Click(object sender, EventArgs e)
{
// Call whatever subroutine you like
StartExstream();
}
public void StartExstream()
{
// Do Stuff Here
}
Unfortunately for me, this doesn't work. I'm getting a "Only assignment, call, increment, decrement, and new object expressions can be used as a statement" error.
How do I call my StartExstream sub from my Button1_Click event?
Thanks,
Jason
EDIT:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Call whatever subroutine you like
StartExstream();
}
public void StartExstream()
{
tcpExstream.Service1Client MyTCP = new tcpExstream.Service1Client();
string ExStreamPath;
string datPath;
string optPath;
// My Working Arguments
ExStreamPath = #"C:\Program Files\Exstream\Dialogue 6.1\Engine.exe";
datPath = #"-FILEMAP=DataFile,\\Dev-srv1\Exstream\LetterWriterApp\Input Files\Data Files\SAVEezkazivaftf40s452ndayb45.dat";
optPath = #"-CONTROLFILE=C:\Exstream\Development\LetterWriter\ControlFiles\Letter.opt";
// Hong's Arguments
//ExStreamPath = #"C:\Program Files\Exstream\Dialogue 6.1\Engine.exe";
//datPath = #"-FILEMAP=DataFile,C:\Exstream\development\AGDocGenerator\TempFiles\DataFiles\Data_456231_1598.xml";
//optPath = #"-CONTROLFILE=C:\Exstream\development\AGDocGenerator\ExstreamDialogue\ControlFiles\AGDocGenerator.opt";
// Kick It!
MyTCP.StartExStream(datPath, optPath, ExStreamPath);
// Extra line of code for breaking point
optPath = "nothing";
}
}
}
First
If the Routine is in the same class than there you can directly call the routine by just writing name of it
Example
private void AnotherMethod()
{
// Call whatever subroutine you like
MyRutine();
}
Second
If its not in the same class you need to create instance of the class which contains routine and than you can use that object to call you routine
Example
MyClass c = new MyClass();
c.MyRutine();
You have to have this all in a namespace and then in a class for this to work.
namespace some.namespace
{
public class myclass
{
private void button1_Click(object sender, EventArgs e)
{ // Call whatever subroutine you like
StartExstream();
}
public void StartExstream()
{ // Do Stuff Here
}
}
}
OK, based on the further post, Your code
tcpExstream.Service1Client MyTCP = new tcpExstream.Service1Client();
is trying to create an object of a type that isn't a type but a member of a type.
instead you would use something like
WhateverTypeTcpExstreamIs MyTCP = new WhateverTypeTcpExstreamIs();
MyTCP.Service1Client = tcpExstream.Service1Client();
Here's my code so far. I have the main form and wcf objects that are created dynamically when a client connects. Right now all wcf objects subscribe to events being fired from the main form.
Yet I want the user to be able to pick a name from the main form's comboBox and fire an event only to the wcf object, that submitted this name.
What do you think would be the best way of doing this?
Thanks!
namespace server2
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public event EventHandler sendEvnt;
private ServiceHost duplex;
private void Form2_Load(object sender, EventArgs e) /// once the form loads, create and open a new ServiceEndpoint.
{
duplex = new ServiceHost(typeof(ServerClass));
duplex.AddServiceEndpoint(typeof(IfaceClient2Server), new NetTcpBinding(), "net.tcp://localhost:9080/service");
duplex.Open();
this.Text = "SERVER *on-line*";
}
private void button1_Click(object sender, EventArgs e)
{
sendEvnt(this, new EventArgs());
// this send an event to all WCF objects
// what should I do for it to send an event ONLY to the wcf object, that's name is selected from the comboBox ?
}
}
class ServerClass : IfaceClient2Server
{
IfaceServer2Client callback;
public ServerClass()
{
callback = OperationContext.Current.GetCallbackChannel<IfaceServer2Client>();
}
public void StartConnection(string name)
{
var myForm = Application.OpenForms["Form2"] as Form2;
myForm.comboBox1.Items.Add(name);
myForm.comboBox1.SelectedItem = name; // adds a name to form's comboBox.
myForm.sendEvnt += new EventHandler(eventFromServer); // somehow need to incorporate the 'name' here.
callback.Message_Server2Client("Welcome, " + name );
}
void eventFromServer(object sender, EventArgs e)
{
var myForm = Application.OpenForms["Form2"] as Form2;
string msg = myForm.tb_send.Text;
if (msg == "") { msg = "empty message"; }
callback.Message_Server2Client(msg);
}
public void Message_Cleint2Server(string msg)
{
}
public void Message2Client(string msg)
{
}
}
[ServiceContract(Namespace = "server", CallbackContract = typeof(IfaceServer2Client), SessionMode = SessionMode.Required)]
public interface IfaceClient2Server ///// what comes from the client to the server.
{
[OperationContract(IsOneWay = true)]
void StartConnection(string clientName);
[OperationContract(IsOneWay = true)]
void Message_Cleint2Server(string msg);
}
public interface IfaceServer2Client ///// what goes from the sertver, to the client.
{
[OperationContract(IsOneWay = true)]
void AcceptConnection();
[OperationContract(IsOneWay = true)]
void RejectConnection();
[OperationContract(IsOneWay = true)]
void Message_Server2Client(string msg);
}
}
How are the WCF objects stored? I assume you are storing them in some sort of collection. If that is the case, try changing your collection to a Dictionary<string, WcfObjectType>. From there you can lookup the object in the Dictionary based on the string the user has entered.
While continuing to learn WCF, I'm trying to make it work with events.
In this example, upon a button click in a form, I want my wcf service to execute and event that would be trigger something in other form that are connected to this service.
That's the code for the Form1.
public Form1()
{
InitializeComponent();
client.TimeShowEvent += new EventHandler(TimeShowEvent);
///// SAYS THAT IT DOES NOT CONTAIN A DEFINITION FOR TimeShowEvent
}
MyWcfService1.IfaceServiceClient client = new MyWcfService1.IfaceServiceClient();
private void button2_Click(object sender, EventArgs e)
{
try
{
MyWcfService1.IfaceServiceClient client = new MyWcfService1.IfaceServiceClient();
client.passTime();
}
catch
{
MessageBox.Show("Service not availabe!");
}
}
void TimeShowEvent(object sender, EventArgs e)
{
textBox2.Text = client.timestring;
//// SAYS THAT IT DOES NOT CONTAIN A DEFINITION FOR timestring
}
and for the service:
namespace wcfLib
{
[ServiceContract]
public interface IfaceService
{
[OperationContract]
int wordLen(string word);
[OperationContract]
string passTime();
///// DO I NEED TO SOMEHOW DECLARE THE VARIABLES ( timestring ) AND EVENTS ( TimeShowEvent ) HERE?
}
}
Service implementation:
public class StockService : IfaceService
{
public event EventHandler TimeShowEvent;
public string timestring = "none";
public string passTime()
{
TimeShowEvent(this, new EventArgs());
timestring = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss");
return "";
}
public int wordLen(string word)
{
return word.Length;
}
}
Service host app:
public class Service
{
static void Main()
{
ServiceHost serviceHost = new ServiceHost(typeof(StockService), new Uri("http://localhost:8000/wcfLib"));
serviceHost.AddServiceEndpoint(typeof(IfaceService), new BasicHttpBinding(), "");
serviceHost.Open();
Console.WriteLine("Press return to terminate the service");
Console.ReadLine();
serviceHost.Close();
}
}
Do I need to somehow declare events and variables in [ServiceContract]? Is so, how?...
Thanks! :)
When you create a service reference to your service, your client gets access to the service methods - and only to the service methods.
The event handler TimeShowEvent in your service implementation class will be present and usable on the server-side only - it will not be available on the client side.
If you want to have something to call, you need to define another service method - those are "mirrored" in the client-side proxy class - and only those.
The only connection your client-side proxy and your server share are the service methods defined in your service contract, and the data that gets passed for those methods - as serialized (XML) messages. There's no "magic link" between client and server - the client can't "reach into" the server class and read stuff from there or call events on that class. There is no "remote object" connection between the two.
Try something like this:
string timeString;
private void button2_Click(object sender, EventArgs e)
{
try
{
//You already declared client, so you don't need to do it again.
//Assign the value from your wcf call to a local variable
timeString = client.passTime();
}
catch
{
MessageBox.Show("Service not availabe!");
}
}
and modify the service to return a string:
public string passTime()
{
TimeShowEvent(this, new EventArgs());
return DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss");
}
and your event:
void TimeShowEvent(object sender, EventArgs e)
{
textBox2.Text = timeString; //this is your local variable
}
And your event handler needs to be in the client. The wcf service will not know anything about the event that called it or used its result.