C# Making a delegate available to a class - c#

I would like to make a delegate available to an entire class. The point of this is to allow a called method from an external class' backgroundWorker to continually report back through all of it's methods (ExternalClass.Run(); calls ExternalClass.Method2(); ExternalClass.Method3(); etc and they all need to send several progress reports. It seems inefficient to have to continually pass the delegate.
I've tried initializing an instance of the delegate globally and setting it to equal the passed instance in Run(); for each method to then have available to it but I am given an error that a null object cannot be implicitly converted.
thanks!
I cannot show the code I am working with as I do not currently have it with me (it's on my laptop) but I will try to better explain now. PSEUDO-CODE:
class form1 : form {
backgroundWorker_doWork()
{
Class2.Run();
}
backgroundWorker_OnProgressChange()
{
// do this
}
}
class class2{
Run(){
OtherMethod();ThirdMethod();
}
OtherMethod(){ //need to call backgroundWorker.ReportProcess(int, string)}
ThirdMethod(){ //need to call backgroundWorker.ReportProcess(int, string)}
}
I really don't want to have to pass it every time is the point, i'd like to somehow pass it to class2

You should show your code that isn't working and the exact error message. It should be fine - here's an example:
using System;
class Demo
{
private readonly Action action;
public Demo(Action action)
{
this.action = action;
}
public void FirstMethod()
{
Console.WriteLine("In first method");
action();
}
public void SecondMethod()
{
Console.WriteLine("In second method");
action();
}
}
class Test
{
static void Main()
{
Demo demo = new Demo(() => Console.WriteLine("Action called"));
demo.FirstMethod();
demo.SecondMethod();
}
}

You can use the InvokeMethod function from a backgroundWorker to allow the worker to execute any delegate, example below (also waits for the invoke to finish, which you may not need):
BackgroundWorker Function (C++.net)
BackgroundWorkerFunction()
{
::IAsyncResult ^ThreadResult;
SetTileCount_Delegate ^SetCountDel = gcnew SetTileCount_Delegate(this, &PartDetail::SetTileCount_Function);
//RecordingContainer is the class I am invoking into
ThreadResult = this->RecordingContainer->BeginInvoke(
SetCountDel, ThisTest->RecordingsCache->Count);
WaitForInvokeTimeOutOrCompletion(ThreadResult);
}
System::Void WaitForInvokeTimeOutOrCompletion(IAsyncResult ^ThreadResult)
{
if(ThreadResult == nullptr) return;
long SleepTotal = 0;
long SleepInterval = 100;
while ((SleepTotal <= 2000) && !ThreadResult->IsCompleted)
{
ThreadResult->AsyncWaitHandle->WaitOne(SleepInterval, false);
SleepTotal += SleepInterval;
}
}

Related

Access statusbar on form from cplex callback function c#

I'm working with an C# .Net application that uses Cplex DLL's for an optimization operation, and during that operation I want to write status progress to a statusbar on the that initiated the operation.
This is the general layout of the specific form;
namespace ActResMain
{
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
//...
cplex.Use(new Cplex_ContinuousCallback());
cplex.Solve()
}
public void Update_OptimizeStatusbarPanel(String strText)
{
statusBarPanel_1.Text = strText;
statusBar1.Refresh();
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize formOpt = new FormOptimize();
public override void Main()
{
//From here I want to edit the statusbar at FormOptimize. I can write progress to console without any problems, but cannot reach function "Update_OptimizeStatusbarPanel".
//If I include "FormOptimize formOpt = new FormOptimize" here, i get Visual studio exception on illegal window reference.
}
}
}
}
I have also tried invoking the Update_OptimizeStatusbarPanel function like this:
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize formOpt = new FormOptimize();
public override void Main()
{
FormCollection fc = Application.OpenForms;
var mpc = fc[1];
Type type = mpc.GetType();
MethodInfo dynMethod = type.GetMethod("Update_OptimizeStatusbarPanel");
dynMethod.Invoke(mpc, new object[] { String.Format("Running Optimization: {0} iterations ", Niterations)});
}
}
But then I get an exception from visual studio stating that an object created by one thread cannot be modified from another thread.
Maybe this is something stupid that I have missed, but help is greatly appriciated
EDIT: I edited the code as per Mohammad Dehghans suggestion,
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
cplex.Use(new Cplex_ContinuousCallback(this));
cplex.Solve()
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize _formOptimize;
public Cplex_ContinuousCallback(FormOptimize formOptimize)
{
this._formOptimize = formOptimize;
}
public override void Main()
{
if (Niterations % 10 == 0)
{
_formOptimize.Update_OptimizeStatusbarPanel(0, String.Format("Running Optimization: {0} iterations ", Niterations), 0);
}
}
}
public void Update_OptimizeStatusbarPanel(short panelIndex, String strText, short severity)
{
if (statusBar1.InvokeRequired)
statusBar1.Invoke(new Action<short, string, short>(Update_OptimizeStatusbarPanel), panelIndex, strText, severity);
else
{
if (panelIndex == 0)
{
//...
statusBarPanel_0.Text = strText;
}
else if (panelIndex == 1)
{
//...
statusBarPanel_1.Text = strText;
}
statusBar1.Refresh();
}
}
}
But by doing that I apparently broke something, as the application just ..stops after statusBar1.Invoke() is called the first time. If I pause the debugger it says that cplex.Solve() is executing, but then nothing more happens.
First of all, you need to pass the instance of your form to the implemented callback class, so when the Main method is called, you have access to the exact instance that is being shown on the screen.
Secondly, you need to use Invoke method to update the UI controls from anther thread (I've not worked with CPLEX so far, but I guess the callback is invoked from another thread. That's usual).
Read this for more information.
The complete code could be:
public class FormOptimize : System.Windows.Forms.Form
{
private callCplex()
{
//Misc code
cplex.Use(new Cplex_ContinuousCallback(this)); // <-- passing `this`
cplex.Solve()
//Misc code
}
public void Update_OptimizeStatusbarPanel(String strText)
{
if (statusBarPanel_1.InvokeRequired)
statusBarPanel_1.Invoke(Action<string>(Update_OptimizeStatusbarPanel), strText);
else
{
statusBarPanel_1.Text = strText;
statusBar1.Refresh();
}
}
internal class Cplex_ContinuousCallback : Cplex.ContinuousCallback
{
FormOptimize _formOptimize;
public Cplex_ContinuousCallback(FormOptimize formOptimize)
{
this._formOptimize = formOptimize;
}
public override void Main()
{
//...
_formOptimize.Update_OptimizeStatusbarPanel(String.Format("Running Optimization: {0} iterations ", Niterations));
}
}
}

