Hi i have some question related to call back feature in libraries created by user in c#
i have created a winform application named "Sample"
i have also created a class library named "Library"
Sample contains only one form that has a button say "CALL"
i have implemented all the coding part in a library
when i click on the call button on form then a method "ACTIVATE CALL" of the library is called.
this method performs some work on a thread.
What i want is when thread work if finished then "CALLBACK" method placed in my winform must be called.
To achieve this i have passed "this" reference of the form to the library
i collected "this" as obj "Object" type in formal arguement in library.
can anybody suggest me how to call callback method?
i tried this:
if(obj.GetType()== typeOf(what to specify here))
{
obj.callback();
}
hope somebody can provide me help.
note: both library and sample are different projects
how to achieve callback feature?
Define your library method with a callback.
public void ACTIVATE(object arg, object arg, Action callback)
{
// Do what you have to do here.
callback.Invoke();
}
Then, in your Sample WinForms client you can call something like this.
public void MethodInSample()
{
Library lib = new Library();
Action callback = () => { DoSomethingHere };
Lib.ACTIVATE(1,1,callback);
}
If you want the callback to return some parameters, you could use a Func<> instead and define the arguments that way.
Alternatively, you could use an event. The BackgroundWorker object is a good example of this. This class has a method called RunWorkerAsync(), which causes some work to be done on a background thread. There is then an event called RunWorkerCompleted which you listen on to indicate when the background thread has completed.
Both methods are valid I think, but the second has the benefit of allowing more than one party to listen for completion of the work.
Have the user of the ActivateCall function supply a callback so in you library:
function void ActivateCall(Action callback){
//Do Stuff
if (null != callback){
callback();
}
}
and then in your main form:
function button1_Click(object sender, EventArgs e){
library.ActivateCall(DoStuff);
}
There are a number of things to look out for though since you say you are doing stuff in a separate thread within the library call. If you are altering the GUI at all in the callback you will need to make sure you do the work in the GUI thread. You will also need to make sure you run the callback once all the work in the thread has been completed (I suspect).
To make sure your callback is run in the GUI thread (if required) do something like this:
function button1_Click(object sender, EventArgs e){
library.ActivateCall(DoStuff());
}
function void DoStuff(){
if (InvokeRequired(){
Invoke(DoStuff);
return;
}
//Do stuff here....
}
Finally i achieved this using Delegate+Event
*****************Sample class**************************
call()
{
//activate method of library is called
libraryObject.stop += new LibraryClass.callback(setCallbackMethod);
libraryObject.activate();
}
public void setCallbackMethod(String str)
{
// most important to be back on main thread
this.Invoke((MethodInvoker)delegate
{
btn.Enabled = true;
});
}
*******************Library***************************
public delegate void callback(String str);
public event callback stop;
activate()
{
//instantiates a thread/timer
aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(CheckForMessage);
aTimer.Interval = 1000;
aTimer.Start();
}
public void CheckForMessage(object source, ElapsedEventArgs e)
{
//performs some work
//calls callback method of ui thread in sample code
if (stop != null)
{
stop("COMPLETED");
}
}
Related
I have an application with a gui and a Rich Text Box where I output what the program is currently doing since data processing can be quite long.
I tried two approaches for that:
1 In the Backgroundworker method I can just call the following code fine:
GlobalVar.backgroundWorkerAppendText = task.Build_CSV_List();
Processchange();
Whereas I cannot use Form1.Processchange(); in the helper class due to the non static context
2 Therefore I tried to create my very first eventhandler.
The Idea was that helper.UpdateConsole() would raise an event
public event EventHandler OnConsoleUpdate;
public void Consoleupdate()
{
OnConsoleUpdate(this, EventArgs.Empty);
}
to which the Backgroundworker listens and then calls Processchange from its context
public void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
StandardTasks task = new StandardTasks();
Helper helper = new Helper();
helper.OnConsoleUpdate += Processchange;
task.DoSomeStuffHere()
}
public void Processchange(object sender=null, EventArgs e=null)
{
//MessageBox.Show(GlobalVar.backgroundWorkerAppendText);
GlobalVar.next = false;
backgroundWorker1.ReportProgress(1);
while (GlobalVar.next == false)
{
helper.TimeBreaker(100,"ms");
}
}
Unfortunately this was was not successful. As soon as rising the Event I get the errormessage System.NullReferenceException which -after googling- leads me to the conclusion that there is no listerner attached to the event eventhouh I attached it in the Backgroundworker Do work.
Edit: the OnConsoleUpdate() == null as shown on the screenshot below
event = null
The helper is in another class file "helpers" which might be important for a solution.
i hope you guys can help me out.
Welcome to SO!
A few things immediately jump to mind.
First, let's get the event issue out of the way. You've got the correct approach - you need an event and method to call it, but that method should check if the event is null.
Basically, do this:
public event EventHandler OnConsoleUpdate;
public void ConsoleUpdate()
{
OnConsoleUpdate?.Invoke(this, EventArgs.Empty);
}
The above makes use of ?, a null-condition operator. You can read more about it on this MSDN page.
Second thing... it's unclear what your background worker actually IS. It sounds like it's some kind of custom class you crated? The reason it's important is because .NET actually has a BackgroundWorker class used for running operations... well, in the background. It also has an OnProgressChanged event which you can hook up to which could be used to update the UI (just remember to set the WorkerReportsProgress property to true). And to use the BackgroundWorker mentioned above, you shouldn't need to create any events of your own.
Here's how you can use the standard .NET BackgroundWorker:
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
void StartBackgroundTask()
{
worker.DoWork += worker_DoWork;
//if it's possible to display progress, use this
worker.WorkerReportsProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
//what to do when the method finishes?
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
//start!
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
//perform any "finalization" operations, like re-enable disabled buttons
//display the result using the data in e.Result
//this code will be running in the UI thread
}
//example of a container class to pass more data in the ReportProgress event
public class ProgressData
{
public string OperationDescription { get; set; }
public int CurrentResult { get; set; }
//feel free to add more stuff here
}
void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
//display the progress using e.ProgressPercentage or e.UserState
//this code will be running in the UI thread
//UserState can be ANYTHING:
//var data = (ProgressData)e.UserState;
}
void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//this code will NOT be running in the UI thread!
//you should NOT call the UI thread from this method
int result = 1;
//perform calculations
for (var i = 1; i <= 10; i++)
{
worker.ReportProgress(i, new ProgressData(){ OperationDescription = "CustomState passed as second, optional parameter", CurrentResult = result });
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
result *= i;
}
e.Result = result;
}
Now, the thing about the BackgroundWorker class is that it is rather old, and with current .NET versions you can use the async / await keywords to easily handle background operations and UI updates, but this probably is going outside the bounds of this question. That said, the existence of async / await doesn't invalidate the use of BackgroundWorker which is pretty simple in its usage.
There's one more worrisome thing in your code.
public void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
StandardTasks task = new StandardTasks(); //<- you create a task
Helper helper = new Helper(); // <- you create a helper
helper.OnConsoleUpdate += Processchange; // <- you hook up to the helper event
task.DoSomeStuffHere(); // <- you do stuff with the task... but the task doesn't know about your helper above! Does `StandardTasks` use `Helper`? If so, how?
}
Do note that events, unless made static, aren't global. So hooking up to an event in one instance of a class won't cause another instance of that class to "fire" that event. It seems one way to fix your issues would be to make the StandardTasks class take Helper as one of the constructor parameters, so the code would look like this:
Helper helper = new Helper(); // <- you create a helper
helper.OnConsoleUpdate += Processchange; // <- you hook up to the helper class event to actually do something
StandardTasks task = new StandardTasks(helper); //<- you create a task which will use the helper with the hooked up event above
Presently, I have a windows form that is receiving data from a named pipe asynchronously. To avoid getting the "Cross thread operation not valid: Control 'myTextBox' accessed from a thread other than the thread it was created on" I am using an anonymous method (see http://www.codeproject.com/Articles/28485/Beginners-Guide-to-Threading-in-NET-Part-of-n):
// Pass message back to calling form
if (this.InvokeRequired)
{
// Create and invoke an anonymous method
this.Invoke(new EventHandler(delegate
{
myTextBox.Text = stringData;
}));
}
else
myTextBox.Text = stringData;
My question is, what does the "new EventHandler(delegate" line do? Does it create a delegate of a delegate? Could someone please explain, how would I implement the above functionality using a named delegate instead (just to help with understanding it)? TIA.
If you have a C++ background, I'd describe delegates as simple pointers to functions. Delegates are .NET's way of safely handling function pointers.
To used a named delegate, you first have to create a function to handle the event:
void MyHandler(object sender, EventArgs e)
{
//Do what you want here
}
Then, for your previous code, change it to this:
this.Invoke(new EventHandler(MyHandler), this, EventArgs.Empty);
If I were doing this though, I'd write it like this to avoid duplicate code.
EventHandler handler = (sender, e) => myTextBox.Test = stringData;
if (this.InvokeRequired)
{
this.Invoke(handler, this, EventArgs.Empty); //Invoke the handler on the UI thread
}
else
{
handler(this, EventArgs.Empty); //Invoke the handler on this thread, since we're already on the UI thread.
}
I have little windows forms program that plays music using console.beep from strings. I have the string playing bit (which is basically a for loop that goes through the strings char by char and plays the appropriate note) set up in a new thread 't'. When you hit the "play" button the thread starts and the button changes to a "stop" button. If you hit this now "stop" button while the music is playing it will stop and the button changes back to play (by calling the "finished" method.
My problem is that I want the loop running in the new thread to also call the "finished" method when the loop has run its course and the song is over.
But if i put finished() after my loop i get the "object reference is required for the non static field" error. If I change the "finshed" method to static then the bit that changes the button text doesnt work...
Heres the code for when you press the button...
//This is the method for when the "start" button is clicked
public void button1_Click(object sender, EventArgs e)
{
if (music == null) { return; }
if (button1.Text == "Play")
{
// it makes a new thread which calls my "parse" method which
// interprets the music and then calls "playnote" to play it.
Thread t = new Thread(parse);
t.Start();
button1.Text = "Stop";
}
else
{
finished();
}
}
public void finished()
{
stop = true;
button1.Text = "Play";
}
Any suggestions?
Thanks a lot in advance!
Edit: Thanks all!! I dont really have time to figure out the background worker atm so I just have seperate start and stop buttons now! :p
I think this would be better accomplished with a BackgroundWorker thread. That way you can call the finished() method in the RunWorkerCompleted event.
Is the parse() method static? It seems like you are trying to call the finished() from a static method without providing the instance, which is why you are getting the "object reference is required for the non static field" error.
What you need to do is make sure that the created thread has access to the object that created it (i.e. "this", in the scope of your "button1_Click()" method). To do this, you can call t.Start(this) instead of t.Start(). In doing so, you will be passing to the thread the object capable of calling the "finished()" method.
Next, you need to make sure the "parse" method takes an argument of type Object. In doing so, when you call "t.Start(this)", the method "parse" will receive "this" as a parameter. A simple cast will be required to convert to the appropriate type. Now, when you want the song to be stopped in the thread, simply call the "finish()" method on the casted parameter.
So your code should look something like:
public void parse(object o)
{
MyClass c = o as MyClass; // replace MyClass with the name of your class
[...] // play music here
c.finished(); // call finished when you are done
}
public void button1_Click(object sender, EventArgs e)
{
[...]
Thread t = new Thread(parse);
t.Start(this); // instead of t.Start()
[...]
}
Static versus member method
Either you have a member (non static) method and you call it
obj.SomeMethod();
Or you have a static method and you call it with
ClassName.SomeMethod();
unless it is called inside the defining class itself. Then it can simply be called with
SomeMethod();
in both cases.
Threading
The only thread which is allowed to interact with the UI (e.g. with button1.Text) is the UI-thread itself. The UI-thread is the main thread in which your code is normally running.
See How to update the GUI from another thread in C#? for interacting with the form from another thread.
What you need to do is raise an event on the main thread to call your finished method.
Start by declaring the event and delegate in your form class.
public delegate void CallFinishedEventHandler();
public event CallFinishedEventHandler CallFinished;
Then create an event handler that will be called when the event is raised, and in the make your call to your finished method.
void Form1_CallFinished()
{
Finished();
}
Then in your form constructor wire up your event handler to your event.
public Form1()
{
CallFinished += Form1_CallFinished;
}
Finally, in your music playing code, when you want to call your finished method (on the UI thread) invoke your event so that it will be fired on the UI thread.
this.Invoke(CallFinished);
This is simple, create a global thread, and then access it where you want.
Thread t;
public void button1_Click(object sender, EventArgs e)
{
if (music == null) { return; }
if (button1.Text == "Play")
{
// it makes a new thread which calls my "parse" method which
// interprets the music and then calls "playnote" to play it.
t = new Thread(parse);
t.Start();
button1.Text = "Stop";
}
else
{
finished();
}
}
public void finished()
{
stop = true;
button1.Text = "Play";
}
for calling finished, make an instance of your form class and then call the method.
Form1 frm1 = new Form1();
frm1.finished();
Problem:
I am working on a application where in for some time consuming operation, i am supposed to show a progress bar on a form (WinForm) with a cancel button. So obviously i am using BackgroundWorker thread for it. Below is the code which simulates roughly of what i am trying to achieve.
namespace WindowsFormsApplication1
{
public delegate void SomeDelegateHandler();
public partial class Form1 : Form
{
public event SomeDelegateHandler DoSomeAction;
BackgroundWorker bgWorker;
public Form1()
{
InitializeComponent();
bgWorker = new BackgroundWorker();
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
//Some logic code here.
for (int i = 0; i < 100; i++)
{
DoSomeAction();
}
}
private void Form1_Shown(object sender, EventArgs e)
{
if (DoSomeAction != null)
bgWorker.RunWorkerAsync();
else throw new EventNotSubscribedException();//Is this a valid style??
}
}
public class EventNotSubscribedException : ApplicationException
{
//Some custom code here
}
}
My Solution
As per the above code, as soon as the form is displayed to the user (OnShown event) i am starting the backgroundworker thread. This is because, the user need not to initiate any action for this to happen. So onshown does time consuming operation job. But the issue is, as i have shown above, the main time consuming job is executed on other class/component where it is kind of tight bounded too (legacy code: cant refactor). Hence i have subscribed to the event DoSomeAction in that legacy code class which launches this form.
Doubt/Question:
Is it valid to throw exception as shown above? (Please read my justification below).
Justification:
The OnShown event does check for null on event handler object. This is because, to make this form usable, the event has to be subscribed by the subscriber (usage code), then only it shall work. If not, then the form just displays and does noting at all and usage code may not know why it is happenings so. The usage code may assume that subscribing to the event is option just like button click events per say.
Hope my post is clear and understandable.
Thanks & Happy Coding,
Zen :)
Do you mean that you need to throw an exception to the caller of the form? Is it called using showDialog or Show?
BTW, I dont prefer to generate an exception from an event. Rather it would be rather nice to keep it such that it returns from the place with some status set on the Form class.
for instance, I would prefer using
IsEventSubscribed = false
this.Close()
rather than EventNotSubscribedException
BTW, One problem I can see in the code, when the bgWorker_DoWork is called, you should check DoSomeAction to null, because otherwise it might cause NullReferenceException.
Preferably,
Start the run the RunWorkerAsync from Form_shown
Check Delegate to null in DoWork, if it is null, do not call DoSomeAction otherwise call it.
On RunWorkerCompleted of the BackgroundWorker, close the form.
Let me know if you need anything more.
I would suggest making the consuming code construct the BackgroundWorker and pass it to the form's constructor. You can do a null test in the constructor and side-step this whole issue. Alternatively, take the delegate as a constructor argument instead. I mean, how likely is it that the consuming code will need to change the worker delegate mid-operation?
Another approach is to have the dialog monitor a task, instead of having a dialog control a task (as you have here). For example, you could have an interface like this:
public interface IMonitorableTask {
void Start();
event EventHandler<TData> TaskProgress;
}
Where TData is a type that provides any information you might need to update the dialog (such as percent completed).
The downside to this is that each task needs to be a type of its own. This can lead to very ugly, cluttered code. You could mitigate that issue somewhat by creating a helper class, something like:
public class DelegateTask : IMonitorableTask {
private Action<Action<TData>> taskDelegate;
public event EventHandler<TData> TaskProgress;
public DelegateTask(Action<Action<TData>> taskDelegate) {
if (taskDelegate == null)
throw new ArgumentNullException("taskDelegate");
this.taskDelegate = taskDelegate;
}
protected void FireTaskProgress(TData data) {
var handler = TaskProgress;
if (handler != null)
handler(this, data);
}
public void Start() {
taskDelegate(FireTaskProgress);
}
}
Then your task methods become factories:
public IMonitorableTask CreateFooTask(object argument) {
return new DelegateTask(progress => {
DoStuffWith(argument);
progress(new TData(0.5));
DoMoreStuffWith(argument);
progress(new TData(1));
});
}
And now you can easily(*) support, say, a command-line interface. Just attach a different monitor object to the task's event.
(*) Depending on how clean your UI/logic separation already is, of course.
im new to C# Event and i want to fire an event without getting a Cross-Thread error..
using System;
using System.Timers;
public class SampleTickEvent
{
private string passStr = string.Empty;
Timer t = new Timer(1000);
public delegate void ImageEventHandler(string s);
public event ImageEventHandler ImageEventTrigger;
public void Start(string ss)
{
passStr = ss;
t.Start();
t.Elapsed += t_Elapsed;
}
public void t_Elapsed(object sender, ElapsedEventArgs eea)
{
ImageEventTrigger(passStr);
}
}
private void button1_Click(object sender, EventArgs e)
{
SampleTickEvent ste = new SampleTickEvent();
ste.Start("sample");
ste.ImageEventTrigger += ste_ImageEventTrigger;
}
private void ste_ImageEventTrigger(string s)
{
Action act = () => listBox1.Items.Add(s);
Invoke(act);
}
is there another way that i will not put the Action act = () = ... and put listbox1.Items.Add(s) instead?
Rather than using System.Timers.Timer, trying using System.Windows.Forms.Timer, which is written so that it raises the event on the UI thread.
If you're in Windows Forms, you can use System.Windows.Forms.Timer instead of System.Timers.Timer. The tick event executes in the UI Thread : http://netpl.blogspot.com/2010/05/systemwindowsformstimer-vs.html
No, Timers operate on a thread from the thread pool, and so to safely modify your form controls, you need to use Invoke.
You could inline the action, but that's about it.
If you want to put the Invoke into the SampleTickEvent.t_Elapsed method, then you'll have to pass a Control as a handle into the SampleTickEvent first. Or, you could just create a new Control as a member of the the SampleTickEvent in its constructor, and call Invoke on that. I've done it that way before.
You could change this option during application startup:
System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
This would go in whichever Form you're using as the main application thread.
Try using the System.Windows.Forms.Timer instead of System.Timers.Timer.
From above Msdn Link:
This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread.