Ok, well I have been at it for a while now and I decided to just use threads. I am making a syntax highlighter but I keep getting terrible performance with the file sizes that it will usually be used for. So I made two forms, the first shows the file in plain text and has a button that says "openincolor" when you click that I start a new thread as such
private void button1_Click(object sender, EventArgs e)
{
ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
theThread.Start();
}
public class ColoringThread
{
string text;
public ColoringThread(string initText)
{
text = initText;
}
public void OpenColorWindow()
{
Form2 form2 = new Form2(text);
form2.ShowDialog();
}
};
I want this form to send back a message each time it has complete say x lines of coloring. Then I will take that and figure out the progress and display it to the user.
How might I go about sending a message, or event(...? can I do that) to my first form to let it know of the others progress?
One very simple way to do this is with BackgroundWorker. It already provides an event to report progress.
How about something like this? This adds an event to the ColoringThread class which is subscribed to by the calling class.
private void button1_Click(object sender, EventArgs e) {
ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
colorer.HighlightProgressChanged += UpdateProgress;
Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
theThread.Start();
}
private void UpdateProgress(int linesComplete) {
// update progress bar here
}
public class ColoringThread
{
string text;
public delegate void HighlightEventHandler(int linesComplete);
public event HighlightEventHandler HighlightProgressChanged;
public ColoringThread(string initText) {
text = initText;
}
public void OpenColorWindow() {
Form2 form2 = new Form2(text);
form2.ShowDialog();
int linesColored = 0;
foreach (String line in text.Split(Environment.NewLine)) {
// colorize line here
// raise event
if (HighlightProgressChanged != null)
HighlightProgressChanged(++linesColored);
}
}
};
You can pass an object as argument to the Thread.Start and share your data between the current thread and the initiating thread.
Here is a good example:
How to share data between different threads In C# using AOP?
Or you can use BackgroundWorker which has ReportProgress
What you need is System.Windows.Threading.Dispatcher's BeginInvoke method. You can't directly modify a WPF object from your background thread, however you can dispatch a delegate to do that.
In your derived Window class object you have the Property Dispatcher, so you use it as follows:
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(status) => { StatusTextBox.Text = status },
thestatus
);
I'm sorry that I can't test that currently and I don't have the project here, where I did that. But I'm sure it will work, good luck ;)
Update: Oops, you're using Form's... I've written about WPF, sorry.
Related
I am dynamically generating some labels with the help of data from the database. Then I added some events to these dynamically generated labels. I am trying to run the click events of labels in a new thread. But I couldn't figure it out so far. Any leads will be really helpful.
private void LoadData(string DeviceCode)
{
//here i am generating the labels (not shown)
//adding the event handler
lb_DeviceData[i].Click += new EventHandler(CalculateClick);
lb_DeviceData[i]..Tag = i;
}
private void CalculateClick(object sender, EventArgs e)
{
Label MyLabel = (Label)sender;
Thread t = new Thread(ThreadedMethodForCalc); //can i pass MyLabel into this method?
t.Start();
}
private void ThreadedMethodForCalc()
{
//complex calculation here
// is it possible to pass 'MyLabel' in this method
}
Well, the simplest way is to change the signature of the method and use closure:
Thread t = new Thread(() => ThreadedMethodForCalc(MyLabel));
t.Start();
...
private void ThreadedMethodForCalc(Label myLabel)
{
}
Btw to update the UI in another thread you can use Control.Invoke(...).
For more information check this article from MSDN.
Yo can your method accepting label
private void ThreadedMethodForCalc(object label)
{
Label MyLabel = (Label)label;
//complex calculation here
// is it possible to pass 'MyLabel' in this method
}
And then use in thread Start
t.Start(sender);
But you aren't allowed to modify the control properties outside the UI thread
I have some controls on the form of the Windows Forms application and I need to update its' texts at run-time from several threads.
Is it safe to just call BeginInvoke method like this:
BeginInvoke((MethodInvoker)delegate()
{
this.label.Text = "Some text";
});
from several threads at the same time? Should I do any additional synchronization in this case? Will it be processed by the same thread one by one and is this order guaranteed?
Thanks in advance.
Calling BeginInvoke puts the delegate on to the message queue to be processed by the UI thread, it will process the queue handling the messages one by one. So no, you do not need to do any additional synchronization (as long as the delegate is not accessing any resources that can't be accessed from the UI thread).
As for order, it is not guaranteed they will be processed in order but in practice most of the time the delegates will be processed in the order they where put in to the queue.
To address the question in the comments, instead of using multiple BeginInvoke calls you should be able to get away with just one.
You never really explained what your animation was so I am going to assume it is going to be that this.label will swap between ., .. and ... then you store the result text in this.label when you are done.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
animationTimer = new System.Windows.Forms.Timer();
animationTimer.Interval = 500;
animationTimer.Tick += animationTimer_Tick;
}
private System.Windows.Forms.Timer animationTimer;
private int dots = 0;
void animationTimer_Tick(object sender, EventArgs e)
{
//Make 1, 2, or 3 dots show up. This runs on the UI thread so we don't need to invoke.
this.label.Text = new String('.', dots + 1);
//Add one then reset to 0 if we reach 3.
dots += 1;
dots = dots % 3;
}
private void button1_Click(object sender, EventArgs e)
{
animationTimer.Start();
Task.Run(() => DoSomeSlowCalcuation());
}
private void DoSomeSlowCalcuation()
{
Thread.Sleep(5000);
this.BeginInvoke((MethodInvoker)delegate()
{
//We stop the timer before we set the text so the timer will not overwrite it.
animationTimer.Stop();
this.label.Text = "Some text";
});
}
}
This code is just a example to get my point across, if I where doing this I would use async/await for the button click and not use BeginInvoke at all.
private async void button1_Click(object sender, EventArgs e)
{
animationTimer.Start();
var result = await Task.Run(() => DoSomeSlowCalcuation());
animationTimer.Stop();
this.label.Text = result;
}
private string DoSomeSlowCalcuation()
{
Thread.Sleep(5000);
return "Some text";
}
I had write a little application on c# to reading some plc data by using ethernet protocol. Ethernet socket, open and close are stored inside a .dll library.
Now, i using this public method:
public static string readdata()
{
try
{
...
return (plcdata());
}
catch
{}
}
My doubt: if the plcdata() (that is a method of a .dll) waiting a few second (for istance slow comunication ...) my application may be frozen.
So, i try to add a EventHandler on string returned like this:
private static TextBox auxDataTextBox = new TextBox();
public static void goRead()
{
auxDataTextBox.TextChanged += new EventHandler(auxDataIncoming);
auxDataTextBox.Text = plcdata();
}
private static void auxDataIncoming(object sender, EventArgs e)
{
// Do something
}
In this case when the "plcdata()" changed, the auxDataIncoming will be raise.
It is correct? Or is better make a timeout control? Or make new thread?
Thanks a lot for yours opinion
Your change won't make a difference, it' still all running on the UI thread. To make plcdata() a non-blocking call you would need to fire it off on another thread e.g.
private static TextBox auxDataTextBox = new TextBox();
public static void goRead()
{
auxDataTextBox.TextChanged += new EventHandler(auxDataIncoming);
Task.Factory.StartNew(() => {
return plcData();
}).ContinueWith(task => {
auxDataTextBox.Text = task.Result;
}, null, TaskContinuationOptions.NotOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
}
private static void auxDataIncoming(object sender, EventArgs e)
{
// Do something
}
This will not unfreeze your application. The effect will be exactly the same. This is because you are still running the plcdata on your UI thread.
The whole event structure you set up does not make sense at all.
You should look into multithreading. A very easy way to do this is using a BackgroundWorker.
I have a c# application that uses a background worker thread, and quite successfully updates the UI from the running thread. The application involves shortest path routing on a network, and I display the network and the shortest path, on the UI, as the background worker proceeds. I would like to allow the user to slow down the display through use of a slider, while the application is running.
I found this as a suggestion, but it is in vb.net, I am not clear on how to get it to work in c#.
How can the BackgroundWorker get values from the UI thread while it is running?
I can pass the value of the slider to the backgroundworker as follows:
// Start the asynchronous operation.
delay = this.trackBar1.Value;
backgroundWorker1.RunWorkerAsync(delay);
and use it within the backgroundworker thread, but it only uses the initially-sent value. I am not clear on how to pick up the value from inside the backgroundworker when I move the slider on the UI.
I have previously used multiple threads and delegates, but if it is possible to utilize the background worker, I would prefer it for its simplicity.
5/10/2012
Thanks to all for your responses. I am still having problems, most likely because of how I have structured things. The heavy duty calculations for network routing are done in the TransportationDelayModel class. BackgroundWorker_DoWork creates an instance of this class, and then kicks it off. The delay is handled in TransportationDelayModel.
The skeleton of code is as follows:
In UI:
private void runToolStripMenuItem1_Click(object sender, EventArgs e)
{
if (sqliteFileName.Equals("Not Set"))
{
MessageBox.Show("Database Name Not Set");
this.chooseDatabaseToolStripMenuItem_Click(sender, e);
}
if (backgroundWorker1.IsBusy != true)
{
// Start the asynchronous operation.
delay = this.trackBar1.Value;
// pass the initial value of delay
backgroundWorker1.RunWorkerAsync(delay);
// preclude multiple runs
runToolStripMenuItem1.Enabled = false;
toolStripButton2.Enabled = false;
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (!backgroundWorkerLaunched)
{
// instantiate the object that does all the heavy work
TransportationDelayModel TDM = new TransportationDelayModel(worker, e);
// kick it off
TDM.Run(sqliteFileName, worker, e);
backgroundWorkerLaunched = true;
}
}
The TransportationDelayModel constructor is:
public TransportationDelayModel(BackgroundWorker worker, DoWorkEventArgs e)
{
listCentroids = new List<RoadNode>();
listCentroidIDs = new List<int>();
listNodes = new List<RoadNode>();
listNodeIDs = new List<int>();
listRoadLink = new List<RoadLink>();
roadGraph = new AdjacencyGraph<int, RoadLink>(true); // note parallel edges allowed
tdmWorker = worker;
tdmEvent = e;
networkForm = new NetworkForm();
}
so I have the tdmWorker, which allows me to pass information back to the UI.
In the internal calculations in TransportationDelayModel, I sleep for the delay period
if (delay2 > 0)
{
tdmWorker.ReportProgress(-12, zzz);
System.Threading.Thread.Sleep(delay2);
}
so the problem seems to be how to pass an updated slider value from the UI back to the object that is executing in the background worker. I have tried a number of combinations, sort of thrashing around, to no avail, either nothing happens or I get a message about not being allowed to access what is happening on the other thread. I realize that if I were doing all the work in the DoWork event handler, then I should be able to do things as you suggest, but there is too much complexity for that to happen.
Again, thank you for your suggestions and help.
6/2/2012
I have resolved this problem by two methods, but I have some questions. Per my comment to R. Harvey, I have built a simple application. It consists of a form with a run button, a slider, and a rich text box. The run button launches a background worker thread that instantiates an object of class "Model" that does all the work (a simplified surrogate for my TransportationModel). The Model class simply writes 100 lines to the text box, incrementing the number of dots in each line by 1, with a delay between each line based on the setting of the slider, and the slider value at the end of the line, something like this:
....................58
.....................58
......................58
.......................51
........................44
.........................44
The objective of this exercise is to be able to move the slider on the form while the "Model" is running, and get the delay to change (as in above).
My first solution involves the creation of a Globals class, to hold the value of the slider:
class Globals
{
public static int globalDelay;
}
then, in the form, I update this value whenever the trackbar is scrolled:
private void trackBar1_Scroll(object sender, EventArgs e)
{
Globals.globalDelay = this.trackBar1.Value;
}
and in the Model, I just pick up the value of the global:
public void Run(BackgroundWorker worker, DoWorkEventArgs e)
{
for (int i = 1; i < 100; i++)
{
delay = Globals.globalDelay; // revise delay based on static global set on UI
System.Threading.Thread.Sleep(delay);
worker.ReportProgress(i);
string reportString = ".";
for (int k = 0; k < i; k++)
{
reportString += ".";
}
reportString += delay.ToString();
worker.ReportProgress(-1, reportString);
}
}
}
This works just fine.
My question: are there any drawbacks to this approach, which seems very simple to implement and quite general.
The second approach, based on suggestions by R. Harvey, makes use of delegates and invoke.
I create a class for delegates:
public class MyDelegates
{
public delegate int DelegateCheckTrackBarValue(); // create the delegate here
}
in the form, I create:
public int CheckTrackBarValue()
{
return this.trackBar1.Value;
}
and the Model class now has a member m_CheckTrackBarValue
public class Model
{
#region Members
Form1 passedForm;
public static MyDelegates.DelegateCheckTrackBarValue m_CheckTrackBarValue=null;
#endregion Members
#region Constructor
public Model(BackgroundWorker worker, DoWorkEventArgs e, Form1 form)
{
passedForm = form;
}
When the background thread is launched by the run button, the calling form is passed
private void button1_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (!backgroundWorkerLaunched)
{
// instantiate the object that does all the heavy work
Model myModel= new Model(worker, e, this);
Model.m_CheckTrackBarValue = new MyDelegates.DelegateCheckTrackBarValue(this.CheckTrackBarValue);
// kick it off
myModel.Run(worker, e);
backgroundWorkerLaunched = true;
}
}
Finally, in the Model, the Invoke method is called on the passed form to get the value of the trackbar.
public void Run(BackgroundWorker worker, DoWorkEventArgs e)
{
for (int i = 1; i < 100; i++)
{
int delay = (int)passedForm.Invoke(m_CheckTrackBarValue,null); // invoke the method, note need the cast here
System.Threading.Thread.Sleep(delay);
worker.ReportProgress(i);
string reportString = ".";
for (int k = 0; k < i; k++)
{
reportString += ".";
}
reportString += delay.ToString();
worker.ReportProgress(-1, reportString);
}
}
This works as well. I kept getting an error until I made the member variable static, e.g.
public static MyDelegates.DelegateCheckTrackBarValue m_CheckTrackBarValue=null;
My questions on this solution: Are there advantages to this solution as regards to the previous version? Am I making things too complicated in the way I have implemented this? Why does m_CheckTrackBarValue need to be static.
I apologize for the length of this edit, but I thought that the problem and solutions might be of interest to others.
You have to pass the TrackBar object to the BackgroundWorker, not delay. delay doesn't change once you set it.
To simplify the needed Invoke(), you can use a helper method, such as this one:
Async.UI(delegate { textBox1.Text = "This is way easier!"; }, textBox1, true);
I will assume that you are already familiarized with cross-thread invocation to update the UI. So, the solution is very simple: in your worker thread, after each iteration, invoke the UI to get the slider thumb position.
To use a backgroundworker, you add a method to the DoWork property, like this:
this.backgroundWorker1.WorkerSupportsCancellation = true;
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
In the DoWork method, you need to check the variable where the updated delay is set.
This could be an integer field that is available on the containing Form or UI control, or it could be the TrackBar itself.
I have some unsolved issue with threads. It's my first time doing it. I know how to use one thread to write in a textBox, but I have no idea how to use two of them to do the job. Anyone have a clue what do I have to do to be able to use two threads to write to the same textBox, but not in the same time. Thank you.
Here's an example that uses two threads to write random numbers to a multi-line text box. As Brandon and Jon B noted, you need to use Invoke() to serialize the calls to the GUI thread.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Random m_random = new Random((int)DateTime.Now.Ticks);
ManualResetEvent m_stopThreadsEvent = new ManualResetEvent(false);
private void buttonStart_Click(object sender, EventArgs e)
{
Thread t1 = new Thread(new ThreadStart(ThreadOne));
Thread t2 = new Thread(new ThreadStart(ThreadTwo));
t1.Start();
t2.Start();
}
private void ThreadOne()
{
for(;;)
{
int n = m_random.Next(1000);
AppendText(String.Format("One: {0}\r\n", n));
if(m_stopThreadsEvent.WaitOne(n))
{
break;
}
}
}
private void ThreadTwo()
{
for(;;)
{
int n = m_random.Next(1000);
AppendText(String.Format("Two: {0}\r\n", n));
if(m_stopThreadsEvent.WaitOne(n))
{
break;
}
}
}
delegate void AppendTextDelegate(string text);
private void AppendText(string text)
{
if(textBoxLog.InvokeRequired)
{
textBoxLog.Invoke(new AppendTextDelegate(this.AppendText), new object[] { text });
}
else
{
textBoxLog.Text = textBoxLog.Text += text;
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
m_stopThreadsEvent.Set();
}
}
Another option is to use a Thread Callback method. This is a method that exists on the main thread, but when creating a new thread you pass a handle/reference to this method. This allows the second thread to call the method on the main thread and the functionality to update/check the textbox would sit there.
Look into passing delegates between threads.
One option you could do, is push messages onto a Queue object and use a timer on the windows form to read messages from this queue and write to the textbox.
In order to make everything nice and threadsage you could lock the Queue object when reading and writing to it.
For example:
private Queue<string> messages = new Queue<string>();
/// <summary>
/// Add Message To The Queue
/// </summary>
/// <param name="text"></param>
public void NewMessage(string text)
{
lock (messages)
{
messages.Enqueue(text);
}
}
private void tmr_Tick(object sender, EventArgs e)
{
if (messages.Count == 0) return;
lock (messages)
{
this.textBox.Text += Environment.NewLine + messages;
}
}
safest approach is to only have 1 thread be able to work on the text box (or any gui object), have any other threads that need to perform an action on the text box communicate their needs to the thread that controls the text box.
so your question becomes how to communicate between threads, this is going to be language/OS specific so you need to provide more information.
This MSDN Article explains how to make thread safe calls to windows form controls.
You can only access GUI components from the main thread. To write to a textbox from another thread, you need to use BeginInvoke().