How do you call a function in a class that works in another thread?

I'm working on a class that looks like this:
public class MatchmakingService {
private bool working;
private List<MatchmakingUser> matchmakingUsers;
// ...
public MatchmakingService()
{
matchmakingUsers = new List<MatchmakingUser>();
}
public void StartService () {
var thread = new Thread(this.MatchmakingWork);
working = true;
thread.Start();
}
void MatchmakingWork () {
while (working)
{
// some work
Thread.Sleep(1000);
}
}
// ...
public void AddMatchmakingUser(MatchmakingUser user)
{
matchmakingUsers.Add(user);
}
}
Now I'm worried about matchmakingUsers list so I figured I'd just call this AddMatchmakingUser to execute in this service thread, but don't really know how to. I've read about Dispatcher class but either mono in Unity doesn't have it or it's a different technology entirely. Basically I'd like to do:
MatchmakingService mmService = new MatchmakingService();
mmService.Start();
// sometime later when needed
mmService.Somehowinvokeinworkingthread(mmService.AddMatchMakingUser(...));
Just call AddMatchmakingUser from any thread and synchronize all the code that's accesing the list to prevent them from occuring at the same time and causing a race condition:
public void AddMatchmakingUser(MatchmakingUser user)
{
lock (matchmakingUsers)
{
matchmakingUsers.Add(user);
}
}
Do the same inside MatchmakingWork whenever you are accessing the list.

implementing delegates in c#

