C# cross-thread call problem - c#

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);

Related

Async the content in textbox

I want to display the content asynchronously in textbox? Do anyone know the bug of my code listed below? I want to implement the text in textbox will be updated per second with new value? also i want to ask why checking the InvokeRequired each time before calling the invoke method for the controller?
private void Counting(int Num)
{
int i = 0;
string counter = null;
while (i < Num)
{
Thread.Sleep(1000);
counter = string.Format(" {0}", i);
tbxStatus.BeginInvoke(new UpdateStatusDelegate(UpdateStatus), new string[] { counter });
}
}
public void UpdateStatus(string data)
{
tbxStatus.Text += data;
}
public delegate void UpdateStatusDelegate(string data);
public delegate void CountDelegate(int num);
private void btnStart_Click(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new CountDelegate(Counting), new object[] { 5 });
}
else
Counting(5);
}
Thanks
Your InvokeRequired test should be inside the delegate:
public void UpdateStatus(string data)
{
if (this.tbxStatus.InvokeRequired)
{
UpdateStatusDelegate d = new UpdateStatusDelegate(UpdateStatus);
this.Invoke(d, new object[] { data });
}
else
{
this.tbxStatus.Text = data;
}
}
You can also make this a lot easier and cleaner looking if you use the following extension methods:
public static TResult SafeInvoke(this T isi, Func call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) {
IAsyncResult result = isi.BeginInvoke(call, new object[] { isi });
object endResult = isi.EndInvoke(result); return (TResult)endResult;
}
else
return call(isi);
}
public static void SafeInvoke(this T isi, Action call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) isi.BeginInvoke(call, new object[] { isi });
else
call(isi);
}
So if I want to call a method that I have defined in my UI thread (like a method defined in your form instance) you can do so with the following code (with no need to create any delegates or anything):
formInstance.SafeInvoke(f => f.myFormMethod("parameter1","parameter2"));
in your case, you could do the following:
formInstance.SafeInvoke(f => f.UpdateStatus(myCounterInt.toString));
or something like that.
I've written about using this on my blog but don't give me the credit as I was writing about this CodeProject article
I have recently come to a similar problem.
I am writing a networked game and I wanted to append text to a text box I have, however I would get an error saying I was trying to change a windows form control from a different thread, thus causing a not safe thread. This is because I am using ASyncCallback on my server side.
I had this previously: (The console method is a simple method that gets a string and appends it to the textbox).
console("message");
And to solve the problem I had to change it to this, solving the problem for now:
this.Invoke((MethodInvoker)delegate { console("message"); });
Hopefully this helps others that come across this problem. It probably isn't the best approach possible but it did the trick for me.
Regards.

Why does the StatusBar.Invoke method not work for a ToolStripProgressBar?

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);
}

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 ???
}
}

WPF - Updating Label Content During Processing

