C# Update Form from threading & class - c#

I working on project and have problem with threading and update of UI. I read many post around web and this site, but didnt find answer to my problem.
I making program that a waiting for some UDP data on port XXXX. Looping in separated thread. When data come he extract information and put it in buffer, and go back wait another data. Another thread routine wait for data in buffer and send it to email.
Because of functionality and code design I have Form1 class (UI) - main thread, Class2 (rutine for reading UDP and extracting data ) - secodn thread, Class3( reading buffer and send mail ) - third thread, Class4 ( Queue buffer ( read, write, delete items ))...
Problem is that when I want to update UI from one of Class I got BufferOwerflow Exception...It's logical because I first must make instance if Class to start threading and in Class I must make instance of Form to pass data, and then we have infinite loop.
All post I found here are about update UI from thread but in same Class. I understand problem about stack and looping because of instances. I just don't want to have one big main class with huge amount of code inside...
Is it posible to update main Form form another class and threading routine inside?
I write some example from head ( it's not working ) just to have clue about what I am talking.
namespace ThreadUIClass
{
public delegate void updateTextBoxDelegate(string text);
public partial class Form1 : Form
{
void updateTextBox(string text)
{
textBox.Text = text;
}
public Form1()
{
InitializeComponent();
Go();
}
public void Go()
{
textBox.Text = "START";
DoWork dW = new DoWork(); //<= instance of work class
Thread myThread = new Thread(dW.WorkInBeckground);
myThread.IsBackground = true;
myThread.Start();
}
}
public class DoWork
{
public void WorkInBeckground()
{
while (true) //loop and wait for data
{
// Listen some ports and get data
// If (data==ok) update main UI textbox with status
Form1 myForm = new Form1(); //<= instance of main class
myForm.Invoke(new updateTextBoxDelegate(???updateTextBox), new object[] { "BUFFER LOADING..." });
Thread.Sleep(1000);
// Continue looping.....
}
}
}
}

You need to use a threadstart object and pass in a reference to your form object. What you are actually doing is creating a new reference to your mainform, which in turn kicks off a thread, which in turn does the same thing.
So just use a paramaterizedthreadstart object to kick off your thread, passing in "this" and you should be fine.
MSDN ParameterizedThreadStart

Would not be better off using the BackgroundWorker to achieve this? There's a pretty good example here. This class is UI thread aware and you can call directly to the UI components then.

Related

Why doesn't my Textbox update with Thread-safe calls using InvokeRequired?

UpDate1:
More detail: Thread 1 and 2 must be continuously active. Thread 1 is updating its GUI and doing HTTP POSTs. Thread 2 is using HTTPListener for incoming HTTP POSTs, and supplying that data to Thread 1. So the GUI needs to be display with current Textbox values and updated when Thread 2 supplies the data. Will Servy's or another approach allow both Threads to do their work concurrently? It appears the main thread waits for Thread 2 to complete it's work. It then takes the prepWork and does work with it. I coded in Servy's example but I couldn't find a definition for Run() with the Task class. It's library has no such method. I'm using Net 4.0 on VS 2010. Is there an equivalent method to use? Start() didn't compile either and I understand you can only run the Task once. Thanks for any additional assistance you can share.
Original Question:
I've tested code that will successfully kick off my event and update my GUI textbox in an event handler if the event is kicked off in what I understand as the UI Thread 1. When I attempt to call a Thread 1 method Fire() from my independent Thread 2 method PrepareDisplay(), Fire() is called and in turns fires off the event. I put in some Thread-safe call code (modeled from MSDN tutorial on Thread-Safety in WinForms), but the event handler still doesn't update the Textbox. When stepping thru the code, it appears that the InvokeRequired is false. My eventual goal is to pass data from Thread 2 to UI Thread 1 and update the Textboxes with the new data. I don't understand why the Thread-safe code isn't enabling this. Can someone help me understand this better, and what I have neglected? Below is the code:
Thank you very much,
namespace TstTxtBoxUpdate
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Aag_PrepDisplay aag_Prep1 = new Aag_PrepDisplay();
Thread AagPrepDisplayThread = new Thread(new ThreadStart(aag_Prep1.PrepareDisplay));
AagPrepDisplayThread.Start();
while(!AagPrepDisplayThread.IsAlive)
;
Thread.Sleep(1000);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SetOperation());
}
}
}
namespace TstTxtBoxUpdate
{
// Thread 1: UI
public partial class SetOperation : Form
{
private string text;
public event Action<object> OnChDet;
delegate void SetTextCallback(string text);
private Thread demoThread = null;
public SetOperation()
{
InitializeComponent();
OnChDet += chDetDisplayHandler;
}
public void FireEvent(Aag_PrepDisplay aagPrep)
{
OnChDet(mName);
}
private void chDetDisplayHandler(object name)
{
this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe));
this.demoThread.Start();
}
private void ThreadProcSafe()
{
this.SetText("402.5");
}
private void SetText(string text)
{
if(this.actFreqChan1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.actFreqChan1.Text = text;
}
}
}
}
namespace TstTxtBoxUpdate
{
// Thread 2: Data prepare
public class Aag_PrepDisplay
{
#region Fields
private Aag_PrepDisplay mAagPrep;
#endregion Fields
#region Properties
public Aag_PrepDisplay AagPrepDisp;
public Aag_PrepDisplay AagPrep
{
get { return mAagPrep; }
set { mAagPrep = value; }
}
#endregion Properties
#region Methods
public void PrepareDisplay()
{
mAagPrep = new Aag_PrepDisplay();
SetOperation setOp1 = new SetOperation();
setOp1.FireEvent(mAagPrep); // calls Thread 1 method that will fire the event
}
#endregion Methods
}
}
You're getting to the point of calling InvokeRequired when your main thread is still on Thread.Sleep. It hasn't even gotten to the point of creating a message loop yet (which is one in Application.Run) so there is no message loop for Invoke to marshal a call to.
There are all sorts of issues here. You're creating multiple instance of your form, one that you show, and an entirely different form that you're setting the text of. You clearly did not intend to do this; you want to have a single form that you're setting the text for.
Your main thread should not be doing a busywait until your first thread finishes. It likely shouldn't be there at all. If it weren't for the fact that your new thread is creating yet another new thread, the fact that your main thread is blocking until the second thread finishes and the second thread is trying to marshall a call to the main thread, it would normally deadlock. You shouldn't really be creating a second new thread here at all, but this is a case of two bugs "cancelling each other out". It prevents the deadlock, but both are still incorrect, and inhibit your ability to get to a working solution.
You also shouldn't have the Thread.Sleep in the main thread at all. I have no idea what purpose that's trying to achieve.
If you're goal is simply to start some long running work before showing the first form and then to update that form when you have your results, you're doing way more work than you need to do.
To do this we can have our form accept a Task in its constructor representing the completion of the long running work. It can add a continuation to that task to set a label, or a textbox, or do...whatever, with the results of that Task.
public class SetOperation : Form
{
private Label label;
public SetOperation(Task<string> prepWork)
{
prepWork.ContinueWith(t =>
{
label.Text = t.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
Then the main thread simply needs to start a new Task to do the given work in a thread pool thread and pass that in to our form:
[STAThread]
static void Main()
{
Task<string> prepWork = Task.Run(() => DoWork());
Application.Run(new SetOperation(prepWork));
}
private static string DoWork()
{
Thread.Sleep(1000);//placeholder for real work
return "hi";
}
And we're done. Note that DoWork should probably be in its own class designed for handling your business logic; it probably shouldn't be stuck into the Program class.

Cross-threading the hide method

I'm trying to hide a form created on the main thread, from a secondary thread but I obviously get a cross-threading issue when I call the hide method. I'm new to this and don't really have a clue as to how to how to correct this. I've always just created a delegate to invoke my method if it's changing stuff created on the main thread, but I don't know how to do that here for the built-in hide method. Let me know if you need more information.
code:
public partial class MainForm : Form
{
ControlPanelForm m_controlPanel = new ControlPanelForm();
// ....
void MeterThread()
{
while (true)
{
// ....
if (EMOdetected)
{
m_controlPanel.Deinitialize();
m_controlPanel.Hide(); // **** //
}
}
}
}
Basically, my MainForm pulls up a control panel form that does some work. In the background I have a thread running and checking for stuff, one of which is an Emergency Off, at which point I want to shut my control panel down and then hide it.
If I try to invoke it right there,
m_controlPanel.Invoke(new EMOHandler(m_controlPanel.Hide)); // **** //
it doesn't look like it executes anything when i debug it. It seems to just pass over the command. Again, I'm new to this so any and all explanations are welcome.
There's no reason to check InvokeRequired or create an anonymous method. Simply write
mainForm.Invoke(new MethodInvoker(mainForm.Hide));
You haven't given any information code-wise but this is a common pattern for manipulating the UI thread from a non-UI thread.
if (mainForm.InvokeRequired)
{
mainForm.Invoke(new Action(() =>
{
mainForm.Hide();
}));
}
else
mainForm.Hide();
As a simple rule, which you already pointed out:
You should not access one window from another thread.
I would suggest you something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
new Thread(SampleFunction).Start();
}
public void AppendTextBox(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(AppendTextBox), new object[] {value});
return;
}
textBox1.Text += value;
}
void SampleFunction()
{
// Gets executed on a seperate thread and
// doesn't block the UI while sleeping
for(int i = 0; i<5; i++)
{
AppendTextBox("hi. ");
Thead.Sleep(1000);
}
}
}
This is a pretty nice example of how to use MultiThreading, which I got from here.
Though in your case, the method Hide already exists on MainForm and literally waits for you to be invoked, like others already pointed out:
mainForm.Invoke(new MethodInvoker(mainForm.Hide));

