I'm using a timer to regularly read-in a log file and post certain contents to a textbox in a Windows Form Application I'm developing in C#. I do this by sending a string to set_textbox_thread which posts the text (s) to the appropriate textbox (tbc) in the else below. The code below works in the practice application I built. However, the same code runs, but fails to update my textbox in the full application I'm building. It seems to be failing on the Invoke statement, which fails to call set_textbox_thread again. My theory is that, because my full application has a more complex set of controls, I am not calling Invoke via the correct control. I've tried calling it via "this" the parent panel, the parent form, and the button that triggers set_textbox_thread, and am dealing with the same outcome. Two questions:
Which control should I call Invoke under?
Is there a way to retrieve "the thread that owns the controls underlying windows handle? Can I do this through the Controls.Owner method?
I have tried making this Invoke call using try/catch, but am unable to retrieve an error message in the catch. Any ideas how to resolve the issue this way?
Thanks in advance!
private delegate void stringDelegate(string s);
private void set_textbox_thread(string s)
{
TextBox tbc = get_thread_tb();
if (tbc.InvokeRequired)
{
MessageBox.Show("Invoke Required");
stringDelegate sd = new stringDelegate(set_textbox_thread);
**this.Invoke(sd, new object[] {s });**
MessageBox.Show("Invoke Completed");
}
else
{
1) It doesn't matter what control you invoke under; there is only one UI thread and any control to marshal the call back to that UI thread.
2) what could you possibly do with this thread?
3) not much detail there. How do you know you have error messages? And what do you mean by "error messages"
Call invoke on the text box instead:
tbc.Invoke(sd, new object[] {s });
Related
I'm having a strange problem. I'm using SocketIO4Net Client for my program written in C#. The program communicates with server written in NodeJS & SocketIO. I'm able to send & receive data between my program & server using 'socket.emit()' & 'socket.On()' methods in SocketIO4NET Client. However, when i try to update a textbox in the GUI with the data i received from the server, nothing happens. But, if i use 'MessageBox.Show()' method, i can display the data i received. I'm using the following code for receiving data:
public Form1()
{
InitializeComponent();
socket = new Client("http://127.0.0.1:80/");
socket.On("message", (data) =>
{
String msg = data.Json.Args[0].ToString();
MessageBox.Show(msg,"Received Data");
rxtxtbox.Text = msg;
});
socket.Connect();
}
For sending data:
private void sendbtn_Click(object sender, EventArgs e)
{
String msg = msgtxtbox.Text.ToString();
socket.Emit("private message", msg);
}
The above code works fine. But its not updating the TextBox 'rxtxtbox'. If I move the line "rxtxtbox.Text = msg;" above "MessageBox.Show();", then nothing will happen on receiving the "message" event. I tried setting breakpoints & watching the value of the variable "msg" & its fine. I tried declaring another function just to update the textbox & passing "msg" to it & still getting no results! I think this has something to do with waiting for "message" event? I tried "Application.DoEvents()" and "rxtxtbox.Refresh()" methods & still no luck! I'm new to C#. Please help!
You mention that if you place the MessageBox.Show in front of the rxtxtbox.Text - you see the payload data, but having the rxtxtbox.Text first - nothing at all happens.
The event handers from socketIO4Net definitely run on a background thread, so I'd bet you are throwing an exception updating the UI control - from this non-UI thread. My understanding of MessageBox is that it is not tied to the UI, so it could be called from non-ui threads w/o issue.
Try this in place of your rxtxtbox.Text = msg line:
rxtxtbox.Invoke(new Action(() => rxtxtbox.Text = msg)));
This uses something known as a lambda express to create an anonymous delegate that will be executed on the thread that owns the control (textbox in this case) underlying handle.
You could also place this line right before you update the Text property, and inspect it (if true, you are indeed on a non-ui thread, and have found your problem):
bool state = rxtxtbox.InvokeRequired;
There are various ways to deal with updates to the GUI from non-ui threads. Search multithreading winforms gui C# etc here on SO. Any messages raised from socketio4net will need to be handled appropriately, or you will throw UI thread exceptions.
I have a Server class which it basically waits for connections from a client. Inside that class I create an NetworkStream object in order to be able to receive bytes from a client. Because the NetworkStream.Read() method is not asynchronous (meaning that it will wait until it reads bytes from a client in order to proceed executing code similar to the messagebox method), I have to read for bytes in a separate thread so that the user using the program can still interact with the program if the program happens to be waiting to read for data.
anyways a lot of objects are owned by that thread. One example is that I have a List called log in that class. I use that list to know the status of the server. Maybe it is listening for a connection or perhaps it's status is "connected" or "disconnected".
So if I do something like:
Server myServer = new Server("192.168.0.120","1300"...\\ I pass the appropite parameters in order to instantiate it
//...
.. then I am able to latter look at the log as
string foo = myServer.Log[0] for example.
because I want to know when the log is updated, on the server class I have created an event as:
public delegate void onUpdateHandler(string newStatus);
public event onUpdateHandler onUpdate = delegate { };
I then fire events on the Server class as:
onUpdate("waitingForConnection");
and I receive those events with the method:
but if I try to do something with newStatus I get the error stating:
System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
so how can I pass an object with an event?
Edit
so I also notice that if I do:
I also get an error!
but when I do the same thing calling that from a button as:
// SERVER IS RUNNING BEFORE CALLING THIS METHOD
private void button3_Click(object sender, RoutedEventArgs e)
{
listView1.Items.Add("my own string");
}
I do NOT get an error!
why is it that I get an error with the event and I do not get an error when calling it with a regular button.
The problem is that the thread tries to access the ListView which is a DependencyObject which has thread affinity, use the Dispatcher to execute methods like this on the UI-thread, e.g.:
Application.Current.Dispatcher.Invoke((Action)(() =>
{
listView1.Items.Add(newStatus);
}));
Also see the threading model reference for additional info.
The problem is not that you try to do something with the value that you sent to the method, the problem is what you are trying to do with it.
The event handler is still running in your background thread, and from there you can't use any UI controls as they belong to the main thread.
The usual way of handling that is to use the CheckAccess method to check if you need to switch treads, and the Invoke method to hand off the work to the main thread:
void server_onUpdate(string newStatus) {
if (!listView1.Dispatcher.CheckAccess()) {
listView1.Dispatcher.Invoke(server_onUpdate, newStatus)
} else {
listView1.Items.Add(newStatus);
}
}
It has been a long question, so here is the summary first,
I have a Client class for my messenger project.
My Client class has a Socket.
I use its BeginReceive method to get messages from server.
In async callback of BeginReceive, I get the message using EndReceive.
When I get the message, I fire the MessageReceived event with message itself.
I use this Client class in my message form.
In message form, I can get the received message by attaching a method to Client.MessageReceived.
I use OnMessageReceived method for this purpose.
When I get the message, I can show it on a TextBox (using Control.Invoke) or MessageBox.
I can also add a new tab to my TabControl in OnMessageReceived.
When I try to initialize a WebBrowser control, I get ThreadStateException.
The control I use to display messages derives from WebBrowser control, so I need it.
Threading.Thread.CurrentThread.ThreadState is "Background".
I don't think you'll need the details but you can find the detailed question I first intented to post below.
Many thanks.
I'm working on a messenger project (Server is a Windows Service and Client is a Windows Forms Application) using Net.Socket's async methods.
I fire Client's MessageReceived event in callback of Socket.BeginReceive;
Everything is how I want them to be until here.
I use MessageReceived event in my form (the one that two people writes to each other) I can do anything I want to the UI using Control.Invoke method (if required) with one annoying exception.
I have tabbed conversations in the form so when a message arrives, I check if there is an open conversation (tab) with the sender. If yes, I select that tab and display the message. If no, I create a new ConversationTab.
Now, I'm sorry if it's being a long question than it should be or if I can't explain myself sufficently. English is not my first language and this is my first question in Stack Overflow.
So, here is the ConversationTab:
public class ConversationTab : TabPage
{
public User Friend { get; private set; }
public MessageBrowser MessageBrowser { get; private set; }
public ConversationTab(User friend) : base(friend.DisplayName)
{
Friend = friend;
MessageBrowser = new MessageBrowser();
Controls.Add(MessageBrowser);
MessageBrowser.Dock = DockStyle.Fill;
}
}
MessageBrowser derives from WebBrowser and the reason I use this is because I could not apply custom styles (color, font, size) 'per message' using RichTextBox. RichTextBox.SelectedColor doesn't always work or I couldn't make it work as intended. MessageBrowser let's me use CSS instead. Wandering off of the subject? Sorry.
Here is the NewConversation method I call when MessageReceived event fires:
public void NewConversation(User friend)
{
ConversationTab tab = Conversations.FirstOrDefault(c => c.Friend.Id == friend.Id);
if (tab != null)
ActiveConversation = tab;
else
{
tab = new ConversationTab(friend);
// add tab to TabControl
}
// bla
}
"Conversations" gets the tab pages of the TabControl and "ActiveConversation" gets or sets the SelectedTab property of the TabControl.
My point in creating these properties are mostly thread-safety logic inside.
So the question: It's throwing ThreadStateException in "tab = new ConversationTab(friend)" part of the above code. It is the "MessageBrowser = new MessageBrowser()" part of the first code and the constructor of MessageBrowser. The reason of why I didn't provide MessageBrowser's constructor code is because the exception is thrown before any line of inner code gets executed (It is about WebBrowser's constructor, I get this exception when I try to initalize a WebBrowser, too.)
Actually I don't even get an exception, the application just closes there without notifying me about anything. I saw the exception when I try to call "MessageBrowser = new MessageBrowser()" on ConversationTab's constructor in Watch window.
I'm kind of new to using threads and asynchronous methods.
MSDN says:
ThreadStateException is thrown by methods that cannot perform the requested operation due to the current state of a thread.
In my case, the thread's state is "Background".
I have no clue about what am I doing wrong.
Thank you very much if you read the whole thing and thank you much more if you can help.
This seems to be related to using COM (web browser control uses COM) in .NET where thread apartment needs to be set to STA.
Try adding [STAThread] to your entry point.
Have a look at this.
In my application I display data from a online web service into several UITableViews. I have added several ways for the user to update the data, but the TableView.ReloadData() method does not seem to work.
When the user calls for an update, I get a new set of data from the server, pass it to the UITableViewSource instance that is attached to the UITableViewController and then call the ReloadData() method, which unfortunately does not in fact reload the data. Only after I return to the main screen and then go back to the table view (because it is already created, I just display the instance that already exists) does the new data show up in the tableview.
What am I doing wrong? I tried creating a new instance of the UITableViewSource when updating the data, but that does not help either.
Here is the code for loading data into the tableview (I reuse it for any event that requires data to be loaded into it):
dataControl.GetList(Tables.UPDATES)); //gets data from the server and passes it to the SQL database
Source source = GetSource(theType.Name, theType, groups); //creates a new source loaded with the data
Updates.TableView.Source = source;
Updates.TableView.AllowsSelection = false;
Updates.TableView.ReloadData();
This code is of course executed in a separate thread that invokes on the main thread.
Basically the same code is called when the user asks for an update(an animation is played while the background thread works).
Pavel is correct - try the following to see if it works:
InvokeOnMainThread(delegate{
Updates.TableView.Source = source;
Updates.TableView.AllowsSelection = false;
Updates.TableView.ReloadData();
});
In future, whenever you're dealing with something that will change the UI currently shown, you will need to ensure that it takes place on the main thread (also known as the GUI thread). This InvokeOnMainThread is a method from NSObject so can be called like above in all UIViews / UIViewControllers etc - you can also call it from an entirely C# class using:
MonoTouch.UIKit.UIApplication.SharedApplication.InvokeOnMainThread(delegate{ /*code here*/ });
You say it is done in a different thread, could it be that the worker thread cannot call the UI thread? I.e. you should call the ReloadData via delegate to be sure it gets called in the UI thread and not "only" in the worker thread, as it might interlock and never get actually called (happened to me in a different scenario).
I also ran into this problem and found this question. Using some of the hint here, I finally got it work by reseting the DataSource and call ReloadView().
tableView.DataSource = new MyDataSource(...);
tableView.RelaodData();
From my testing, it doesn't make different if I wrap the ReloadView() within the InvokeOnMainThread or not. Well, that's maybe because I'm not using worker thread.
It is strange that in another case I could refresh the table view by simply calling ReloadData() in ViewDidAppear(). The only difference is that the above case is the refresh within the same view.
You can reload datain viewwillappear() or set load data code in viewwillappear().
For googlers:
I had the same issues. This is what fixed it for me
InvokeOnMainThread(delegate {
myTableView.Source = new TableViewSource();
myTableView.ReloadData();
this.View.SetNeedsDisplay();
});
A C# thread (Read()) causes System.NotSupportedException when it tries to update a winform based on received content.
The full error message is
Read() System.NotSupportedException:
An error message cannot be displayed
because an optional resource assembly
containing it cannot be found at
Microsoft.AGL.Common.MISC.HandelAr()
at
System.Windows.Forms.ProgressBar._SetInfo()
at
System.Windows.Forms.ProgressBar.set_Value()
at ...ProcessStatus() at ...Read()
The Build/Target Environment is: Microsoft.NET\SDK\CompactFramework\v2.0\WindowsCE. Is the problem writing to the ProgressBar from a Thread?
If so, what is the correct C#/winforms method to update a ProgressBar from a Thread? In this application the Read() Thread is continuous: it is started when the application starts and runs forever.
void ProcessStatus(byte[] status)
{
Status.Speed = status[5];
var Speed = Status.Speed/GEAR_RATIO;
Status.Speed = (int) Speed;
progressBarSpeed.Value = Status.Speed;
...
You'll need to use Invoke to make changes to controls created in the Gui Thread.
To make life easier, take a look at some of the extension methods provided here
You should call Control.BeginInvoke