How to update textbox on GUI from another thread [duplicate] - c#

This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 5 years ago.
I'm new with C# and I'm trying to make a simple client server chat application.
I have RichTextBox on my client windows form and I am trying to update that control from server which is in another class. When I try to do it I get the error: "Cross-thread operation not valid: Control textBox1 accessed from a thread other than the thread it was created on".
Here the code of my Windows form:
private Topic topic;
public RichTextBox textbox1;
bool check = topic.addUser(textBoxNickname.Text, ref textbox1, ref listitems);
Topic class:
public class Topic : MarshalByRefObject
{
//Some code
public bool addUser(string user, ref RichTextBox textBox1, ref List<string> listBox1)
{
//here i am trying to update that control and where i get that exception
textBox1.Text += "Connected to server... \n";
}
So how to do that? How can I update the textbox control from another thread?
I'm trying to make some basic chat client/server application using .net remoting.
I want to make windows form client application and console server application as separate .exe files. Here im trying to call server function AddUser from client and i want to AddUser function update my GUI. Ive modified code as you suggested Jon but now instead of cross-thread exception i've got this exception ... "SerializationException: Type Topic in Assembly is not marked as serializable".
Ill post my whole code bellow, will try to keep it simple as possible.
Any suggestion is welcome. Many thanks.
Server:
namespace Test
{
[Serializable]
public class Topic : MarshalByRefObject
{
public bool AddUser(string user, RichTextBox textBox1, List<string> listBox1)
{
//Send to message only to the client connected
MethodInvoker action = delegate { textBox1.Text += "Connected to server... \n"; };
textBox1.BeginInvoke(action);
//...
return true;
}
public class TheServer
{
public static void Main()
{
int listeningChannel = 1099;
BinaryServerFormatterSinkProvider srvFormatter = new BinaryServerFormatterSinkProvider();
srvFormatter.TypeFilterLevel = TypeFilterLevel.Full;
BinaryClientFormatterSinkProvider clntFormatter = new BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = listeningChannel;
HttpChannel channel = new HttpChannel(props, clntFormatter, srvFormatter);
// Register the channel with the runtime
ChannelServices.RegisterChannel(channel, false);
// Expose the Calculator Object from this Server
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Topic),
"Topic.soap",
WellKnownObjectMode.Singleton);
// Keep the Server running until the user presses enter
Console.WriteLine("The Topic Server is up and running on port {0}", listeningChannel);
Console.WriteLine("Press enter to stop the server...");
Console.ReadLine();
}
}
}
}
Windows form client:
// Create and register a channel to communicate to the server
// The Client will use the port passed in as args to listen for callbacks
BinaryServerFormatterSinkProvider srvFormatter = new BinaryServerFormatterSinkProvider();
srvFormatter.TypeFilterLevel = TypeFilterLevel.Full;
BinaryClientFormatterSinkProvider clntFormatter = new BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = 0;
channel = new HttpChannel(props, clntFormatter, srvFormatter);
//channel = new HttpChannel(listeningChannel);
ChannelServices.RegisterChannel(channel, false);
// Create an instance on the remote server and call a method remotely
topic = (Topic)Activator.GetObject(typeof(Topic), // type to create
"http://localhost:1099/Topic.soap" // URI
);
private Topic topic;
public RichTextBox textbox1;
bool check = topic.addUser(textBoxNickname.Text,textBox1, listitems);