Well I've tried several methods of getting this to work, background worker, Dispatcher.Invoke, threading within the called class and nothing seems, to work. The best solution so far is an Extension method which calls the invoke of the control. Also I've tried avoid passing the data for the label through my event classes and simply invoking within my processing code, however this made no difference.
In regards to the background component, I kept getting exceptions saying the background worker was busy, so I instantiated the class several times, however the label only visibly changed once the entire operation had been complete.
I've removed my previous code, here's everything that is relevant, as it seems the issue is difficult to resolve.
Method Being Called
private void TestUris()
{
string text = new TextRange(rtxturis.Document.ContentStart, rtxturis.Document.ContentEnd).Text;
string[] lines = Regex.Split(text.Remove(text.Length - 2), "\r\n");
foreach (string uri in lines)
{
SafeUpdateStatusText(uri);
bool result;
string modUri;
if (!uri.Contains("http://"))
{
modUri = uri;
result = StoreData.LinkUriExists(new Uri("http://" + modUri));
}
else
{
modUri = uri.Substring(7);
result = StoreData.LinkUriExists(new Uri(uri));
}
if (!result)
{
Yahoo yahoo = new Yahoo();
yahoo.Status.Sending += (StatusChange);
uint yahooResult = 0;
yahooResult = yahoo.ReturnLinkCount(modUri);
if (yahooResult > 1000 )
{ results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 1000, "Will be processed", true)); }
else
{ results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, (int)yahooResult, "Insufficient backlinks", false)); }
}
else
{
results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 0, "Previously been processed", false));
}
}
foreach (var record in results)
{
dgvresults.Items.Add(record);
}
EnableStartButton();
}
Yahoo Class
public class Yahoo
{
/// <summary>
/// Returns the amount of links each Uri has.
/// </summary>
public uint ReturnLinkCount(string uri)
{
string html;
Status.Update(uri, false); //this is where the status is called
try
{
html = client.DownloadString(string.Format("http://siteexplorer.search.yahoo.com/search?p=http%3A%2F%2F{0}&fr=sfp&bwm=i", uri));
}
catch (WebException ex)
{
ProcessError(ex.ToString());
return 0;
}
return (LinkNumber(html));
}
Status Classes
public class StatusEventArgs : EventArgs
{
private string _message;
private bool _isidle;
public StatusEventArgs(string message, bool isidle)
{
this._message = message;
this._isidle = isidle;
}
public bool IsIdle
{
get { return _isidle; }
}
public string Message
{
get { return _message; }
}
}
public class Status
{
public Status()
{
}
// Declaring an event, with a custom event arguments class
public event EventHandler<StatusEventArgs> Sending;
// Some method to fire the event.
public void Update(string message, bool isIdle)
{
StatusEventArgs msg = new StatusEventArgs(message, isIdle);
OnUpdate(msg);
}
// The method that invokes the event.
protected virtual void OnUpdate(StatusEventArgs e)
{
EventHandler<StatusEventArgs> handler = Sending;
if (handler != null)
{
handler(this, e);
}
}
}
Method That Changes the labels Content
private void StatusChange(object sender, StatusEventArgs e)
{
if(!e.IsIdle)
{
lblstatus.Content = e.Message;
lblstatus.Foreground = StatusColors.Green;
lblstatus.Refresh();
}
else
{
lblstatus.Content = e.Message;
lblstatus.Foreground = StatusColors.Grey;
lblstatus.Refresh();
}
}
The Refresh static method called:
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render , EmptyDelegate);
}
Another EDIT: Staring at my code for a bit longer, I've realised, that the foreach loop will execute really quickly, the operation which takes the time, is
yahooResult = yahoo.ReturnLinkCount(modUri);
Therefore I've declared the status class (which handles the event and invokes the label etc) and subscibed to it. I've gotten better results, although it still feels random, sometimes I see a couple of label updates, and sometimes one even though the exact same URI's are passed, so weird.
I hope there is sth. helpful...
private void button1_Click(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem(o =>
{
int result = 0;
for (int i = 0; i < 9999999; i++)
{
result++;
Dispatcher.BeginInvoke(new Action(() =>
{
this.label1.Content = result;
}));
Thread.Sleep(1);
}
});
}
SOLVED IT YES WOOHOOOOOOOO 3 days of testing, testing, testing.
I decided to start a new project just with the extension method above and a simply for loop to test UI update functionality. I started testing different DispatchPrioraties (tested them all).
Weirdly, I found the highest priorities were the worse, for example using Send didn't update the label at all, Render updated it twice on average. This was the weird behavior I was experiencing as I tried different priorities. I discovered Background:
The enumeration value is 4. Operations are processed after all other non-idle operations are completed.
Now this sounded exactly what I didn't want, as obviously the label should update during processing, hence why I never tried it. I'm guessing that once one of my method has been completed, before the next it called, the UI is updated. I'm find of guessing, but it 100% consistently updates correctly on two separate operations.
Thanks all.
Well this is going to sound stupid but you could just reference the forms namespace, and then you can do this
using System.Windows.Forms;
mylabel = "Start";
Application.doEvents();
myLabel = "update"
Application.doEvents();
now the problem using this would be you are using wpf but you can still reference forms and use this namespace. The other issue is what ever is in the que would execute directly to the ui. However this is the most simplistic way of doing label updates i could think of.
would it be easier/better to add the status info as a property on this object, and have it just fire property change notifications?
that way the label text (or whatever) could be bound to the property instead of having the async work try to update a label?
or add a method like this to update status if you have to update it?
void SafeUpdateStatusText(string text)
{
// update status text on event thread if necessary
Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate
{
lblstatus.Content = text;
}, null);
}
otherwise, i don't think we have enough details to help yet....
I hope this helps:
private delegate void UpdateLabelDelegate(DependencyProperty dp, object value);
public void UpdateLabelContent(Label label, string newContent)
{
Dispatcher.Invoke(new UpdateLabelDelegate(label.SetValue), DispatcherPriority.Background, ContentProperty, newContent);
}
Usage:
while (true)
{
UpdateLabelContent(this.lblStatus, "Next random number: " + new Random().Next());
Thread.Sleep(1000);
}

C# Making a delegate available to a class

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;
}
}

Categories

Resources