How to generate callback (event) from library to application in c# - c#

I'm developing one library (DLL), in which I need to provide event (interrupt) to user as one method with data. Library's work is start listing on socket, receive data from socket and pass this data to user in one method.
Library:
public void start(string IP, int port)
{
// start logic...
// receives data from socket... send this data to user
}
Application:
Library. Class a = new Library. Class();
a.start(ip, port);
// I need this method called by library automatically when it receives data...
void receivedData(string data)
{
// data which received by library....
}
How to raise event to application with data from library?
Thanks in advance....

Add an event to your library like this:
public event Action<string> OnDataReceived = null;
Then, in Application:
Library.Class a = new Library.Class();
a.OnDataReceived += receivedData;
a.start(ip, port);
That's it.
But you may want to write events with the conventions and I suggest you'll start get use to it because .NET is using events that way so whenever you bump into that convention you'll know it's events.
So if I refactor your code a little bit it should be something like:
In your class library:
//...
public class YourEventArgs : EventArgs
{
public string Data { get; set; }
}
//...
public event EventHandler DataReceived = null;
...
protected override OnDataReceived(object sender, YourEventArgs e)
{
if(DataReceived != null)
{
DataReceived(this, new YourEventArgs { Data = "data to pass" });
}
}
When your class library wants to launch the event it should call the OnDataReceived which is responsible for checking that someone is listening and constructing the appropriate EventArgs for passing by your data to the listener.
In the Application you should change your method signature:
Library.Class a = new Library.Class();
a.DataReceived += ReceivedData;
a.start(ip, port);
//...
void ReceivedData(object sender, YourEventArgs e)
{
string data = e.Data;
//...
}

You should change signature of start method to pass there delegate:
public void start(string IP, int port, Action<string> callback)
{
// start logic...
// receives data from socket... send this data to user
callback(data);
}
Library. Class a = new Library. Class();
a.start(ip, port, receivedData);
// I need this method called by library automatically when it receives data...
void receivedData(string data)
{
// data which received by library....
}

Add event to your class
public event DataReceivedEventHandler DataReceived;
public delegate void DataReceivedEventHandler(object sender, SocketDataReceivedEventArgs e);
Create a class that contents your required parameters like Ex : SocketDataReceivedEventArgs here
Trigger event like
SocketDataReceivedEventArgs DataReceiveDetails = new SocketDataReceivedEventArgs();
DataReceiveDetails.data = "your data here";
DataReceived(this, DataReceiveDetails);
in application create method
void receivedData(object sender, SocketDataReceivedEventArgs e)
{
// e.data is data which received by library....
}
Now attach handler to it
Library. Class a = new Library. Class();
a.DataReceived += receivedData;
a.start(ip, port);
You need to write it in multiple threads as your requirement is
Here is how you can add thread safe support to above
Dispatcher.Invoke and accessing textbox from another thread

Related

How to report file progress to clients using SignalR?

