I'm trying to update a button control from a child thread.
I get some problems with passing parameters to the new thread.
I get the following message:
No overload for 'UpdateText' matches delegate 'System.Threading.ParameterizedThreadStart' (CS0123)
As far as I understand ParameterizedThreadStart takes and type "object" argument. How can I cast the object "button1" to Button in my UpdateText method?
public delegate void MyDelegate(Control ctrl);
void Button1Click(object sender, EventArgs e)
{
Thread thr =new Thread(new ParameterizedThreadStart(UpdateText));
thr.Start(button1);
}
public static void UpdateText(Control control_button)
{
if (control_button.InvokeRequired)
{
MyDelegate md = new MyDelegate(UpdateText);
control_button.Invoke(md, control_button);
}
else
{
control_button.Text = "Updated";
}
}
change UpdateText argument to Object:
public static void UpdateText(Object o)
{
Control control_button = (Control) o;
// ... the rest of your code ...
Check this reference on ParametrizedThreadStart:
Also at this line, i don't really understand what you're trying:
MyDelegate md = new MyDelegate(UpdateText);
control_button.Invoke(md, control_button);
Did you mean to:
control_button.Invoke( () => {
control_button.Text = "Updated";
});
or
control_button.Invoke(MyDelegate, control_button);
Related
I want to use System.Threading.Timer so that I can use this timer to call method "CreateThread" which is as follows.
public void CreateThread()
{
th1 = new Thread(ChangeLabel);
th1.Start();
}
I am getting error at line:
public void textBox1_TextChanged_1(object sender, EventArgs e)
{
TimerCallback tcb = new TimerCallback(CreateThread); //This line is showing error
}
Can anybody please explain what is the problem?
TimerCallback expects a state property in the delegate:
public void CreateThread(object state)
{
th1 = new Thread(ChangeLabel);
th1.Start();
}
MSDN:
public delegate void TimerCallback(
Object state
)
The problem is your method signature doesn't match with the TimerCallback delegate.It takes an object as parameter, but your method takes nothing.
If you look at the MSDN Documentation for the TimerCallback delegate, you will see that it has a signature of void TimerCallback(object).
The method you want the timer to call needs the same signature so that it will compile.
Something like this would fix it:
public void CreateThread()
{
CreateThread(null);
}
private void CreateThread(object state)
{
th1 = new Thread(ChangeLabel);
th1.Start();
}
This would allow any existing callers of CreateThread() to remain unchanged, whilst giving you a method with the correct signature to call the method.
Alternatively, you could do the following by using a lambda:
public void textBox1_TextChanged_1(object sender, EventArgs e)
{
TimerCallback tcb = new TimerCallback(() => CreateThread());
}
namespace SmartDeviceProject4
{
public partial class Form1 : Form
{
Barcode2 brkd = new Barcode2();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
brkd.ScanBufferStart();
brkd.OnScan += new Barcode2.OnScanHandler(brkd_OnScan);
}
void brkd_OnScan(ScanDataCollection scancollection)
{
if (textBox1.InvokeRequired)
{
textBox1.Text = scancollection.GetFirst.Text;//Occurs error here
}
else
{
textBox1.Text = "invoke not required";
}
brkd.ScanBufferStop();
}
}
}
If InvokeRequired is true, then you cannot access an UI element like a textbox because the current code is running in a different thread than the one in which the UI element has been defined. In this case you call the Invoke method passing again the delegate to the same method and the parameters expected by the delegate.
void brkd_OnScan(ScanDataCollection scancollection)
{
if (textBox1.InvokeRequired)
{
// Don't try to use UI properties here, but reinvoke this same method
// asking the framework to switch to the thread owning the control....
textBox1.Invoke(new Barcode2.OnScanHandler(brkd_OnScan), scancollection);
}
else
{
// Now this code runs in the same thread of the UI element and it is possible
// to change the properties of the textbox.
textBox1.Text = scancollection.GetFirst.Text;
}
brkd.ScanBufferStop();
}
WORKER CLASS1:
public override void Process(Crawler crawler, PropertyBag propertyBag)
{
SynchronizationContext uiContext = SynchronizationContext.Current;
Thread thread = new Thread(ThreadFunc);
}
void ThreadFunc(object state)
{
var syncContext = state as SynchronizationContext;
syncContext.Send(new Action(() => {
using (GeckoBrowserForm geckoBrowserForm = new GeckoBrowserForm ("http//www.google.com"))
{
geckoBrowserForm.ShowDialog();
while (!geckoBrowserForm.Done)
{
Application.DoEvents();
}
other code
}
}), null);
}
But I Always get
"Argument 'System.Action' is not assignable to parameter type
'System.Threading.SendOrPostCallback'"
I want that every Process will have own SynchronizationContext and then execute UIS Thread with some logic.
UI THREAD CLASS2:
protected override void OnLoad(EventArgs e)
{
...
String val = "Some value";
Done = true;
and return back `val` to the `CLASS1`
}
is this possible?
From MSDN.
public virtual void Send(SendOrPostCallback d, Object state)
public delegate void SendOrPostCallback(Object state)
Clearly, the Send method takes a delegate with an object parameter and void return value. You are passing a delegate without a parameter. Try
syncContext.Send(new Action(o => { ...
Your SendOrPostCallback() lambda needs to take one parameter of object type. See the MSDN page.
Try changing your call to this (note the lambda changed to (o) from ()):
syncContext.Send(new Action((o) => {
using (GeckoBrowserForm geckoBrowserForm = new GeckoBrowserForm ("http//www.google.com"))
{
geckoBrowserForm.ShowDialog();
while (!geckoBrowserForm.Done)
{
Application.DoEvents();
}
// other code
}
}), null);
I have recently been working on an application where I wanted to display the progress of another thread in the status bar via the ToolStripProgressBar control that is contained in the StatusBar control. Before I attempted to add this code I originally had the code changing the text of a ToolStripStatusLabel control and to do this I used the Invoke method with delegates and everything worked fine. However, I found that when I attempted this with the ToolStripProgressBar the call to the status bar's Invoke method failed without a notification (no error, no exception, nothing). What I have since learned is that to use a progress bar in this way I had to use a BackgroundWorker control. So my code works but I don't understand why I couldn't use the Invoke method that already seemed to work.
Some examples of what worked and what didn't:
This worked
public delegate void changeStatusMessage(String message);
public changeStatusMessage changeStatusMessageDelegate;
public void changeStatusMessageMethod(String message){
if(statusbar.InvokeRequired){
statusbar.Invoke(changeStatusMessageDelegate, new Object[] {message});
}else{
toolstripLabel.Text = message;
}
}
This did not work
public delegate void incrementProgressBar(int value);
public incrementProgressBar incrementProgressBarDelegate;
public void incrementProgressBarMethod(int value){
if(statusbar.InvokeRequired){
statusbar.Invoke(incrementProgressBarDelegate, new Object[] {value});
}else{
toolstripProgress.Increment(value);
}
}
In the example that didn't work the InvokeRequired property is true so the Invoke method is called and then nothing happens. Where as I expected it to call the incrementProgressBarMethod again this time where InvokeRequired is false and thus allowing the Increment method to fire.
I would really like to know why this doesn't work. As I said I have already retooled to use a BackgroundWorker, I just want an explanation.
Invoke calls postmessage API and enqueue message on windows message. If UI thread is blocked, then you can have a deadlock, because it cant push queued message, nothing will happens. The other side of invoke required is not fired, and if something is waiting for it, bang, deadlock.
This is the reason you need to be careful with Invoke.
How to invoke a function on parent thread in .NET?
But your problem is on creation of the delegate, it is a null delegate, you need to create the delegate on same thread that it is being called by invoke, because other way, the underling system will fail on marshaling the delegate (its a pointer).
private void changeStatusMessageMethod(String message)
{
if (this.InvokeRequired)
{
var changeStatusMessageDelegate = new changeStatusMessage(changeStatusMessageMethod);
this.Invoke(changeStatusMessageDelegate, new Object[] { message });
}
else
{
toolstripLabel.Text = message;
}
}
delegate void incrementProgressBar(int value);
private void incrementProgressBarMethod(int value)
{
if (this.InvokeRequired)
{
var incrementProgressBarDelegate = new incrementProgressBar(incrementProgressBarMethod);
this.Invoke(incrementProgressBarDelegate, new Object[] { value });
}
else
{
toolstripProgress.Increment(value);
}
}
This works on dotnet framework v4
private void button1_Click(object sender, EventArgs e)
{
var t = new System.Threading.Thread(new System.Threading.ThreadStart(x));
t.Start();
}
private void x()
{
do
{
changeStatusMessageMethod(DateTime.Now.ToString());
System.Threading.Thread.Sleep(1000);
} while (true);
}
private void button2_Click(object sender, EventArgs e)
{
var t = new System.Threading.Thread(new System.Threading.ThreadStart(y));
t.Start();
}
private void y()
{
do
{
incrementProgressBarMethod(1);
System.Threading.Thread.Sleep(1000);
} while (true);
}
In the code below, is there a way to instead of always subscribing the updateWorker_DoWork method, pass it a method like this
public void GetUpdates(SomeObject blah)
{
//...
updateWorker.DoWork += new DoWorkEventHandler(blah);
//...
}
public void GetUpdates()
{
//Set up worker
updateWorker.WorkerReportsProgress = true;
updateWorker.WorkerSupportsCancellation = true;
updateWorker.DoWork += new DoWorkEventHandler(updateWorker_DoWork);
updateWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(updateWorker_RunWorkerCompleted);
updateWorker.ProgressChanged +=
new ProgressChangedEventHandler(updateWorker_ProgressChanged);
//Run worker
_canCancelWorker = true;
updateWorker.RunWorkerAsync();
//Initial Progress zero percent event
_thes.UpdateProgress(0);
}
For your RunWorkerAsync() you can pass any argument you like. You can just put a Func() or Action() into it and in your DoWork() you just cast the object back to this specific type and call it.
Examples are here and here.
private void InitializeBackgroundWorker()
{
_Worker = new BackgroundWorker();
// On a call cast the e.Argument to a Func<TResult> and call it...
// Take the result from it and put it into e.Result
_Worker.DoWork += (sender, e) => e.Result = ((Func<string>)e.Argument)();
// Take the e.Result and print it out
// (cause we will always call a Func<string> the e.Result must always be a string)
_Worker.RunWorkerCompleted += (sender, e) =>
{
Debug.Print((string)e.Result);
};
}
private void StartTheWorker()
{
int someValue = 42;
//Take a method with a parameter and put it into another func with no parameter
//This is called currying or binding
StartTheWorker(new Func<string>(() => DoSomething(someValue)));
while(_Worker.IsBusy)
Thread.Sleep(1);
//If your function exactly matches, just put it into the argument.
StartTheWorker(AnotherTask);
}
private void StartTheWorker(Func<string> func)
{
_Worker.RunWorkerAsync(func);
}
private string DoSomething(int value)
{
return value.ToString("x");
}
private string AnotherTask()
{
return "Hello World";
}
If I didn't misunderstand you, you need lambda expressions to construct anonymous method.
updateWorker.DoWork += (sender,e)=>
{
//bla
}
Now you needn't always to write a method and pass it to new DoWorkEventHandler(myMethod)
Worked it out, was way simpler than I was thinking. Just had to make a delegate for the method called on DoWork. Probably should have phrased my original question better.
public delegate void DoWorkDelegate(object sender,DoWorkEventArgs e);
public void GetUpdates()
{
StartWorker(new DoWorkDelegate(updateWorker_DoWork));
}
public void StartWorker(DoWorkDelegate task)
{
//Set up worker
updateWorker.WorkerReportsProgress = true;
updateWorker.WorkerSupportsCancellation = true;
updateWorker.DoWork += new DoWorkEventHandler(task);
updateWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(updateWorker_RunWorkerCompleted);
updateWorker.ProgressChanged +=
new ProgressChangedEventHandler(updateWorker_ProgressChanged);
//Run worker
_canCancelWorker = true;
updateWorker.RunWorkerAsync();
//Initial Progress zero percent event
_thes.UpdateProgress(0);
}
private void updateWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = GetUpdatesTask(worker, e);
}