Access form variables

When using a basic form application in C# I am having trouble accessing the variabels within it.
So with in the form class I have
public partial class pingerform : Form
{
..
..
private System.Windows.Forms.TextBox textBox2;
public string textBox2Text
{
get { return textBox2.Text; }
set { textBox2.Text = value; }
}
And then in the main application I have
Application.Run(new pingerform());
...
...
pingerform.textBox2Text.text() = str;
but I am told that there is no object reference.
Error 1
An object reference is required for the non-static field,
method, or property
'pingerform.textBox2Text.get' C:\Users\aaron.street\Documents\Visual
Studio 11\Projects\PingDrop\PingDrop\Program.cs 54 21 PingDrop
So I thought I would make the pinger form class static but it wont let me do this?
Error 1
Cannot create an instance of the static class
'PingDrop.pingerform' C:\Users\aaron.street\Documents\Visual Studio
11\Projects\PingDrop\PingDrop\Program.cs 21 29 PingDrop
How can I access the forms properties with out creating a specific instance of the form,
I have a background thread running that I want to update a text filed with in the form at regular intervals?
Cheers
Aaron
You have no choice but to create new instance and either pass it as parameter to the thread, or store it as member of your main Program class.
Example for the second option:
private static pingerform myPingerform = null;
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
myPingerform = new pingerform();
Thread thread = new Thread(new ThreadStart(UpdateTextBox));
thread.Start();
Application.Run(myPingerform);
}
private static void UpdateTextBox()
{
while (true)
{
myPingerform.textBox2.Text = DateTime.Now.Ticks.ToString();
Thread.Sleep(1000);
}
}
And don't forget to change the textbox to be public.
Note: this is simple working solution to the simple case of one background thread accessing the textbox. If you have more threads accessing it, this will break. For best practice methods that require some more work, please read this.
You cannot access properties of an instance without creating that instance, it is nonsense (or VB which is the same). And you have already created the instance which you then passed to Application.Run(). And anyway you cannot do anything with your form after Application.Run() because it returns only when app exits. If you want to do anything with the form you need to do that in some other places. And of course you cannot make the form class static because you need to create instances.
If you need to do something with a form in another thread, you need to pass the form instance to the thread when you create it. Note though that direct messing with GUI elements from non-GUI threads is a bad idea, you should use Control.BeginInvoke().
Please try this:
pingerform myForm = new pingerform();
Application.Run(myForm);
myForm.textBox2Text = "this is text";