I have written a component that gets and excel file and stores its data into a database. Since I wanted this class to support different environments (for example using it inside a console app) So I decided to create some events:
public interface IDataSeeder
{
Task Seed(string fileName);
event EventHandler<UserSucceedEventArgs> UserAdded;
event EventHandler<UserErrorEventArgs> Error;
event EventHandler<UserUpdateEventArgs> UserUpdated;
event EventHandler<FinishDataSeederEventArgs> ProcessCompleted;
}
Each of this events will trigger in different places inside Seed method. It works like a charm in my console application.
Now I want to use this component inside my ASP.NET MVC app, for doing so I decided to use SignalR for pushing event's data to client, So I created a hub like this:
public class ProgressHub : Hub
{
public static void UserAdded(UserSucceedEventArgs e)
{
var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
ctx.Clients.All.userAdded(e);
}
public static void Error(UserErrorEventArgs e)
{
var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
ctx.Clients.All.error(e);
}
public static void UserUpdated(UserUpdateEventArgs e)
{
var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
ctx.Clients.All.userUpdated(e);
}
public static void ProcessCompleted(FinishDataSeederEventArgs e)
{
var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
ctx.Clients.All.processCompleted(e);
}
}
Then I created this action method for getting uploaded file and pass it to my component:
[HttpPost]
public async Task<ActionResult> AddFromExcel(HttpPostedFileBase excelFile)
{
if (excelFile != null)
{
var fileName = Utilities.UploadFile.UploadFile.UploadCommonFile(excelFile, "users");
_dataSeeder.UserAdded += DataSeeder_Succeed;
_dataSeeder.Error += DataSeeder_Error;
_dataSeeder.UserUpdated += DataSeeder_Update;
_dataSeeder.ProcessCompleted += DataSeeder_Finish;
await _dataSeeder.Seed(Server.MapPath($"~/Content/Files/users/{fileName}"));
return RedirectToAction("AddFromExcel");
}
return RedirectToAction("List");
}
private static void DataSeeder_Finish(object sender, FinishDataSeederEventArgs e)
{
ProgressHub.ProcessCompleted(e);
}
private static void DataSeeder_Update(object sender, UserUpdateEventArgs e)
{
ProgressHub.UserUpdated(e);
}
private static void DataSeeder_Error(object sender, UserErrorEventArgs e)
{
ProgressHub.Error(e);
}
private static void DataSeeder_Succeed(object sender, UserSucceedEventArgs e)
{
ProgressHub.UserAdded(e);
}
As you can see inside each event handler I notify the clients using my signalr hub.
All of this process is like a messaging system, but I have no idea how to implement it inside a web application. A flaw with my code is that after attaching the event handler I immediately redirect the user to another action method, I know it must be an asynchronous process but I have no idea how to make my events async.
Any idea?
What you have to do is match the SignalR server calls with JavaScript functions that will show the results to the connected clients:
<script type="text/javascript">
// the proxy to your hub
var hub = $.connection.ProgressHub;
// the function that will be called when you call
// ctx.Clients.All.userAdded(e);
hub.client.userAdded = function (data) {
// show the data
alert(data);
};
// follow suit with the rest of the functions
hub.client.error = function (data) {
alert(data);
};
// etc
</script>

Updating GUI from different Class and Thread in c#