You need to either use BackgroundWorker, or Control.Invoke/BeginInvoke. Anonymous functions - either anonymous methods (C# 2.0) or lambda expressions (C# 3.0) make this easier than it was before.
In your case, you can change your code to:
public bool AddUser(string user, RichTextBox textBox1, List listBox1)
{
MethodInvoker action = delegate
{ textBox1.Text += "Connected to server... \n"; };
textBox1.BeginInvoke(action);
}
A few things to note:
To conform with .NET conventions, this should be called AddUser
You don't need to pass the textbox or listbox by reference. I suspect you don't quite understand what ref really means - see my article on parameter passing for more details.
The difference between Invoke and BeginInvoke is that BeginInvoke won't wait for the delegate to be called on the UI thread before it continues - so AddUser may return before the textbox has actually been updated. If you don't want that asynchronous behaviour, use Invoke.
In many samples (including some of mine!) you'll find people using Control.InvokeRequired to see whether they need to call Invoke/BeginInvoke. This is actually overkill in most cases - there's no real harm in calling Invoke/BeginInvoke even if you don't need to, and often the handler will only ever be called from a non-UI thread anyway. Omitting the check makes the code simpler.
You can also use BackgroundWorker as I mentioned before; this is particularly suited to progress bars etc, but in this case it's probably just as easy to keep your current model.
For more information on this and other threading topics, see my threading tutorial or Joe Albahari's one.

Use Invoke method
// Updates the textbox text.
private void UpdateText(string text)
{
// Set the textbox text.
yourTextBox.Text = text;
}
Now, create a delegate that has the same signature as the method that was previously defined:
public delegate void UpdateTextCallback(string text);
In your thread, you can call the Invoke method on yourTextBox, passing the delegate to call, as well as the parameters.
yourTextBox.Invoke(new UpdateTextCallback(this.UpdateText),
new object[]{”Text generated on non-UI thread.”});

Related

Is there a way to avoid using side effects to process this data

I have an application I'm writing that runs script plugins to automate what a user used to have to do manually through a serial terminal. So, I am basically implementing the serial terminal's functionality in code. One of the functions of the terminal was to send a command which kicked off the terminal receiving continuously streamed data from a device until the user pressed space bar, which would then stop the streaming of the data. While the data was streaming, the user would then set some values in another application on some other devices and watch the data streamed in the terminal change.
Now, the streamed data can take different shapes, depending on the particular command that's sent. For instance, one response may look like:
---RESPONSE HEADER---
HERE: 1
ARE: 2 SOME:3
VALUES: 4
---RESPONSE HEADER---
HERE: 5
ARE: 6 SOME:7
VALUES: 8
....
another may look like:
here are some values
in cols and rows
....
So, my idea is to have a different parser based on the command I send. So, I have done the following:
public class Terminal
{
private SerialPort port;
private IResponseHandler pollingResponseHandler;
private object locker = new object();
private List<Response1Clazz> response1;
private List<Response2Clazz> response2;
//setter omited for brevity
//get snapshot of data at any point in time while response is polling.
public List<Response1Clazz> Response1 {get { lock (locker) return new List<Response1Clazz>(response1); }
//setter omited for brevity
public List<Response2Clazz> Response2 {get { lock (locker) return new List<Response1Clazz>(response2); }
public Terminal()
{
port = new SerialPort(){/*initialize data*/}; //open port etc etc
}
void StartResponse1Polling()
{
Response1 = new List<Response1Clazz>();
Parser<List<Response1Clazz>> parser = new KeyValueParser(Response1); //parser is of type T
pollingResponseHandler = new PollingResponseHandler(parser);
//write command to start polling response 1 in a task
}
void StartResponse2Polling()
{
Response2 = new List<Response2Clazz>();
Parser<List<Response2Clazz>> parser = new RowColumnParser(Response2); //parser is of type T
pollingResponseHandler = new PollingResponseHandler(parser); // this accepts a parser of type T
//write command to start polling response 2
}
OnSerialDataReceived(object sender, Args a)
{
lock(locker){
//do some processing yada yada
//we pass in the serial data to the handler, which in turn delegates to the parser.
pollingResponseHandler.Handle(processedSerialData);
}
}
}
the caller of the class would then be something like
public class Plugin : BasePlugin
{
public override void PluginMain()
{
Terminal terminal = new Terminal();
terminal.StartResponse1Polling();
//update some other data;
Response1Clazz response = terminal.Response1;
//process response
//update more data
response = terminal.Response1;
//process response
//terminal1.StopPolling();
}
}
My question is quite general, but I'm wondering if this is the best way to handle the situation. Right now I am required to pass in an object/List that I want modified, and it's modified via a side effect. For some reason this feels a little ugly because there is really no indication in code that this is what is happening. I am purely doing it because the "Start" method is the location that knows which parser to create and which data to update. Maybe this is Kosher, but I figured it is worth asking if there is another/better way. Or at least a better way to indicate that the "Handle" method produces side effects.
Thanks!
I don't see problems in modifying List<>s that are received as a parameter. It isn't the most beautiful thing in the world but it is quite common. Sadly C# doesn't have a const modifier for parameters (compare this with C/C++, where unless you declare a parameter to be const, it is ok for the method to modify it). You only have to give the parameter a self-explaining name (like outputList), and put a comment on the method (you know, an xml-comment block, like /// <param name="outputList">This list will receive...</param>).
To give a more complete response, I would need to see the whole code. You have omitted an example of Parser and an example of Handler.
Instead I see a problem with your lock in { lock (locker) return new List<Response1Clazz>(response1); }. And it seems to be non-sense, considering that you then do Response1 = new List<Response1Clazz>();, but Response1 only has a getter.

How to use delegate to point a bunch of logs written to .txt to somewhere else? C#

I just learned about delegates and the publisher/subscriber pattern, however I have been having some problem implementing them in my current code, mainly because Im not sure what should be assign to what(I shall explain this).
I have a class, example Class A. It is a library class that contains codes that write logs into .txt file. I would like to be able to take these logs and write them somewhere else, example another .txt file/TextBox/RichTextBox.
Class A
//Just a library class for log functions
//Declare and instantiate the delegate
public void delegate myDel(string message)
public myDel customDel, customDel2
LogCategory(string category)
{
//Bunch of codes that separates the log into category Info/Warn/Error
WriteLog()
}
WriteLog()
{
StreamWriter sw = new StreamWriter(LogFilePath)
//writes logs into .txt file1
}
then in a separate class
Class B
//This is the main program where all the logs are written
public void PrintLog(string message)
{
Class A ca = new Class A();
ca.LogCategory();
}
public void delegateTheLogs()
{
//how do I use customDel to write the logs to another text file in a
//different directory
}
The idea is that delegate is suppose to:
act as a pointer
allow the program to write logs to multiple destination at the same time
The question is what do I use customDel for and how do I use it catch the logs and write them somewhere?
I think this is an interesting topic, and if anyone knows how to do this, please help me figure this out.
Oh and Im not interested in using events, I know delegate and events are pretty common to use together.
Thanks
Following on from my comment, here's an example. We have a class called FlexibleLogger that basically knows how to format stuff that it is given but it doesn't have any baked in ability to write the log data to anywhere, the idea being that the code that creates the logger also creates the routine that the logger will use to output:
public class FlexibleLogger{
Action<string> _logWriterAction;
public FlexibleLogger(Action<string> logWriterAction){
_logWriterAction = logWriterAction;
}
public Log(string message){
_logWriterAction($"{DateTime.UtcNow}: {Message}");
}
public Log(Exception ex){
Log(ex.Message);
}
}
This class doesn't know how to write a file, or console, or post the message to a web service, or email it, or put it in a rabbit queue etc.. all it knows how to do is formulate a log message provided into having a time at the start, or pull the message out of an exception(and then pass it to the method that puts a time at the start), and then call the Action (a neater way of declaring a delegate that takes arguments of various types and returns no value) passing in the message
The Action is some variable(able to be varied) method created by you
We might use it like this:
public class Program
{
public static void Main()
{
//it's a "local function", IMHO a neater way of providing a method that can be passed as an action
void consoleWriterFunc(string f){
Console.WriteLine(f);
};
//see the thing we pass as the Action parameter is a method/function,
//not a data item like a string, int, Person etc
var logger = new FlexibleLogger(consoleWriterFunc);
//log will make a string like "12-Dec-2020 12:34:56: a"
//and invoke the consoleWriterFunc, passing the string into it
//in turn it prints to the console
logger.Log("a");
//how about a logger that writes a file?
void fileWriterFunc(string f){
File.AppendAllText("c:\\temp\\some.log", f);
};
logger = new FlexibleLogger(fileWriterFunc);
logger.Log(new Exception("something bad happened"));
}
}
Doesn't have to be a local function, you can pass any method at all that takes a string and returns a void, as your Action<string>. It doesn't even have to be a method you wrote:
var sw = new System.IO.StringWriter();
var logger = new FlexibleLogger(sw.Write);
logger.Log("I'm now in the string writer" );
Microsoft wrote the method StringWriter.Write- it takes a strong, returns a void and calling logger.Log having passed the Write method of that stribgwriter instance means that the logger will Log into the stringwriter (a wrapper around a stringbuilder)
Hopefully this helps you understand that a delegate is "just a way to make a method into something you can pass as a parameter, just like anything else. They've been available for years, if you think about it, manifested as events. Microsoft have no idea what you want to do when you click a button, so they just have the button expose an event, which is really just a collection of delegates; a List of methods that the button should call when it's clicked.

WCF Callback locking up if called in same thread as current context

I hope I can word this correctly. I have a WCF Service that I'm using (duplex channel communications) in which one client registers with the service. The service's registration method returns a value. I want the the method of the called service registration method to also call the callback method that will send out notification of the client registration (I have my reasons for this and explaining it here will only confuse the issue). The problem is that the client's implemented callback has to run in the main application thread to work correctly (due mostly to integration with a third-party application). The service registration method call is also occuring in this same thread, so it effectively locks up since the client is looking for a return from the service registration method holding on to the thread preventing the callback method from being able to run. If I tell it to call all callback methods for all contexts other than the one just registered, it works just fine. But if I tell it to include it, obviously it locks up because that thread is already locked up. I can set the callback attribute property for UseSynchronizationContext to false, but then this means the callback method is called on a separate thread from the main and now the rest of the program will not work. Any help would be greatly appreciated.
Here's basically that registration method (first draft..)
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
UseSynchronizationContext = false,
ConcurrencyMode = ConcurrencyMode.Multiple,
Namespace = "http://MyApp/Design/CADServiceTypeLibrary/2012/12")]
public class DTOTransactionService : IDTOTransactionService, IDisposable
{
//some more stuff
public CADManager RegisterCADManager(int processID, bool subscribeToMessages)
{
List<CADManager> cadMgrs = this.CADManagers;
bool registered = false;
//Create new CADManager mapped to process id
CADManager regCADManager = new CADManager(processID);
//Add to CADManagers List and subscribe to messages
if (regCADManager.IsInitialized)
{
cadMgrs.Add(regCADManager);
this.CADManagers = cadMgrs;
//Subscribe to callbacks
if (subscribeToMessages)
SubscribeCallBack(regCADManager.ID);
registered = true;
}
//Send registration change notification
RegistrationState state;
if (registered)
state = RegistrationState.Registered;
else
state = RegistrationState.RegistrationException;
foreach (CallBackSubscriber subscriber in this.CallBackSubscribers)
{
subscriber.CallBackProxy.CADManagerRegistrationNotification(regCADManager.ID, state);
}
return regCADManager;
}
}
I think I've got it figured out. It struck me that a little deeper what's happening is that since the call to the service method is expecting a return value and since the callback will occur in the same thread as the client method expecting a return value that this could be the result of the deadlock condition. I then decided to try calling the callback methods in the service using a different thread to work around the current thread condition. In other words, work around the current thread whose method has yet to have provided a return value from the service method. It worked! Was this the right approach? I have enough experience to be dangerous here, so if someone else's experience shows this to be the wrong way to handle this, I'm all ears.
Thread notifyThread = new Thread(delegate()
{
foreach (CallBackSubscriber subscriber in this.CallBackSubscribers)
{
subscriber.CallBackProxy.CADManagerRegistrationNotification(regCADManager.ID, state);
}
});
Update:
Yes, the threading and deadlock condition was the issue, however the more appropriate fix I recently discovered is to use SynchronizationContext. To use, create a property or field of the type SynchronizationContext, then assign the value to the field/property while in the context you wish to capture using SynchronizationContext.Current. Then, use the Post() method (providing it a delegate via the SendOrPostCallback object) in the callback method being called by the Service. A short example:
private SynchronizationContext _appSyncContext = null;
private DTOCommunicationsService()
{
this.AppSyncContext = SynchronizationContext.Current;
//Sets up the service proxy, etc, etc
Open();
}
// Callback method
public void ClientSubscriptionNotification(string clientID, SubscriptionState subscriptionState)
{
SendOrPostCallback callback = delegate(object state)
{
object[] inputArgs = (object[])state;
string argClientID = (string)inputArgs[0];
SubscriptionState argSubState = (SubscriptionState)inputArgs[1];
//Do stuff with arguments
};
_appSyncContext.Post(callback, new object[] { clientID, subscriptionState });
}

webClient Bot - Multithreading

I'm making bot for online game.
It works, but it is singlethread application.
I want to make it multithread application.
I know how background worker works.
To all my tasks I use one WebClient with added Cookie support.
My for example needs to open one page, wait 10 min and do next instruction.
I also want to be able to stop bot at any time.
Do I have to pass my WebClient object to background worker to work with?
What is the best way to update controls on my Form?
I have one class that has all the values that I want to show on Main Form.
Should I fire some event when property changes? If yes, can you give me example?
UPDATE:
This is my Special WebClient:
using System;
using System.Net;
namespace Game_Bot
{
class WebClientEx : WebClient
{
public CookieContainer CookieContainer { get; private set; }
public WebClientEx()
{
CookieContainer = new CookieContainer();
}
public void ClearCookies()
{
CookieContainer = new CookieContainer();
}
protected override WebRequest GetWebRequest(Uri address)
{
var request = base.GetWebRequest(address);
if (request is HttpWebRequest)
{
(request as HttpWebRequest).CookieContainer = CookieContainer;
}
return request;
}
}
}
Is this good way of updating UI? Or is there any beter?
public void SetStatus(string status)
{
if (TransferLeftLabel.Dispatcher.Thread == Thread.CurrentThread)
{
TransferLeftLabel.Text = status;
}
else
{
TransferLeftLabel.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(Action)(() => { SetStatus(string status); }));
}
}
This is how I would do it:
First:
I like to manage threads manually instead of using the BackgroundWorker control when making multithreads applications like the one you want to modify.
To start a new thread, it is as simple as:
public void SomeMethod() {
var thread = new Thread(MyMethod);
thread.Start(); //Will start the method
}
public void MyMethod() {
//Do whatever you want inside the thread here
}
You can get as many Thread instances as you want, store them in a list, and manage how you prefer. However, it isn't true that the more threads the better. Search in Google.
About opening pages and keeping Cookies.
I think you could have an instance of a class in your Form, or where you have the logic (some place that threads can access), (let's name it WebUtils) with a method like: GoToUrl(<url here>) or something like that, and a CookieCollection as a field in that class to keep cookies.
Something you should take in count:
When calling GoToUrl, you might need to do lock when accessing the cookies variable, to avoid inconsistency.
About updating controls:
You can create an event inside the class WebUtils, and everytime the page is accessed you can fire this event. Before starting the threads, you must subscribe to the event in your Form, you can do something similar with lock when updating/accessing/modifying controls in your form.
Now, how to avoid the message Control ____ accessed from a thread other than the thread it was created...?
Here's an example:
If you want to modify property Text of the control textBox1, instead of just doing:
textBox1.Text = "Ey, I accessed the site
you can do:
MethodInvoker m = () => { textBox1.Text = "Ey, I accessed the site" };
if (InvokeRequired)
BeginInvoke(m);
else
m.Invoke()
Make sure all the modifications are done like that.
This is just an overview. I'm not a thread expert.
Here is good reference about threadings in general: Threading in C#
Edit:
Take a look at the IsBackground property of threads. That could be the cause of application freezes when you just want to cose it.
I suggested creating a class WebUtils, or however you want to name, because that's how I've created it in the past.
Something like:
public class WebUtils {
CookieContainer _cookies;
public WebUtils() {
_cookies = new CookieContainer();
}
public void AccessPage(string url) {
//Here I create a new instance of a HttpWebRequest class, and assign `_cookies` to its `Cookie` property.
//Don't really know if `WebClient` has something similar
}
}

What is a callback?

What's a callback and how is it implemented in C#?
I just met you,
And this is crazy,
But here's my number (delegate),
So if something happens (event),
Call me, maybe (callback)?
In computer programming, a callback is executable code that is passed as an argument to other code.
—Wikipedia: Callback (computer science)
C# has delegates for that purpose. They are heavily used with events, as an event can automatically invoke a number of attached delegates (event handlers).
A callback is a function that will be called when a process is done executing a specific task.
The usage of a callback is usually in asynchronous logic.
To create a callback in C#, you need to store a function address inside a variable. This is achieved using a delegate or the new lambda semantic Func or Action.
public delegate void WorkCompletedCallBack(string result);
public void DoWork(WorkCompletedCallBack callback)
{
callback("Hello world");
}
public void Test()
{
WorkCompletedCallBack callback = TestCallBack; // Notice that I am referencing a method without its parameter
DoWork(callback);
}
public void TestCallBack(string result)
{
Console.WriteLine(result);
}
In today C#, this could be done using lambda like:
public void DoWork(Action<string> callback)
{
callback("Hello world");
}
public void Test()
{
DoWork((result) => Console.WriteLine(result));
DoWork(Console.WriteLine); // This also works
}
Definition
A callback is executable code that
is passed as an argument to other code.
Implementation
// Parent can Read
public class Parent
{
public string Read(){ /*reads here*/ };
}
// Child need Info
public class Child
{
private string information;
// declare a Delegate
delegate string GetInfo();
// use an instance of the declared Delegate
public GetInfo GetMeInformation;
public void ObtainInfo()
{
// Child will use the Parent capabilities via the Delegate
information = GetMeInformation();
}
}
Usage
Parent Peter = new Parent();
Child Johny = new Child();
// Tell Johny from where to obtain info
Johny.GetMeInformation = Peter.Read;
Johny.ObtainInfo(); // here Johny 'asks' Peter to read
Links
more details for C#.
A callback is a function pointer that you pass in to another function. The function you are calling will 'callback' (execute) the other function when it has completed.
Check out this link.
If you referring to ASP.Net callbacks:
In the default model for ASP.NET Web
pages, the user interacts with a page
and clicks a button or performs some
other action that results in a
postback. The page and its controls
are re-created, the page code runs on
the server, and a new version of the
page is rendered to the browser.
However, in some situations, it is
useful to run server code from the
client without performing a postback.
If the client script in the page is
maintaining some state information
(for example, local variable values),
posting the page and getting a new
copy of it destroys that state.
Additionally, page postbacks introduce
processing overhead that can decrease
performance and force the user to wait
for the page to be processed and
re-created.
To avoid losing client state and not
incur the processing overhead of a
server roundtrip, you can code an
ASP.NET Web page so that it can
perform client callbacks. In a client
callback, a client-script function
sends a request to an ASP.NET Web
page. The Web page runs a modified
version of its normal life cycle. The
page is initiated and its controls and
other members are created, and then a
specially marked method is invoked.
The method performs the processing
that you have coded and then returns a
value to the browser that can be read
by another client script function.
Throughout this process, the page is
live in the browser.
Source: http://msdn.microsoft.com/en-us/library/ms178208.aspx
If you are referring to callbacks in code:
Callbacks are often delegates to methods that are called when the specific operation has completed or performs a sub-action. You'll often find them in asynchronous operations. It is a programming principle that you can find in almost every coding language.
More info here: http://msdn.microsoft.com/en-us/library/ms173172.aspx
Dedication to LightStriker:
Sample Code:
class CallBackExample
{
public delegate void MyNumber();
public static void CallMeBack()
{
Console.WriteLine("He/She is calling you. Pick your phone!:)");
Console.Read();
}
public static void MetYourCrush(MyNumber number)
{
int j;
Console.WriteLine("is she/he interested 0/1?:");
var i = Console.ReadLine();
if (int.TryParse(i, out j))
{
var interested = (j == 0) ? false : true;
if (interested)//event
{
//call his/her number
number();
}
else
{
Console.WriteLine("Nothing happened! :(");
Console.Read();
}
}
}
static void Main(string[] args)
{
MyNumber number = Program.CallMeBack;
Console.WriteLine("You have just met your crush and given your number");
MetYourCrush(number);
Console.Read();
Console.Read();
}
}
Code Explanation:
I created the code to implement the funny explanation provided by LightStriker in the above one of the replies. We are passing delegate (number) to a method (MetYourCrush). If the Interested (event) occurs in the method (MetYourCrush) then it will call the delegate (number) which was holding the reference of CallMeBack method. So, the CallMeBack method will be called. Basically, we are passing delegate to call the callback method.
Please let me know if you have any questions.
Probably not the dictionary definition, but a callback usually refers to a function, which is external to a particular object, being stored and then called upon a specific event.
An example might be when a UI button is created, it stores a reference to a function which performs an action. The action is handled by a different part of the code but when the button is pressed, the callback is called and this invokes the action to perform.
C#, rather than use the term 'callback' uses 'events' and 'delegates' and you can find out more about delegates here.
callback work steps:
1) we have to implement ICallbackEventHandler Interface
2) Register the client script :
String cbReference = Page.ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context");
String callbackScript = "function UseCallBack(arg, context)" + "{ " + cbReference + ";}";
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "UseCallBack", callbackScript, true);
1) from UI call Onclient click call javascript function for EX:- builpopup(p1,p2,p3...)
var finalfield= p1,p2,p3;
UseCallBack(finalfield, ""); data from the client passed to server side by using UseCallBack
2) public void RaiseCallbackEvent(string eventArgument) In eventArgument we get the passed data
//do some server side operation and passed to "callbackResult"
3) GetCallbackResult() // using this method data will be passed to client(ReceiveServerData() function) side
callbackResult
4) Get the data at client side:
ReceiveServerData(text) , in text server response , we wil get.
A callback is a function passed as an argument to another function. This technique allows a function to invoke the parameter function argument and even to pass a value back to the caller. A callback function can be designed to run before/after the function has finished and can pass a value.
It is a kind of construct where you call a long running function and ask him to call you back once it has finished with can return a parameter result to the caller.
It's like someone calls you in the middle of your work asking for status and you say "you know what give me 5 min and i will call you back" and at the end you call him to update. If you are a function the caller just added and passed another function that you invoked at the end. This can simpley be written in C# as:
public void VinodSrivastav(Action statusUpdate){
//i am still here working..working
//i have finished, calling you
statusUpdate();
}
//invokes
stackoverflow.VinodSrivastav((cam) => {
Console.Write("Is it finished");
});
The one simple example is the iterator function where the return will be multiple times, one can argue that we have yield for it:
public void IntreationLoop(int min, int max,Action<int> Callback)
{
for(int i = min;i<= max;i++)
Callback(i);
}
//call
IntreationLoop(5,50,(x) => { Console.Write(x); }); //will print 5-50 numbers
In the code above the function return type is void but it has an Action<int> callback which is called and sends each item from the loop to the caller.
The same thing can be done with if..else or try..catch block as:
public void TryCatch(Action tryFor,Action catchIt)
{
try{
tryFor();
}
catch(Exception ex)
{
Console.WriteLine($"[{ex.HResult}] {ex.Message}");
catchIt();
}
}
And call it as:
TryCatch(()=>{
int r = 44;
Console.WriteLine("Throwing Exception");
throw new Exception("something is wrong here");
}, ()=>{
Console.WriteLine("It was a mistake, will not try again");
});
In 2022 we have Func & Action doing the same, please see the demo code below which shows how this can be be used:
void Main()
{
var demo = new CallbackDemo();
demo.DoWork(()=> { Console.WriteLine("I have finished the work"); });
demo.DoWork((r)=> { Console.WriteLine($"I have finished the work here is the result {r}"); });
demo.DoWork(()=> { Console.WriteLine($"This is passed with func"); return 5;});
demo.DoWork((f)=> { Console.WriteLine($"This is passed with func and result is {f}"); return 10;});
}
// Define other methods and classes here
public class CallbackDemo
{
public void DoWork(Action actionNoParameter)
{
int a = 5;
int b = 10;
//i will do th maths and call you back
int result = a + b;
//callback
actionNoParameter(); //execute
Console.WriteLine($"[The Actual Result is {result}]");
}
public void DoWork(Action<int> actionWithParameter)
{
int a = 5;
int b = 10;
//i will do th maths and call you back
int result = a + b;
//callback
actionWithParameter(result); //execute
Console.WriteLine($"[The Actual Result is {result}]");
}
public void DoWork(Func<int> funcWithReturn)
{
int a = 5;
int b = 10;
//i will do th maths and call you back
int result = a + b;
//callback
int c = funcWithReturn(); //execute
result += c;
Console.WriteLine($"[The Actual Result is {result}]");
}
public void DoWork(Func<int,int> funcWithParameter)
{
int a = 5;
int b = 10;
//i will do th maths and call you back
int result = a + b;
//callback
result += funcWithParameter(result); //execute
Console.WriteLine($"[The Actual Result is {result}]");
}
}

Categories