I have one main windows form and within that form I have custom controls that represents different screens in application. I want to access this control's child controls. There's something I'm not getting here...sometimes I get this error:
Cross-thread operation not valid:
Control 'lblText' accessed from a thread
other than the thread it was created on.
but sometimes everything works OK. I don't completelly understand why the error...probably something with external device (MEI BillAcceptor) which has an event (inside Form1 class) that does the changes to the control... so let me write a simple code...
//user control
public partial class Screen2 : UserControl
{
public void changeValue(string txt)
{
lblText.Text = txt;
}
}
and the method changeValue is called from a form1 when particular event is rised...
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
BillAcceptor.SomeBillAcceptorEvent +=
new SomeBillAcceptorEventHandler(changeText);
}
private void changeText(object sender, EventArgs args)
{
_screen2.changeValue("some text");
}
}
So the most annoying thing is that sometimes everything actually works... So my question is "do I have to use Invoke here?" or how do I solve this with less changes to the application...
In your handler. do something like this.
if (this.InvokeRequired)
{
Invoke(new MethodInvoker(() =>
{
_screen2.changeValue("some text");
}));
}
else
{
_screen2.changeValue("some text");
}
I would guess that the event is being raised on a seperate thread other that the main UI thread.
Yes you need to use Invoke if there is a possibility of that method being called from a different thread.
You can check this.InvokeRequired(), if true, then use invoke, if false do a normal call.
This occurs due to thread unsafe call
You should make only thread safe calls in program
Check this link.
The short answer is yes, you must use Invoke. See this question and its accepted answer if you need details.
The reason the exception is only thrown some of the time, by the way, comes down to timing. You currently have a race condition in which sometimes you get lucky and sometimes you don't.
By the way, here is pretty handy pattern for this sort of thing.
Refactor any code that sets form values into its own private void method(s).
In this new method, call InvokeRequired. If it returns true, call Invoke, passing the current method so as to recurse back into it. If it returns false, go ahead and make the change.
Call this new method from the event handler.
For example:
private void ChangeScreen2() {
if (this.InvokeRequired) {
this.Invoke(new MethodInvoker(ChangeScreen2));
}
else {
_screen2.changeValue("some text");
}
}
private void changeText(object sender, EventArgs args)
{
ChangeScreen2();
}
The idea being that you sequester all code that modifies the form into these methods that always begin with a check of InvokeRequired and always Invoke themselves if so required. This pattern works with .NET 1.0 onward. For even neater approach, see the accepted answer to this question, which works with .NET 3.0 and later.
Related
So using windows form builder, I have created a new form with textbox in it, calling this form as LogForm.cs, this form/class has a method called log(string text).
In my main form class (Form1.cs), I have created an instance of that form.
LogForm logForm = new LogForm();
logForm.log("Logger has started...");
and it show fine on the LogForm textbox. But when I call logForm.log("Some logging info...") On my code inside a thread, it somehow makes my application crash.
How do I deal with this? Please help me demostrate a small code.I am fairly new to C# and programming as a whole so I hope you consider.
Use/call this function in LogForm.log (btw methods in C# are usually capitalized).
private void SetText(string text)
{
Action set = () => yourTextBox.Text = text;
if (yourTextBox.InvokeRequired)
{
yourTextBox.Invoke(set);
}
else
{
set.Invoke();
}
}
If it cannot be set from the current thread yourTextBox.InvokeRequired will be true and the function will work it out. Otherwise it just sets it directly.
Inspiration from this answer at possible duplicate.
Since you are saying the problem persists I'll show a bit more code and try to expain it further.
First of all, I edited the SetText method. I added the private modifier since this function is not indended to be called anywhere outside of LogForm. I also added the curly brackets since that's my preferred style and it also makes sure that the if-statement behaves as expected.
public void Log(string message) {
SetText(message);
//do stuff
}
Both of these methods (Log and SetText) are placed inside the LogForm class. You can now call logForm.Log("Logger has started..."); from any thread as long as your form (containing the textbox) is already initialized. This usually happens in the constructor by calling InitializeComponent(); on the first line.
Without knowing more about your code this is probably as far as I can help you.
I am creating a Form when a certain event occurs. I put this created Form into a static member of the class where it is created. I debugged the code and everything works fine but the Form stays blocked and the user can't do anything in this window. It just appears with a loading animation (see picture). So nothing in the opened window is clickable, you can't even close it.
class CallManagementObserver : CallObserver
{
private static FrmIncomingCall frmCurrentCall;
public CallManagementObserver()
{
}
public void callChangedEvent(CallEv[] events)
{
foreach (CallEv currentEvent in events)
{
switch (currentEvent.getID())
{
case TermConnRingingEv.ID:
// Incoming call
frmCurrentCall = new FrmIncomingCall(currentEvent);
frmCurrentCall.Show();
frmCurrentCall.Update();
break;
case CiscoCallInfoChangedEv.ID:
// User accepted external call on terminal
frmCurrentCall.Close();
break;
case TermConnActiveEv.ID:
// User is in call
frmCurrentCall.Close();
break;
case ConnDisconnectedEv.ID:
// Caller has hung up
frmCurrentCall.Close();
break;
default:
break;
}
}
}
}
}
As you can see above I wrote my own Form class whose code is here:
public partial class FrmIncomingCall : Form
{
Call incomingCall;
CallEv currentEvent;
public FrmIncomingCall(CallEv currentEvent)
{
InitializeComponent();
this.currentEvent = currentEvent;
this.incomingCall = currentEvent.getCall();
}
private void initGui()
{
Connection[] callConnections = incomingCall.getConnections();
Address caller = callConnections[1].getAddress();
lblIncomingCallSource.Text = caller.getName();
}
private void btnAcceptCall_Click(object sender, System.EventArgs e)
{
TermConnEv termConnEv = (TermConnEv)currentEvent;
TerminalConnection termConn = termConnEv.getTerminalConnection();
termConn.answer();
}
private void frmIncomingCall_Load(object sender, System.EventArgs e)
{
initGui();
}
}
When I show the Form via ShowDialog() it is usable but the program stops (since this is what dialogs are made for I guess).
Any ideas what I'm doing wrong? Nothing freezes, the program is running correctly.
Well, your application is poorly designed... It seems that you have no idea of what multithreading is and why you should use it.
If the application hangs forever, then either there is a deadlock (something like the dialog wait on the calling system and the calling system wait on the dialog).
As I have no idea what CallEv is and how it is intended to be used.
Well, if the calling system works and the UI is never updated, then obviously, you never let the UI have time to be updated because your UI thread is 100% of the time using the calling system or waiting on it.
That means that the calling system should probably be used from another thread and that you should have some communication between both threads...
It might also be possible that the calling system might be used in many different ways (as it would be the case for serial port and TCP communication) where one could use what fit most with his application.
Another problem with your code is that when you close a dialog, as far as I know it cannot be used anymore without recreating the dialog as the dialog would be disposed... So you would need to set the formCurrentCall to null and update any affected code. Alternatively, you might hide the form instead and show it again when required.
In any case, it is hard to help you because we don't have any idea of what is CallEv and other classes or events in your code. Also, we have no idea which code is executing when the UI is not responding (or updated). So the question do not have enough informations. In fact, such problem are way easier to debug using a debugger as it is far easier to see what code is run and which line of code take time to execute or even to see which code is not executed.
I'm just trying to learn this thing and in future, wanted to use it in one of my projects.
I have a small Form with a simple Text box, stored in a .Net dll (C#). And here is my class in this dll which contains methods to interact with this Form:
using System;
using System.Collections.Generic;
using System.Text;
namespace ClassLibrary1
{
public class Class1
{
static Form1 dlg = new Form1();
public static void ShowForm()
{
dlg.ShowIcon = true;
dlg.Show();
}
public static void SetText(string MyText)
{
dlg.Text = "Form Text ";
dlg.SetText(MyText);
}
}
}
Successfully loaded this form by referencing this dll into another C# application while calling its method i.e.:
private void button1_Click(object sender, EventArgs e)
{
ClassLibrary1.Class1.ShowForm();
}
And I was able to interact with the form perfectly.
Now loading same in Powershell using:
[Reflection.Assembly]::LoadFile("D:\Playing\ClassLibrary1\ClassLibrary1\bin\Debug\ClassLibrary1.dll")
[ClassLibrary1.Class1]::ShowForm()
Now this is loaded successfully at its default position, but I can't interact with this form i.e. I can't type in its Text Box, neither I can move or even close this form by clicking on its Close (x) button on right corner. Whenever I put my mouse on it, it becomes a HourGlass i.e. waiting for some process .
To verify if form is not hanged, I called SetText at Powershell prompt:
[ClassLibrary1.Class1]::SetText("String from Powershell")
and it worked fine. TextBox received this text properly, but still I can't interact with the form with my mouse.
I feel, I have to manually set its Window Handler i.e. System.Windows.Forms.IWin32Window.
But I don't know which Handler and how to achieve this?
Please guide .... Would really appreciate for any alternative tricks.
You can't show a form from PowerShell using Form.Show() method because it needs a message pump (and it's not provided by PowerShell host process).
Here what you can do to solve this issue:
Use Form.ShowDialog() or Application.Run(), your form will have its own message pump.
It'll be modal then you need to run it in another thread. I suggest to use a background thread and BeginInvoke() in your SetText() method.
Here code to do that (I won't change your code too much so I'll keep it as a singleton instance even if this prevents to display form multiple times). Code is just an example (I wouldn't suggest to use Thread Pool for this task) to illustrate the procedure.
public static void ShowForm()
{
if (dlg != null)
dlg.BeginInvoke(new MethodInvoker(delegate { dlg.Dispose(); }));
ThreadPool.QueueUserWorkItem(delegate(object state)
{
Application.Run(_dlg = new Form1());
});
}
public static void SetText(string text)
{
_dlg.BeginInvoke(new MethodInvoker(delegate { dlg.SetText(text); }));
}
In this way Form1 will be modal in another thread (with its own message pump) and your calling PowerShell thread won't be stopped. Communication between them is still possible via message dispatching (Invoke()/BeginInvoke()).
Please note that SetText() is now asynchronous, to make it synchronous just replace BeginInvoke() with Invoke().
I'm using C# and Winforms in Visual Studio 2010
I have a program with which I am trying to read output through a serial port and print it to the screen. It originally started as a Console program but has now evolved to where we would like to have the output be in a field on a form. I have the code that parses out the output I'm looking for off the serial port written and working, I just need to change the Console.WriteLine to label.text = "";, basically. I have merged the function that listens to the serial port into the GUI code so everything is in the same file.
I'm getting hung up on how to get the function to write to the label, though. It is STATIC so I cant just say 'label.text ='. I tried creating a new form object inside the function to use, and that allowed me to access the control on the form, but doesnt update the form I see at runtime (I'm guessing because I've created a new instance of the form rather than accessed the existing instance?)
I need to have the serial listener run at the same time as the GUI as well, so the GUI label will update with the results it gets from running the function in close to real-time, so Ive tried to set it up to be threaded, with the GUI being one thread that is started by main() and the serial listener being another thread which is started when i click the button to start it. However, I run into the same issue with not being able to access the label in the serial listener thread because it has to be static to be initialized using system.threading.
I'm thinking maybe I need to use a background worker for the serial listener but I have absolutely zero experience with those. Would a background worker be able to update the label on the GUI in real time?
I cant post specific code but heres the general idea:
Main() starts GUIthread
GUI has button to start serial listener
OnClick button starts ListenerThread
ListenerThread outputs to console, want to output to a form label instead
Cant access GUI.Label because Listener is static out of necessity to be threaded
Creating new GUI instance inside Listener allows me to call the controls for that instance, but they dont update the GUI at runtime
have ensured label is public.
The BackgroundWorker class was essentially made just for this.
Just have the DoWork method do your actual work, and ensure that ReportProgess is called while working as needed. You can pass any data as a string (or whatever else, if you want) and then use that value in the ProgressChanged event handler, which the form can handle to update it's UI.
Note that the BackgroundWorker will automatically ensure that the ProgressChanged and RunWorkerCompleted events run in the UI thread, so you don't need to bother with that.
Here's a sample worker:
public class MyWorker//TODO give better name
{
public void DoWork(BackgroundWorker worker)//TODO give better name
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);//to mimic real work
worker.ReportProgress(0, i.ToString());
}
}
}
And here's an example of configuring the background worker. Here I use lambdas both because it's convenient to be able to close over variables (i.e. use variables across each of these anonymous methods) but if you wanted to you could refactor each of the event handlers out into methods.
private void button1_Click(object sender, EventArgs e)
{
var bgw = new BackgroundWorker();
MyWorker worker = new MyWorker();
bgw.WorkerReportsProgress = true;
bgw.DoWork += (s, args) => { worker.DoWork(bgw); };
bgw.ProgressChanged += (s, data) =>
{
label1.Text = data.UserState.ToString();
};
bgw.RunWorkerCompleted += (s, args) =>
{
label1.Text = "All Done!";
};
bgw.RunWorkerAsync();//actually start the worker
}
Note here that none of the controls in the form are public, none of them are static, and I'm not passing any references to my form outside of the class. It's considered best form each Form to be responsible for updating it's own Controls. You shouldn't be allowing anyone else to directly access them. Rather than allowing some other worker class to directly access the label or modify it's text, what's happening is that the worker is simply telling the form, "Hey, I've got some data, you can go update yourself accordingly based on these values." It is then the form that is responsible for updating itself. events are what you use to allow these workers, or other types of child elements (such as other forms you create, for example) to inform the "parent" form that it needs to update itself.
To write to any windows control, you must be on the UI thread. If you have a serial listener running on a different thread, then you need to switch threads before changing the windows control. The BeginInvoke can be handy, http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx.
What I would do, is add a Action to the serial listener that is called whenever the listener wants to display something. And then this Action would call BeginInvoke.
Something like:
static class SerialListner
{
public Action<string> SomethingToDisplay;
void GotSomethingToDisplay(string s)
{
SomethingToDisplay(s);
}
And then somewhere in your windows form
SerialListern.SomethingToDisplay = (s) =>
label.BeginInvoke((Action) () => label.Text = s);
I think you can use a background worker, and they are really easy to use.
In order to use a BackgroundWorker, you'll have to implement at least two events:
backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
there you read your input. It's triggered calling backgroundWorker1.RunWorkerAsync(...)
backgroundWorker1_ProgressChanged(....)
there you update your label. Maybe you'll have to create a delegate to update it.
you can also implement:
backgroundWorker1_RunWorkerCompleted(....)
to let you know when it stop...
Going on what you said about a static listener method and that it used to be a console application, I think a relatively minor modification might be the following:
class Program
{
static void Main(string[] args)
{
// Create a main window GUI
Form1 form1 = new Form1();
// Create a thread to listen concurrently to the GUI thread
Thread listenerThread = new Thread(new ParameterizedThreadStart(Listener));
listenerThread.IsBackground = true;
listenerThread.Start(form1);
// Run the form
System.Windows.Forms.Application.Run(form1);
}
static void Listener(object formObject)
{
Form1 form = (Form1)formObject;
// Do whatever we need to do
while (true)
{
Thread.Sleep(1000);
form.AddLineToTextBox("Hello");
}
}
}
In this case, Form1 is obviously the form class, and Listener is the listening method. The key here is that I'm passing the form object as an argument to the Listen method (via Thread.Start), so that the listener can access the non-static members of the GUI. Note that I've defined Form1.AddLineToTextBox as:
public void AddLineToTextBox(string line)
{
if (textBox1.InvokeRequired)
textBox1.Invoke(new Action(() => { textBox1.Text += line + Environment.NewLine; }));
else
textBox1.Text += line + Environment.NewLine;
}
Note especially that since now the Listener method is running in a separate thread, you need to use the Invoke method on the GUI control to make a change. I've used a lambda expression here, but if you're targeting an earlier version of .net you could use a full method just as easily. Note that my textBox1 is a TextBox with Multiline set to true and ReadOnly set to false (to be similar to a label).
An alternative architecture which may require more work but would probably be more elegant would be to do the opposite dependence relationship: you create the form with a reference to a Listener object. The listener will then raise events which the GUI would be subscribed to in order to update its display.
All I am trying to do is update a textBox (in this case txtInit) from another class. I have been reading a lot about how a UI Thread has to change itself, and something about using a dispatcher. I found an answer on here that seemed close, but I couldnt get it to work for me... it said to try using the line:
MainForm.Dispatcher.Invoke(new Action(delegate() {MainForm.myInstance.txtInit.Text = "Text"};);
In my ServerSide class, I need to send a String to the txtInit textbox on my MainForm.. and that is all I need to do.. thanks for any help.
Classes have nothing to do with threads(which is your problem right now).
Each Control has an Invoke method which will do the right thread synchronization for you.
So you can do
MainForm.myInstance.txtInit.Invoke((sender, args) => (sender as TextBox).Text = "text");
To improve performance you can test(which basically tells you if you're in the same thread) the Control.IsInvokeRequired property.
Another way to do it is by using the SynchronizationContext of the UI thread which you need to capture in the constructor of the form from SynchronizationContext.Current and then do
syncContext.Send((obj) => MainForm.myInstance.txtInit.Text = "Text", null);
I would probably just create a public method on the MainForm that you can pass a string to and let that method set the text for the text box. You can also control whether or not you need to us the Invoke call (different threads) so you never have to worry about coding this in other areas - just call the method and pass the string.
Here is an example:
public partial class Form1 : Form
{
public delegate void UpdateText(string text);
public Form1()
{
InitializeComponent();
}
public void SetTextBoxText(string text)
{
// Check to see if invoke required - (from another thread)
if(textBox1.InvokeRequired)
{
textBox1.Invoke(new UpdateText(this.SetTextBoxText),
new object[]{text});
}
else
{
textBox1.Text = text;
}
}
}
If I understand correctly, it seems you want to access the Windows form elements from another thread or from some asynchronous events. In such case following links may help you.
http://msdn.microsoft.com/en-us/library/ms171728.aspx
Update UI from multiple worker threads (.NET)
Controlling form elements from a different thread in Windows Mobile