update button property from another class and thread in c#

im new to C# language and i would appreciate any help/feedback on the following issue.
Basically, im trying to develop a program such that it has two classes class1(main thread) and class2. class1 has a button and class2 starts a thread and do some calculations. How can i update any property of the button in class1(main) from class2 within the thread in class2?
here is an example i would like to do
// CLASS 1 is a win form
class1:form
{
public btn_click()
{
// call function startthread from class2
btn.Enabled = false;
startthread()
}
}
// CLASS 2 is not a winform
class2
{
public startthread()
{
Thread worker = new Thread(doCalculation)
}
public doCalculation()
{
// do some calculation then
// blahhh
// blahhh
// HERE I NEED SOMEHOW ENABLED MY BUTTON
}
}
This depends a bit on the UI framework you're using. As an example, say you wanted to update the text written on the button.
If you're using WPF, you could use:
class1Instance.TheButton.Dispatcher
.BeginInvoke( (Action) () => class1Instance.TheButton.Content = "Foo" );
With Windows Forms, you'd use:
class1Instance.TheButton
.BeginInvoke( (Action) () => class1Instance.TheButton.Text = "Foo" );
You have to bring the modifying code back onto the the main thread, regardless whether its WPF or Winforms, cross-thread access to the UI is either explicitly disallowed (usually) or strongly discouraged and buggy.
There are a lot of ways to do this, in Winforms I typically use Control.Invoke like this:
public void UpdateMyTextBox(string NewText)
{
if(InvokeRequired)
Invoke(new Action<string>(UpdateMyTextBox), NewText);
else
myTextBox.Text = NewText;
}
In this example, the InvokeRequired property will check to see if it's being called on the UI thread, and if not, we will call Invoke which will place the request onto the UI thread.
In WPF, you watch to use the Dispatcher, and you do so in a very similar way:
public void UpdateMyWpfTextBox(string NewText)
{
if(!CheckAccess())
Dispatcher.Invoke(new Action<string>(UpdateMyWpfTextBox), NewText);
else
myTextBox.Text = NewText;
}
Not to sure what your trying to do here, but this is best an answer I can come up with, considering the questions. I'm going to assume this is a winforms application
Class2 is going to need a reference to the button in Class 1. So you'll need to pass that to it.
Once Class2 has reference to the button you can attempt to modify the property as
btn.Text = "New Text";
I'm going to assume again that you've tried this route, got an exception and so you posted your question. The reason you got the exception was that your btn and your class2 are running in separate threads. To get around this, you can call invoke on the button. I often handle this like this.
private void SetButtonText(string newText) {
if (btn.InvokeRequired) {
Invoke((MethodInvoker)(() => SetButtonText(newText)));
}
else {
btn.Text = newText;
}
}