This would be the first time I'd use delegates in c# so please bear with me. I've read a lot about them but never thought of how/why to use this construct until now.
I have some code that looks like this:
public class DoWork()
{
public MethodWorkA(List<long> TheList) {}
public void MethodWork1(parameters) {}
public void MethodWork2(parameters) {}
}
I call MethodWorkA from a method outside the class and MethodWorkA calls MethodWork 1 and 2. When I call methodA, I'd like to pass some sort of parameter so that sometimes it just does MethodWork1 and sometimes it does both MethodWork1 and MethodWork2.
So when I call the call it looks like this:
DoWork MyClass = new DoWork();
MyClass.MethodA...
Where does the delegate syntax fit in this?
Thanks.
public void MethodWorkA(Action<ParamType1, ParamType2> method) {
method(...);
}
You can call it using method group conversion:
MethodWorkA(someInstance.Method1);
You can also create a multicast delegate that calls two methods:
MethodWorkA(someInstance.Method1 + someInstance.Method2);
For what you described, you don't need delegates.
Just do something like this:
public class DoWork
{
public void MethodWorkA(List<long> theList, bool both)
{
if (both)
{
MethodWork1(1);
MethodWork2(1);
}
else MethodWork1(1);
}
public void MethodWork1(int parameters) { }
public void MethodWork2(int parameters) { }
}
If you're just experimenting with delegates, here goes:
public partial class Form1 : Form
{
Func<string, string> doThis;
public Form1()
{
InitializeComponent();
Shown += Form1_Shown;
}
void Form1_Shown(object sender, EventArgs e)
{
doThis = do1;
Text = doThis("a");
doThis = do2;
Text = doThis("a");
}
string do1(string s)
{
MessageBox.Show(s);
return "1";
}
string do2(string s)
{
MessageBox.Show(s);
return "2";
}
}
Considering that all methods are inside the same class, and you call MethodWorkA function using an instance of the class, I honestly, don't see any reason in using Action<T> or delegate, as is I understood your question.
When I call methodA, I'd like to pass some sort of parameter so that
sometimes it just does MethodWork1 and sometimes it does both
MethodWork1 and MethodWork2.
Why do not just pass a simple parameter to MethodWorkA, like
public class DoWork()
{
public enum ExecutionSequence {CallMethod1, CallMethod2, CallBoth};
public MethodWorkA(List<long> TheList, ExecutionSequence exec)
{
if(exec == ExecutionSequence.CallMethod1)
MethodWork1(..);
else if(exec == ExecutionSequence.CallMethod2)
MethodWork2(..);
else if(exec == ExecutionSequence.Both)
{
MethodWork1(..);
MethodWork2(..);
}
}
public void MethodWork1(parameters) {}
public void MethodWork2(parameters) {}
}
Much simplier and understandable for your class consumer.
If this is not what you want, please explain.
EDIT
Just to give you an idea what you can do:
Example:
public class Executor {
public void MainMethod(long parameter, IEnumerable<Action> functionsToCall) {
foreach(Action action in functionsToCall) {
action();
}
}
}
and in the code
void Main()
{
Executor exec = new Executor();
exec.MainMethod(10, new List<Action>{()=>{Console.WriteLine("Method1");},
()=>{Console.WriteLine("Method2");}
});
}
The output will be
Method1
Method2
In this way you, for example, can push into the collection only functions you want to execute. Sure, in this case, the decision logic (which functions have to be executed) is determined outside of the call.

C# cross-thread call problem

