I know how to do a thread safe update on a text box that was already defined http://msdn.microsoft.com/en-us/library/ms171728.aspx .... how can i do this on text boxes that were generated later on in the program? You advice is much appreciated.
Given some TextBox object, just invoke on it:
TextBox foo = new TextBox(...);
// Code to add the new box to the form has been omitted; presumably
// you do this already.
Action update = delegate { foo.Text = "Changed!"; };
if (foo.InvokeRequired) {
foo.Invoke(update);
} else {
update();
}
If you're using this pattern a lot, this extension method might be helpful:
public static void AutoInvoke(
this System.ComponentModel.ISynchronizeInvoke self,
Action action)
{
if (self == null) throw new ArgumentNullException("self");
if (action == null) throw new ArgumentNullException("action");
if (self.InvokeRequired) {
self.Invoke(action);
} else {
action();
}
}
Then you can reduce your code to:
foo.AutoInvoke(() => foo.Text = "Changed!");
This will just do the right thing, executing the delegate on the main GUI thread whether or not you are currently executing on it.
We definitely need more information here, but from what I can gather, you're lamenting the fact that the thread's main function doesn't take any arguments. You could make the textbox(es) members of the surrounding class, and access them that way. If you go this route though, be sure to use a mutex or some other locking device for threads.
Related
I know this question is already answered before. But I tried in my project and it is throwing exception. I am new to C# and I want to know where am doing wrong. Thanks in advance.
Issue: I am trying to update a list box(lbLine) which is present in a dialog box. I am running a separate thread that decodes the data received from the socket and populates them on the list box control.
My code sample is as below:
private void AddLine(ref int nLine, ref int nType)
{
PLine pLine = new PLine();
pLine.LineNo = nLine;
pLine.Type = nType;
((MainWindow)System.Windows.Application.Current.MainWindow).pConnect.lbLine.Dispatcher.Invoke(() =>
{
Dictionary<string, PConn> pConnList =
((MainWindow)System.Windows.Application.Current.MainWindow).PConnList;
if (((MainWindow)System.Windows.Application.Current.MainWindow).pConnect != null)
{
bool isPCUExist = pConnList.ContainsKey(((MainWindow)System.Windows.Application.Current.MainWindow).pConnect.tbIPAddress.Text);
if (isPCUExist && pConnList[((MainWindow)System.Windows.Application.Current.MainWindow).pcuConnect.tbIPAddress.Text].IsConnected)
{
PConn pConn = pConnList[((MainWindow)System.Windows.Application.Current.MainWindow).pConnect.tbIPAddress.Text];
if (pConn != null)
{
pConn.AddPLineNo(pLine);
}
}
}
});
}
Try using
Application.Current.Dispatcher.Invoke
instead of
(MainWindow)System.Windows.Application.Current.MainWindow).pConnect.lbLine.Dispatcher.Invoke
The problem may be that the pConnect or lbLine is a UI object, so it, like any other UI object, cannot be used from other threads than the main thread.
Usually there is only one UI/Main thread, so all dispatchers or other ways to move execution to it will be equivalent.
I have written a Window Manager for my program, which keeps certain windows open for the life of the Program (on background threads) (if the user wants them open).
I just implemented an action for the contacts window. The problem is that, the action works when the window is already open, but if the action is invoked when the window isn't open yet, then the window opens, but the action is not carried out (pressing the button again will carry out the action).
the code:
private static SetupContacts _contactsWindow;
private static Thread _contactthread;
public static void ShowContact(repUserObject uo, ContactFormAction action, int contactID)
{
if (_contactsWindow == null)
CreateContactThread(uo, contactID);
// make sure it is still alive
if (!_contactthread.IsAlive)
CreateContactThread(uo, contactID);
if (_contactsWindow != null)
{
_contactsWindow.BringToFront();
_contactsWindow.Focus();
switch (action)
{
case ContactFormAction.ViewContact:
if (contactID > 0)
_contactsWindow.LoadCustomer(contactID); // load the contact
break;
case ContactFormAction.AddNewContact:
_contactsWindow.AddCustomer();
break;
}
}
}
private static void CreateContactThread(repUserObject uo, int contactID)
{
if (_contactthread == null || !_contactthread.IsAlive)
{
_contactthread = new Thread(delegate()
{
_contactsWindow = new SetupContacts(uo, contactID);
_contactsWindow.CerberusContactScreenClosed += delegate { _contactsWindow = null; };
_contactsWindow.CerberusContactHasBeenSaved += delegate(object sender, ContactBeenSavedEventArgs args)
{
if (CerberusContactHasBeenSaved != null)
CerberusContactHasBeenSaved.Raise(sender, args);
};
Application.EnableVisualStyles();
BonusSkins.Register();
SkinManager.EnableFormSkins();
UserLookAndFeel.Default.SetSkinStyle("iMaginary");
Application.Run(_contactsWindow);
});
_contactthread.SetApartmentState(ApartmentState.STA);
_contactthread.Start();
}
}
What happens when the routine runs for the first time, (by calling ShowTime), that it hits the first if statement and goes to CreateContactThread() routine. That does it job, but when it returns, the _contactsWindow is still null. The next time the routine is called (ie, call by pressing the button the second time), it all works fine as the _contactWindow is not null.
How do i get it to do it all in one go ?
I am in vehement agreement with commenter Blorgbeard, who advises that it's a bad idea to run more than one UI thread. The API itself works best when used in a single thread, and many of the kinds of actions and operations one might want to do in code with respect to the UI objects are most easily handled in a single thread, because doing so inherently ensures things happen in the order one expects (e.g. variables are initialized before being used).
That said, if for some reason you really must run your new window in a different thread, you can synchronize the two threads so that the initial thread cannot proceed until the new thread has gotten far enough for the operations you want to perform on the newly-initialized object to have a reasonable chance of success (including, of course, that object having been created in the first place).
There are lots of techniques for synchronizing threads, but I prefer the new TaskCompletionSource<T> object. It's simple to use, and if and when you update the code to use async/await, it will readily mesh with that.
For example:
public static void ShowContact(repUserObject uo, ContactFormAction action, int contactID)
{
CreateContactThread(uo, contactID);
if (_contactsWindow != null)
{
_contactsWindow.BringToFront();
_contactsWindow.Focus();
switch (action)
{
case ContactFormAction.ViewContact:
if (contactID > 0)
_contactsWindow.LoadCustomer(contactID); // load the contact
break;
case ContactFormAction.AddNewContact:
_contactsWindow.AddCustomer();
break;
}
}
}
private static void CreateContactThread(repUserObject uo, int contactID)
{
if (_contactthread == null || !_contactthread.IsAlive)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
_contactthread = new Thread(delegate()
{
_contactsWindow = new SetupContacts(uo, contactID);
_contactsWindow.CerberusContactScreenClosed += delegate { _contactsWindow = null; };
_contactsWindow.CerberusContactHasBeenSaved += delegate(object sender, ContactBeenSavedEventArgs args)
{
if (CerberusContactHasBeenSaved != null)
CerberusContactHasBeenSaved.Raise(sender, args);
};
_contactsWindow.Loaded += (sender, e) =>
{
tcs.SetResult(true);
};
Application.EnableVisualStyles();
BonusSkins.Register();
SkinManager.EnableFormSkins();
UserLookAndFeel.Default.SetSkinStyle("iMaginary");
Application.Run(_contactsWindow);
});
_contactthread.SetApartmentState(ApartmentState.STA);
_contactthread.Start();
tcs.Task.Wait();
}
}
Notes:
You had what appears to me to be redundant checks in your code. The CreateContactThread() method itself checks for null and !IsAlive, and restarts the thread if either of those are false. So in theory, by the time that method returns, the caller should be guaranteed that everything has been initialized as desired. And you should only have to call the method once. So I changed the code to do just that: call the method exactly once, and do so unconditionally (since the method will just do nothing if there is nothing to do).
The calling thread will wait in the CreateContactThread() method after starting the new thread, until the new window's Loaded event has been raised. Of course, the window object itself has been created earlier than that, and you could in fact release the calling thread at that time. But it seems likely to me that you want the window object fully initialized before you start trying to do things to it. So I've delayed the synchronization to that point.
As Blorgbeard has noted, one of the risks of running UI objects in multiple threads is that it's harder to access those objects without getting InvalidOperationExceptions. Even if it works, you should not really be accessing _contactsWindow outside of the thread where it was created, but the code above does just that (i.e. calls BringToFront(), Focus(), LoadCustomer(), and AddCustomer() from the original thread). I make no assurances that the code above is actually fully correct. Only that it addresses the primary synchronization issue that you are asking about.
Speaking of other possible bugs, you probably have an unresolved race condition, in that the new contacts-form thread might be exiting just as you are checking its IsAlive property. If you check the property just before it exits, but then try to access the thread and/or the window after it has exited, your code is likely to do something bad (like crash with an exception). This is yet another example of something that would be a lot easier to address if all of your UI objects were being handled in a single thread.
I admit that some of the above is speculative. It's impossible for me to say for sure how your code will behave without seeing a good, minimal, complete code example. But I feel the likelihood of all of the above being accurate and applicable is very high. :)
I have a windows forms program with a form MainForm. On a button press I start a code that runs (pulses) on every 0.5secs on another thread. I want to modify many things, like labels, progressbars on my MainForm, from the Pulse method. How is this possible?
So I would like to know, how to interract with variables, values, in that thread, and the MainForm. Modify each other, etc..
On foo button click, I tell my pulsator to start.
Pulsator.Initialize();
Here is the Pulsator class:
public static class Pulsator
{
private static Thread _worker;
public static void Initialize()
{
_worker = new Thread(Pulse);
_worker.IsBackground = true;
_worker.Start();
}
public static void Close()
{
if (_worker != null)
{
_worker.Abort();
while (_worker.IsAlive || _worker.ThreadState != ThreadState.Stopped)
{
//closing
}
}
}
public static void Pulse()
{
if (_worker != null)
{
while (true)
{
SomeOtherClass.Pulse();
Thread.Sleep(500);
}
}
else
{
SomeOtherClass.Pulse(); // yeah I know this doesnt needed
}
}
}
SomeOtherClass Pulse method looks like :
public static void Pulse()
{
//here I will have several values, variables, and I want to show results,
// values on my MainForm, like:
Random random = new Random();
MainForm.label1.Text = random.Next(123,321).ToString(); // I hope you know what I mean
}
Of course it's much complicated, it's just a silly example.
Generally, in WinForms it's not safe to modify the state of visual controls outside the thread that owns the control's underlying unmanaged resources (window handle). You have to use the Control.Invoke method to schedule executing the modification on the control's owning thread.
As others already mentioned, you have to use Control.Invoke to change the UI controls from the background thread.
Another option is to use System.ComponentModel.BackgroundWorker (it's available in the form designer toolbox). You could then take a regular forms timer, to call the RunWorkerAsync-Method and do your background work in the DoWork event handler, which is automatically called from another thread.
From there, you can hand data back to the main thread, by calling ReportProgress. This will raise the ProgressChanged event in the main thread, where you are free to update all your UI controls.
Why not use a System.Timers.Timer?
E.g.:
trainPassageTimer = new Timer(500);
trainPassageTimer.AutoReset = true;
trainPassageTimer.Elapsed += TimeElapsed;
...
private void TimeElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
// Do stuff
// Remember to use BeginInvoke or Invoke to access Windows.Forms controls
}
C# 2 or higher (VS2005) has anonymous delegates (and C# 3 has lambdas which are a slightly neater version of the same idea).
These allow a thread to be started with a function that can "see" variables in the surrounding scope. So there is no need to explicitly pass it anything. On the downside, there is the danger that the thread will accidentally depend on something that it should not (e.g. a variable that is changing in other threads).
_worker = new Thread(delegate
{
// can refer to variables in enclosing scope(s).
});
How can I invoke a control with parameters? I've googled this up, but nowhere to find!
invoke ui thread
This is the error i get:
Additional information: Parameter count mismatch.
And this happens when i do a simple check whether the text property of a textbox control is empty or not. This works in WinForms:
if (this.textboxlink.Text == string.Empty)
SleepThreadThatIsntNavigating(5000);
It jumps from this if the line to the catch block and shows me that message.
This is how i try to invoke the control:
// the delegate:
private delegate void TBXTextChanger(string text);
private void WriteToTextBox(string text)
{
if (this.textboxlink.Dispatcher.CheckAccess())
{
this.textboxlink.Text = text;
}
else
{
this.textboxlink.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new TBXTextChanger(this.WriteToTextBox));
}
}
What am I doing wrong? And since when do i have to invoke a control when i just want to read its content?
When you call Invoke, you're not specifying your argument (text). When the Dispatcher tries to run your method, it doesn't have a parameter to supply, and you get an exception.
Try:
this.textboxlink.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new TBXTextChanger(this.WriteToTextBox), text);
If you want to read the value from a text box, one option is to use a lambda:
string textBoxValue = string.Empty;
this.textboxlink.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action( () => { textBoxValue = this.textboxlink.Text; } ));
if (textBoxValue == string.Empty)
Thread.Sleep(5000);
Reed is correct, but the reason you need to do this is that GUI elements are not thread safe and so all GUI operations have to be done on the GUI thread to ensure that the content is being read correctly. Its less obvious why this is necessary with a read operation like this but it is very necessary with writes and so the .NET framework just requires all access to the GUI to be done in the GUI thread.
if (listBox1.InvokeRequired)
{
listBox = new StringBuilder(this.listBox1.Text);
}
This is the code in c# which when executed produces an invalid cross thread operation error for listBox1 which is a listbox in my form.
Could u guys please tell me why??
I am using the invokeRequired method too and am not changing the contents of the listbox either.
InvokeRequired only tells you that an Invoke is necessary in order to validly access the element. It doesn't make the access legal. You must use the invoke method to push the update to the appropriate thread
Action update = () => listbox = new StringBuilder(this.listBox1.Text);
if (listBox1.InvokeRequired) {
listBox1.Invoke(update);
} else {
update();
}
InvokeRequired simply checks to see if Invoke is required. You found it's required, yet didn't call Invoke!
Your code should run when InvokeRequired is false
delegate void SetListBoxDelegate();
void SetListBox()
{
if(!InvokeRequired)
{
listBox = new StringBuilder(this.listBox1.Text);
}
else
Invoke(new SetListBoxDelegate(SetListBox));
}
Edit:
Check out Making Windows Forms thread safe