Issue with UI access from Background worker

I'm shifting a project over from winforms to WPF. When my code was based on WinForms I used (this.InvokeRequired) to check if the thread has access. Now I use the following code based on my Mainform :
// this is the delegate declaration to Allow Background worker thread to write to Log Output
delegate void LogUpdateCallBack(String LogMessage);
// method to update the Log Window from the Background Thread
public void LogUpdate(String LogMessage)
{
Console.WriteLine("Entering");
if (!Application.Current.Dispatcher.CheckAccess())
{
Console.WriteLine("Thread doesn't have UI access");
LogUpdateCallBack callback = new LogUpdateCallBack(LogUpdate);
Application.Current.Dispatcher.Invoke(callback, LogMessage);
}
else
{
Console.WriteLine("Thread has UI access");
listBox_Output.Items.Add(LogMessage);
Console.WriteLine(LogMessage);
// listBox_Output.TopIndex = listBox_Output.Items.Count - 1;
}
Console.WriteLine("Exiting");
}
The issue I have is that the Listbox isn't updating. There are no errors or exceptions, I've tried updating other UI controls. The LogMessage is writing into the Output window so I'm stumped.
Here's sample Console output :
Entering
Thread doesn't have UI access
Entering
Thread has UI access
My LogMessage is output here
Exiting
Exiting
Entering
Thread doesn't have UI access
Entering
Thread has UI access
My LogMessage is output here
Exiting
Exiting
I've tried updating other UI controls just to check if it's an issue with my Listbox but with no luck.
Other than switching over to CheckAccess() the only other major change I've made in the new WPF code is to base all the code running in the Background worker in another Class .. I'm not sure if this could be part of the issue ?.
--
#JonRaynor
I tried your idea :
Application.Current.Dispatcher.BeginInvoke(new LogUpdateCallBack(LogUpdate), LogMessage)
However my Listbox still isn't updating, if I output
Console.WriteLine(listBox_Output);
I see the list box array growing :
System.Windows.Controls.ListBox Items.Count:2020
System.Windows.Controls.ListBox Items.Count:2021
System.Windows.Controls.ListBox Items.Count:2022
System.Windows.Controls.ListBox Items.Count:2023
System.Windows.Controls.ListBox Items.Count:2024
System.Windows.Controls.ListBox Items.Count:2025
But no change in the form. This is very confusing !.
I just started on WPF as well and had to relearn from the old WinForms way. I've been using BeginInvoke() and this type of syntax on my screens (forms)...
public delegate void WorkCompleted();
/// <summary>
/// Marks the end of the progress
/// </summary>
private void ProgressComplete()
{
if (!this.Dispatcher.CheckAccess())
{
this.Dispatcher.BeginInvoke(new WorkCompleted(ProgressComplete), System.Windows.Threading.DispatcherPriority.Normal, null);
}
else
{
this.buttonClose.Visibility = Visibility.Visible;
this.progressBarStatus.IsIndeterminate = false;
}
}
I finally solved this.
My issue was that as I was calling the LogUpdate method from both another thread AND another class, therefore I needed to pass a reference to my main form which contained the listbox into this class rather than create a new instance of the main form in the secondary class.
so rather than have this declaration in my secondary class :
public MainWindow MainForm = new MainWindow();
I needed to pass a reference of the form into the method in the secondary class :
public void Plot(BackgroundWorker worker, MainWindow MainForm)
In WPF, all the UI controls are derived from DispatcherObject. That means your ListBox control is, itself, a dispatch-capable object. Try invoking the Dispatcher on that object instead of relying on the form itself. Take out the parameter on your delegate (because you have access to the LogMessage variable anyway) and it'd look something like:
public void LogUpdate(string LogMessage)
{
Console.WriteLine("Entering");
if (listBox_Output.Dispatcher.CheckAccess())
{
listBox_Output.Dispatcher.Invoke(new LogUpdateCallBack(delegate
{
listBox_Output.Items.Add(LogMessage);
}));
Console.WriteLine(LogMessage);
}
}

Categories