I'm writing a form app in c# and I need to be able to change the contents of a Rich Text Box from any thread, I tried using a delegate and InvokeRequired, but the delegate I made still gives me a cross-thread call error, and InvokeRequired crashes the form, without giving an error.
Function I need to be able to execute from any thread:
public static void updateSub(int what)
{
subDisplay.subBox.Text = tb[what];
}
The delegate I tried to use:
public delegate void UpdateDelegateVoid(int what);
static public UpdateDelegateVoid uSub = new UpdateDelegateVoid(updateSub);
uSub(0);
My InvokeRequired code:
public static void updateSub(int what)
{
if (subDisplay.subBox.InvokeRequired)
{
subDisplay.subBox.Invoke(new MethodInvoker(finish));
}
else
{
subDisplay.subBox.Text = tb[what];
}
}
I'm not really sure why the code above isn't working. Thanks!
Strictly speaking, when you check InvokeRequired and find it's true, you should marshall the call to the same method. I'm not sure it fixes your specific problem (I'd need to see more exception details and code) but this is what I mean:
public static void updateSub(int what)
{
if (subDisplay.subBox.InvokeRequired)
{
subDisplay.subBox.Invoke(new Action<int>(updateSub), what);
}
else
{
subDisplay.subBox.Text = tb[what];
}
}
If you're getting "weird behaviour", then check that the form is actually created on the main application thread. In WinForms this isn't forced (as it is in WPF) so it's just possible that the thread that the form was created on isn't actually the root thread of the app.
I mostly use this, and it works perfectly. For the exact same purpose are what you are intending.
public void UpdateSub(string message)
{
subDisplay.subBox.Invoke((Action)delegate {
subDisplay.subBox.Text = message;
});
}
Hope it help's your or someone else with it!
Try this - where you call the same method if an invoke is required.
public void UpdateSub(string message)
{
if (!subDisplay.subBox.InvokeRequired)
{
subDisplay.subBox.Text = message;
}
else
{
var d = new UpdateFormText(UpdateSub);
Invoke(d, new object[] { message });
}
}
Where UpdateFormText is the delegate
public delegate void UpdateFormText(string message);

How to update textboxes in main thread from another thread?

How you update textboxes and labels in the main thread from a new thread running a different class.
MainForm.cs (Main thread)
public partial class MainForm : Form
{
public MainForm()
{
Test t = new Test();
Thread testThread = new Thread(new ThreadStart(t.HelloWorld));
testThread.IsBackground = true;
testThread.Start();
}
private void UpdateTextBox(string text)
{
textBox1.AppendText(text + "\r\n");
}
}
public class Test
{
public void HelloWorld()
{
MainForm.UpdateTextBox("Hello World");
// How do I execute this on the main thread ???
}
}
I have looked at the examples on here but cant seem to get it right. Please could someone give some good links.
I have started again fresh so I don't mess up my code. If anyone would like to put up a working example with my example that would be great.
Also if I had to update multiple objects like textboxes and labels etc (not all at the same time) what would be the best way to go about it, having a method for each textbox or is there a way to do this with one method?
Invoke or BeginInvoke, e.g.
Invoke((MethodInvoker)delegate {
MainForm.UpdateTextBox("Hello World");
});
#tiptopjones I guess you're asking also how to get a reference to the form. You could make your HelloWorld method take an object parameter, use the ParameterizedThreadStart delegate, and then pass a reference to the form as a parameter to the Thread.Start method. But I would suggest reading about anonymous methods which makes it a lot easier and keeps everything strongly typed.
public class MainForm : Form {
public MainForm() {
Test t = new Test();
Thread testThread = new Thread((ThreadStart)delegate { t.HelloWorld(this); });
testThread.IsBackground = true;
testThread.Start();
}
public void UpdateTextBox(string text) {
Invoke((MethodInvoker)delegate {
textBox1.AppendText(text + "\r\n");
});
}
}
public class Test {
public void HelloWorld(MainForm form) {
form.UpdateTextBox("Hello World");
}
}
When you get comfortable with that you could read up on lambda expressions and do it like:
Thread testThread = new Thread(() => t.HelloWorld(this));
You can call the BeginInvoke method, which will queue a delegate to be executed asynchronously on the UI thread.
If you need the background thread to wait until the function finishes on the UI thread, you can call Invoke instead.
Note that you will need a reference to the instance of your form; you should probably pass that to the Test constructor and store it in a private field.
The BackgroundWorker component will do all of this automatically using the ReportProgress method; you should consider using it.
The prefered way in WinForms is to use the SynchronizationContext
public partial class MainForm : Form
{
SynchronizationContext ctx;
public MainForm()
{
ctx = SynchronizationContext.Current;
Test t = new Test();
Thread testThread = new Thread(new ThreadStart(t.HelloWorld));
testThread.IsBackground = true;
testThread.Start();
}
private void UpdateTextBox(string text)
{
ctx.Send(delegate(object state)
{
textBox1.AppendText(text + "\r\n");
},null);
}
}
public class Test
{
public void HelloWorld()
{
MainForm.UpdateTextBox("Hello World");
// How do I excute this on the main thread ???
}
}

Categories

Resources