Im new to programming and just wanted to know if a solution for a problem I got is appropriate.
I wanted to write a status (string) into a textbox from a class which is creating a Socket and the class listens for data to receives (in an other thread).
This is what i did:
Create the Class whithin the Form.cs with a button click:
private void button_Create_Click(object sender, EventArgs e)
{
int port;
Int32.TryParse(textBox_Port.Text, out port);
ServerSocketClass serverSocket = new ServerSocketClass(port, this);
}
The ServerSocketClass looks like:
class ServerSocketClass
{
Socket ServerSocket;
Socket Accepted;
IPEndPoint LocalEndpoint;
int Port = 1337; // just for fun
Messenger MainForm;
public ServerSocketClass(int port, Messenger form)
{
MainForm = form;
if (port != 0)
Port = port;
ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
LocalEndpoint = new IPEndPoint(IPAddress.Any, Port);
MainForm.writeToMessages("Binding Endpoint to Socket...");
ServerSocket.Bind(LocalEndpoint);
MainForm.writeToMessages("Starting ServerListener Thread...");
Thread ServerListenThread = new Thread(startListening);
ServerListenThread.Name = "ServerListenerThread";
ServerListenThread.Start();
}
private void startListening()
{
ServerSocket.Listen(5);
MainForm.writeToMessages("Whaiting for incoming connections...");
Accepted = ServerSocket.Accept();
whaitForData();
}
and to update the GUI in the forms class i created a delegate and a "update" method with an invoke:
public delegate void writeMessege(string message);
public writeMessege MessegeDelegate;
public void writeToMesseges(string messege)
{
if (InvokeRequired)
{
this.Invoke(MessegeDelegate, new object[] { messege });
return;
}
textBox_Messeges.AppendText("SYSTEM: " + messege + "\n");
}
It works, but I wanted to know if this is a "valid" way to do it or if I should go to the developer hell ;-)
thanks in advance
Locke
It's a perfectly valid way to do that, although whether it is "right" depends very much on the context - how often you call it, what you want to do inside it, and the code that you need to call it. There are many different ways of doing it without invoke, but there is nothing wrong with using InvokeRequired/Invoke - that's what it's there for. You could just use an update method that invokes itself, which is almost the same as your code, but slightly less verbose:
public void WriteMessages(string message)
{
if (InvokeRequired)
{ this.Invoke(new Action<string>(WriteMessages), new object[] { message }); }
else
{ textBox_Messages.AppendText("SYSTEM: " + message + "\n"); }
}
There are a lot of posts already on Invoke/InvokeRequired. As a starting point, check:
Isn't blindly using InvokeRequired just bad practice?
I had a similar situation, where I had a class that was called from other classes with many separate threads and I had to update one specific form from all these other threads. So creating a delegate and an event in the class with a handler in the form was the answer. So I wanted to share it as it seems simpler (even if not necessarily a better solution).
The solution that worked for me:
I created an event in the class I wanted to do the update on another form. (First of course I instantiated the form (called SubAsstToolTipWindow) in the class.
Then I used this event (ToolTipShow) to create an event handler on the form I wanted to update the label on. Worked like a charm.
I used this description to devise my own code below in the class that does the update:
public static class SubAsstToolTip
{
private static SubAsstToolTipWindow ttip = new SubAsstToolTipWindow();
public delegate void ToolTipShowEventHandler();
public static event ToolTipShowEventHandler ToolTipShow;
public static void Show()
{
// This is a static boolean that I set here but is accessible from the form.
Vars.MyToolTipIsOn = true;
if (ToolTipShow != null)
{
ToolTipShow();
}
}
public static void Hide()
{
// This is a static boolean that I set here but is accessible from the form.
Vars.MyToolTipIsOn = false;
if (ToolTipShow != null)
{
ToolTipShow();
}
}
}
Then the code in my form that was updated:
public partial class SubAsstToolTipWindow : Form
{
public SubAsstToolTipWindow()
{
InitializeComponent();
// Right after initializing create the event handler that
// traps the event in the class
SubAsstToolTip.ToolTipShow += SubAsstToolTip_ToolTipShow;
}
private void SubAsstToolTip_ToolTipShow()
{
if (Vars.MyToolTipIsOn) // This boolean is a static one that I set in the other class.
{
// Call other private method on the form or do whatever
ShowToolTip(Vars.MyToolTipText, Vars.MyToolTipX, Vars.MyToolTipY);
}
else
{
HideToolTip();
}
}
long time ago, but I wanted you all know how I finally solved this to my full satisfaction (solved it with Events - of course ;-)):
I defined an EventArgs to pass all the Information I wanted to pass:
public class IncomingMessageEventArgs : EventArgs
{
private Message _message;
public Message Message
{
get
{
return _message;
}
}
public IncomingMessageEventArgs(Message message)
{
_message = message;
}
}
On the Class that publishes the information (to the WPF - Form) define the Event and its Handler:
public delegate void IncomingMessageEventHandler(object sender, IncomingMessageEventArgs e);
public event IncomingMessageEventHandler IncomingMessageEvent;
protected void OnIncomingMessageEvent(IncomingMessageEventArgs e)
{
if (IncomingMessageEvent != null)
IncomingMessageEvent(this, e);
}
and of course Raise the event, if the WPF Form needs to be updated (also on the "information sending class"):
OnIncomingMessageEvent(new IncomingMessageEventArgs(message));
on the WPF Class you need to listen to the events but first define a EventHandler because your information comes from a differen Thread!! :
private delegate void writeMessageToChatEventHandler(object sender, IncomingMessageEventArgs e);
now we write our method witch will handle the raised event:
// Write to Chat
private void writeMessageToChat(object sender, IncomingMessageEventArgs e)
{
try
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new writeMessageToChatEventHandler(writeMessageToChat), new object[] { sender, e } );
return;
}
textBox_Chat.AppendText(e.Message.getFormatedMessageText() + "\n");
}
catch (Exception ex)
{
writeLogToChat(this, new IncomingLogEventArgs("ERROR: " + ex.Message));
}
}
and finally, we need to subscribe to the event of course (the first method, you can ignore, its just to meet the MS Nameing conventions:
private void ClientSocket_IncomingMessageEvent(object sender, IncomingMessageEventArgs e)
{
writeMessageToChat(sender, e);
}
ClientSocket.IncomingMessageEvent += ClientSocket_IncomingMessageEvent;
Hopefully I made this understandable :P
Thanks to all the people how helped me!
bye

How to know when dll events fired

I created a dll contain this event handler:
public void tcp1_Data(object sender, Sockets.DataEventArgs e)
{
Tcp tcp = (Tcp)sender;
response = "Socket Connection" + tcp.Tag.ToString() + " replied : " + e.Data.ToString();
tcp.Close();
}
this will fire when server write some thing in socket connection. so by this, I can read the data on socket.
I used this dll in another project. I want to know in my project (that used dll) exactly when server is writing data on socket connection. as you see in tcp1_Data event, I set result into response variable and in main project (that used dll), I checked this variable polling (if response is not null, it means that this event fired). but Its not what I want. I dont want check this variable all the time.
is there any other way?
I tried this as #ThorstenDittmar said:
my dll project (its name is ClientSample) contain:
TheClassInDLL Class
public class TheClassInDLL
{
public event EventHandler<MyEventArgs> DataEventCalled;
public void tcp1_Data(object sender, Sockets.DataEventArgs e)
{
Tcp tcp = (Tcp)sender;
// Note: LOCAL variable
string myresponse = "Socket Connection" + tcp.Tag.ToString() + " replied : " + e.Data.ToString();
// Call the new event here
if (DataEventCalled != null)
DataEventCalled(this, new MyEventArgs(myresponse));
tcp.Close();
}
// We use this class to pass arguments to the event handler
public class MyEventArgs : EventArgs
{
public MyEventArgs(string response)
{
this.Response = response;
}
public string Response
{
get;
private set;
}
}
}
TCPSample class
public class TCPSample
{
Tcp tcp = new Tcp();
tcp.Data += new System.EventHandler
and in another project that I used above dll:
public partial class Form1 : Form
{
private TheClassInDLL myClass;
ClientSample.TCPSample t = new ClientSample.TCPSample();
public Form1()
{
InitializeComponent();
myClass = new TheClassInDLL();
myClass.DataEventCalled += DataEvent;
}
private void button1_Click(object sender, EventArgs e)
{
t.newTCP();
}
private void DataEvent(object sender, TheClassInDLL.MyEventArgs e)
{
MessageBox.Show(e.Response);
}
}
but It didnt work, DataEvent never happend.
Thanks for any helping...
What you wrote here is an event handler that is called when something happens. There must be a class containing this event handler. Instead of writing a global response variable, declare and invoke another event you can subscribe to from outside that class like this:
public class <TheClassInDLL>
{
public event EventHandler<MyEventArgs> DataEventCalled;
// SNIP: All the other code that leads to tcp1_Data being called
...
// The event handler that's called by some code in the class
public void tcp1_Data(object sender, Dart.Sockets.DataEventArgs e)
{
Tcp tcp = (Tcp)sender;
// Note: LOCAL variable
string myresponse = "Socket Connection" + tcp.Tag.ToString() + " replied : " + e.Data.ToString();
// Call the new event here
if (DataEventCalled != null)
DataEventCalled(this, new MyEventArgs(myresponse));
tcp.Close();
}
// We use this class to pass arguments to the event handler
public class MyEventArgs : EventArgs
{
public MyEventArgs(string response)
{
this.Response = response;
}
public string Response
{
get;
private set;
}
}
}
From the caller, you use it like this:
public class <TheCallingClassOutsideDLL>
{
private <TheClassInDLL> myClass;
public TheCallingClassOutsideDLL()
{
myClass = new TheClassInDLL();
myClass.DataEventCalled += DataEvent;
}
private void DataEvent(object sender, <TheClassInDLL>.MyEventArgs e)
{
Console.WriteLine(e.Response);
}
}
Of course you need to replace <TheClassInDLL> and <TheCallingClassOutsideDLL> with the real class names! Creating additional classes of course doesn't work!
For that you got to define your own event and raise it when needed...
for Example -> In the class where you set the "response" variable define an event
//your custom event
public event EventHandler<CustomEventArgs> MyCustomEvent;
//This will raise your event and notify all who registered
private void RaiseMyCustomEvent(CustomEventArgs e)
{
var handler = MyCustomEvent;
if (handler != null)
{
handler(this, e);
}
}
Maybe you will also need CustomEventArgs (used in the example above)
public class CustomEventArgs : EventArgs
{
public String Message {get;private set;}
public CustomEventArgs(String message){
this.Message = message;
}
}
The class which is using the dll and that wants to get notified needs to register for this event
YourDllClassInstance.MyCustomEvent += OnMyCustomEvent;
public void OnMyCustomEvent(object sender, CustomEventArgs e){
Console.WriteLine("Event received");
}
That means in your dll class you got to do something like the following when you want to raise the event
response = "blablabla";
RaiseMyCustomEvent(new CustomEventArgs(response);
Is that what you where asking for?

Addressed event subscription

In several of my projects, it is becoming apparent that I need something slightly more powerful than the standard .NET events.
Basically, I want the option of within a message pump, having callbacks (events) raised to specific subscribers when the component they're interested changes.
This could be a specific I/O changing state (e.g. button closing contact) for one project, or a message received from a wireless ethernet for a specific MAC address in another.
My current line of thinking is to use a dictionary, and list of delegates against each address (for this example).
I haven't debugged this yet but is the following along the right lines?
class CustomSubscription
{
public delegate void DataReceivedHandler(object sender, DataReceivedEventArgs args);
public class DataReceivedEventArgs : EventArgs
{
public byte[] data;
}
private readonly Dictionary<int, List<DataReceivedHandler>> _subscribers;
public CustomSubscription()
{
_subscribers = new Dictionary<int, List<DataReceivedHandler>>();
}
public void AddSubscriber(int address, DataReceivedHandler callback)
{
if (false == _subscribers.ContainsKey(address))
{
_subscribers.Add(address, new List<DataReceivedHandler>());
}
_subscribers[address].Add(callback);
}
public void RemoveSubscriber(int address, DataReceivedHandler callback)
{
if (false == _subscribers.ContainsKey(address))
{
return;
}
if (_subscribers[address].Contains(callback))
{
_subscribers[address].Remove(callback);
}
}
public void HandleIncommingData(int address, object sender, byte[] payload)
{
if (false == _subscribers.ContainsKey(address))
{
// Nothing subscribed - take no action
return;
}
// Raise callbacks with all subscribers
foreach (DataReceivedHandler callback in _subscribers[address])
{
callback(sender, new DataReceivedEventArgs
{
data = payload
});
}
}
}
Looks like you are trying to implement an event aggregator pattern. There are plenty implementations already on the web. You can start from here, for example:
https://stackoverflow.com/questions/2343980/event-aggregator-implementation-sample-best-practices

How to access initial parameters in the callback of an asynchronous WCF request?

I'm currently updating a client application that makes use of a WCF web service from synchronous to asynchronous calls. The main server and the client are on the same local network, but it is too unreliable for the application to hang while it waits for a response.
The application makes use of 4 identical endpoints across 2 servers (so if an instance has crashed or a server is offline, there should still be something to call).
The client has a layer responsible for making calls to the web service. My initial synchronous design was for the the active endpoint to be called and if an exception was thrown we would then move to the next endpoint and recursively call the same method. This would be done until all endpoints are exhausted.
I've now made the modifications to make this async but there is one issue. The parameters are lost once we are in the callback. So when it it time to call the Begin method again recursively, the parameters are not accessible to be passed in again.
What would be the best way to pass parameters from the Begin method to the callback method? Are they stored anywhere in the client object? Can it be done through the event or should I store them at the class level?
public delegate void GetUserInfoCompletedEventHandler(UserInfo e);
public static event GetUserInfoCompletedEventHandler GetUserInfoCompleted;
public delegate void GetUserInfoFaultedEventHandler(string errorMessage);
public static event GetUserInfoFaultedEventHandler GetUserInfoFaulted;
public static void BeginGetUserInfo(string fobID)
{
MyClient client = new MyClient(availableEndpoints[activeEndpointIndex].Name);
client.GetUserInfoCompleted += new EventHandler<GetUserInfoCompletedEventArgs>(client_GetUserInfoCompleted);
client.GetUserInfoAsync(fobID);
}
static void client_GetUserInfoCompleted(object sender, GetUserInfoCompletedEventArgs e)
{
// Get the instance of the client
MyClient client = (MyClient)sender;
if (null == e.Error)
{
// Close the client instance if there was no error
try { client.Close(); }
catch { }
if ((null != GetUserInfoCompleted) && (null != e.Result))
{
// Report as successful and raise the event
ServiceActionSuccessful();
GetUserInfoCompleted(e.Result);
}
}
else
{
// Abort the client as there was an error
try { client.Abort(); }
catch { }
if (e.Error is FaultException<WebServiceError>)
{
FaultException<WebServiceError> fault = (FaultException<WebServiceError>)e.Error;
if (null != GetUserInfoFaulted)
{
// A fault occurred in the web service
GetUserInfoFaulted(fault.Detail.ErrorMessage);
}
}
else
{
// Assume this was problem in connection so test if there any more endpoints to attempt
bool isNextEndpointAvaialble = ServiceActionFailure();
if (isNextEndpointAvaialble)
{
// If there are more endpoints to try, call the method to run again
BeginGetUserInfo(); // Need parameters here
}
else
{
if (null != GetUserInfoFaulted)
{
// No more endpoints to try
GetUserInfoFaulted(Errors.GetUserFriendlyMessage(e.Error));
}
}
}
}
}
If MyClient is a generated class, there should be a second function called
MyClient.GetUserInfoAsync(string fobID, object userState);
The content of the userState argument is passed dirctly to the GetUserInfoCompletedEventArgs.UserState property in the eventargs received by client_GetUserInfoCompleted.
So you could do something like this:
public static void BeginGetUserInfo(string fobID)
{
MyClient client = new MyClient(availableEndpoints[activeEndpointIndex].Name);
client.GetUserInfoCompleted += new EventHandler<GetUserInfoCompletedEventArgs>(client_GetUserInfoCompleted);
client.GetUserInfoAsync(fobID, fobID);
}
static void client_GetUserInfoCompleted(object sender, GetUserInfoCompletedEventArgs e)
{
string fobID = e.UserState as string;
// handle the event here...
}
Another alternative is to use a lambda for handling the event:
public static void BeginGetUserInfo(string fobID)
{
MyClient client = new MyClient(availableEndpoints[activeEndpointIndex].Name);
client.GetUserInfoCompleted += (sender, args) => client_GetUserInfoCompleted(sender, args, fobID);
client.GetUserInfoAsync(fobID);
}
static void client_GetUserInfoCompleted(object sender, GetUserInfoCompletedEventArgs e, string fobID)
{
// access the additional parameter fobID here
}

Categories