i am trying to make a basic IRC client...but my problem is getting the text to display in a RTF box without it lagging
i settled on using threading, and i want to update the RTF box in a thread, but i cant because it gives me errors about the RTF box element not being static?
any insight? i will paste code if you guys want it
ok here is the code (do edits bump?)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Net;
namespace IrcClient
{
public partial class mainWindow : Form
{
static IRC client;
static string newLine, oldLine;
public mainWindow()
{
InitializeComponent();
}
private void main()
{
}
private void mainWindow_Load(object sender, EventArgs e)
{
client = new IRC("irc.freenode.net" ,6667, "jimi__hendrix");
new Thread(new ThreadStart(update)).Start();
}
private static void update()
{
newLine = client.sr.ReadLine();
Thread.Sleep(50);
}
private void btnSend_Click(object sender, EventArgs e)
{
client.privmsg(inputBox.Text);
messageBox.AppendText(inputBox.Text + "\n");
inputBox.Text = "";
}
private void timer1_Tick(object sender, EventArgs e)
{
if (newLine != oldLine)
{
messageBox.AppendText(newLine + "\n");
oldLine = newLine;
}
}
}
class IRC
{
TcpClient connection;
NetworkStream stream;
public StreamWriter sw;
public StreamReader sr;
string nick;
public IRC(string server, int port, string Nick)
{
try
{
connection = new TcpClient(server, port);
stream = connection.GetStream();
sr = new StreamReader(stream);
sw = new StreamWriter(stream);
nick = Nick;
sw.WriteLine("PASS " + "caruso11");
sw.Flush();
sw.WriteLine("NICK " + nick);
sw.Flush();
sw.WriteLine("USER Jimi__Hendrix 8 irc.freenode.net :Jimi__Hendrix");
sw.Flush();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
public void privmsg(string msg)
{
sw.WriteLine(msg);
sw.Flush();
}
public void parse(string msg)
{
}
}
}
some of the methods are blank and some code could be cleaned up, but i want to get this done first...also, theres the code generated by visual studios that sets up the window
In general, trying to update a Control from a thread other than the main thread will not work, due to limitations of Windows. If you need to call a method or set a property on the RTF box from your worker thread, you'll probably need to use Invoke or BeginInvoke. This might look like the following:
void MyThreadMethod()
{
// stuff
this.rtfBox.BeginInvoke( (MethodInvoker)(()=> this.rtfBox.Text = "foo") );
// stuff
}
Edit: As others point out, if this is actually failing to compile due to an error about the control not being static, you're probably trying to reference a member variable from a static function, which is illegal. Posting code will help narrow down the problem.
I believe you are using Windows Forms. Check out this text about it, there is some remarks that you have to take care before making a thread accessing forms elements directly.
If this is not exactly your problem, please elaborate more the question.
RULE: You should not access one window from another thread.
Use this technique:
from your thread raise an event to update the RTF (with required text etc data)
on the GUI thread use the following:
use the "InvokeRequired" property to verify call is from GUI thread or not
Use the Invoke method (you will need a delegate for this)
BeginInvoke also works but the only problem is it does not really guarantee immediate start (it uses threadpool). I prefer to use the Invoke method instead.
Others have already mentioned what the problem you are experienced is, and how to solve it.
Not that having a separate worker thread is a bad idea, it seems odd that you need multiple threads because of performance reasons. It seems that for as something as simple as an IRC client, you should be able to do everything on one thread without any UI sluggishness. Maybe you can post some code so that we can see what the root of the problem is?
The RichTextBox is on the UI thread so you're not going to be able to access the RichTextBox unless your on the UI thread. How are you loading the content? How big is the content? Is the lag coming from loading the content or the RTF box parsing it?
You're probably referencing a member variable of the form, instead of a static variable, thus the error.
Anyway this is quite a wrong approach, since UI elements can only be updated from the UI thread, otherwise you'll get a cross-thread operation exception. So you'll need to do Invoke() to the set-text method on the UI thread, which will eventually 'lag' too.
Not sure what you mean by 'lagging', you may try making your parent form double-buffered to reduce flicker.
Related
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'm a brand new n00bie in visual c# and I ran into a weird obstacle that is driving me CRAZY!! Here is the code in question (yes, an Hello World program):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Equals("Goodbye Cruel World"))
{
textBox1.Text = ("Hello World!");
}
else { textBox1.Text = ("Goodye Cruel World"); }
}
}
}
I also tried to use textBox1.Text=="Goodbye Cruel World"; as the evaluation parameter for the if statement getting no errors in the compiler (by the way I am using Visual Studio 2012 Ultimate)
The program runs fine. I initalised the textbox text property as "Hello World!" using the Design GUI of VS. The problem I am facing is that the code works only the first time the user clicks the button. Any time after the button does NOTHING.
I debugged the code and I made sure that the textbox text property is appropriately changed the first time the user clicks the button. When the user clicks the button a second time (or any time after that for that matter) once the code gets to the if statement it skips it, as if the evaluation of the expression within is FALSE. In fact, keeping up with the debug tool, the button keeps executing only the code within the else block, even though I know for a fact that the TextBox.Text property that I am working with has been appropriately changed before.
What am I missing here??? Why doesn't the button just switches the textbox text value between the two strings I hardcoded within?
You are using three strings, not two. "Goodye Cruel World" is not equal to "Goodbye Cruel World". Hence, you cannot expect any kind of "string swapping" behaviour whatsoever from this source code.
Lesson to learn: Do not use the same string at different points of your code. Instead, create a constant string variable which has that value, and then use it every time you need it. For example code see Habib's answer.
That is a case of defining string constant in your code:
public partial class Form1 : Form
{
private const string GOODBYE = "Goodbye Cruel World";
private const string HELLO = "Hello World!";
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Equals(GOODBYE ))
{
textBox1.Text = HELLO;
}
else { textBox1.Text = (GOODBYE ); }
}
}
If you are using same string in multiple places then its better if you define it as a const and use that everywhere in your code, this will help you in reducing errors like the one you have now (Goodye is Goodbye) and it is also easier to change/maintain.
Check the spelling of Goodye in the else clause. The condition will always be false.
I'm doing an application that does some sort of scanning (it checks availability of URL's through a short list) and depending on the result, it adds to one or another listbox. if it exists, it goes to lstOK, else, it goes to lst404.
The issue is that these web checks take time (specially when it is OK), it takes an awfully long time, and inserts all the items in the listboxes in the end, while the form is "not responding" and nothing appears or can be clicked or displays any interaction.
Is there a way for the form to be still usable and the listboxes to update on the go ?
This should be simple, I just don't know it (yet)
I'm using C# in Visual Studio
--[update]--
The whole url checking is in one single function Start()
try the background worker
If this is a desktop application that is performing these "web checks" then you can use a BackgroundWorkerThread to perform the processing, and get the results.
Or you could do something like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace ThreadWithDataReturnExample
{
public partial class Form1 : Form
{
private Thread thread1 = null;
public Form1()
{
InitializeComponent();
thread1 = new Thread(new ThreadStart(this.threadEntryPoint));
Thread1Completed += new AsyncCompletedEventHandler(thread1_Thread1Completed);
}
private void startButton_Click(object sender, EventArgs e)
{
thread1.Start();
//Alternatively, you could pass some object
//in such as Start(someObject);
//With apprioriate locking, or protocol where
//no other threads access the object until
//an event signals when the thread is complete,
//any other class with a reference to the object
//would be able to access that data.
//But instead, I'm going to use AsyncCompletedEventArgs
//in an event that signals completion
}
void thread1_Thread1Completed(object sender, AsyncCompletedEventArgs e)
{
if (this.InvokeRequired)
{//marshal the call if we are not on the GUI thread
BeginInvoke(new AsyncCompletedEventHandler(thread1_Thread1Completed),
new object[] { sender, e });
}
else
{
//display error if error occurred
//if no error occurred, process data
if (e.Error == null)
{//then success
MessageBox.Show("Worker thread completed successfully");
DataYouWantToReturn someData = e.UserState as DataYouWantToReturn;
MessageBox.Show("Your data my lord: " + someData.someProperty);
}
else//error
{
MessageBox.Show("The following error occurred:" + Environment.NewLine + e.Error.ToString());
}
}
}
#region I would actually move all of this into it's own class
private void threadEntryPoint()
{
//do a bunch of stuff
//when you are done:
//initialize object with data that you want to return
DataYouWantToReturn dataYouWantToReturn = new DataYouWantToReturn();
dataYouWantToReturn.someProperty = "more data";
//signal completion by firing an event
OnThread1Completed(new AsyncCompletedEventArgs(null, false, dataYouWantToReturn));
}
/// <summary>
/// Occurs when processing has finished or an error occurred.
/// </summary>
public event AsyncCompletedEventHandler Thread1Completed;
protected virtual void OnThread1Completed(AsyncCompletedEventArgs e)
{
//copy locally
AsyncCompletedEventHandler handler = Thread1Completed;
if (handler != null)
{
handler(this, e);
}
}
#endregion
}
}
If it's a web form look into AJAX.NET. There are several controls (UpdatePanel being one off the top of my head) that will help you do this.
Take a look at the toolkit.
EDIT: Only for web apps.
Application.DoEvents(); will do all the events that have happened up to that point.
so in your loop, after each website is checked, for example. do Application.DoEvents();
on the other hand if you just want to refresh your listboxes it'll be listboxname.Refresh();
both of these options, however will still have a time where it freezes while the website is pinged, unless you do many of them, which i dont suggets doing.
both methods also only use a single thread and is very linear.
The best option would be to create a new thread to do the tests on, or use a background worker that can do the tests on a seperate thread, so the events of the form can be handled instantly without a need to wait.
Manually controlling another thread shouldnt be too difficult.
here's an example.
using System.Threading;
public class MultiThreadingClass
{
private void FunctionForNewThread()
{
//do stuff
}
private void FunctionWithParameter(object param)
{
//Should do checks with typeof() on param before casting
int convertedparam = (int)param;
//do stuff
}
Thread t, t2;
static void Main()
{
ThreadStart ts = new ThreadStart(FunctionForNewThread);
t = new Thread(ts);
t.Start();
int x = 5;
ParameterizedThreadStart pts = new ParameterizedThreadStart(FunctionWithParameter);
t2 = new Thread(pts);
t2.Start(x);
}
}
it may be important to note here that you should never add a Thread as a local variable that will dissapear, as you can only really get the thread instance back by doing Thread.CurrentThread in the function which was called by the new thread, but if that thread has already locked up, you have a bit of a problem there :)
To easily handle Threads in a global variable either create an Array of threads and call Thread.Abort(); on each running thread when the program closes, or use the ThreadPool class in System.Threading.
I've been tasked with working on a download queuing system but I'm a bit confused about where to start.
Essentially what we need to do is to have something like a download manager (but not as fully blown). We have about 20-100 files to download, we give the user a UI (with a listview) to allow them to pause, stop, or move the priorty of jobs around.
What I'm confused about is the data-structure to use, a Priority Queue seems like the way to go from my research, but I'm confused about how to make it work. Do I have a background thread that peeks into the Queue and picks up the next task and carries it forward? I need to provide progress too as the files are being downloaded - they are quite large, sometimes 120Mb (but its local, so no more than 10mins).
Sometimes they need to pause a job and shove a job higher up in the queue as its deemed urgent.
Its not a download manager, so no throttling etc issues. How do people write things like this?
I was thinking of having an interface like IDownloadTask which describes the task to carry out, have a few properties and an event to expose its Progress (which gets wired up when the tasks runs).
Then put that IDownloadTask into the queue with a priority. A background worker picks it up (the PriorityQUeue will need to be synchronised I guess) and then executes the .Execute() method in the interface implementation on a seperate thread.
Does this sound reasonable? Are there any concrete examples anyone can show me somewhere?
EDIT
Wow thanks for the reply and the vote of confidence, I should mention that I'm using .NET 2.0 (we can't move higher because of Windows compatibility requirements for Windows 9x).
As for tracking progress, your thread can report progress using events, as well as completion. Here is an example with a completion event, but the same concept would work for a Status update event. You'd just change the class that holds the data so that it can pass info about progress.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace ThreadWithDataReturnExample
{
public partial class Form1 : Form
{
private Thread thread1 = null;
public Form1()
{
InitializeComponent();
thread1 = new Thread(new ThreadStart(this.threadEntryPoint));
Thread1Completed += new AsyncCompletedEventHandler(thread1_Thread1Completed);
}
private void startButton_Click(object sender, EventArgs e)
{
thread1.Start();
//Alternatively, you could pass some object
//in such as Start(someObject);
//With apprioriate locking, or protocol where
//no other threads access the object until
//an event signals when the thread is complete,
//any other class with a reference to the object
//would be able to access that data.
//But instead, I'm going to use AsyncCompletedEventArgs
//in an event that signals completion
}
void thread1_Thread1Completed(object sender, AsyncCompletedEventArgs e)
{
if (this.InvokeRequired)
{//marshal the call if we are not on the GUI thread
BeginInvoke(new AsyncCompletedEventHandler(thread1_Thread1Completed),
new object[] { sender, e });
}
else
{
//display error if error occurred
//if no error occurred, process data
if (e.Error == null)
{//then success
MessageBox.Show("Worker thread completed successfully");
DataYouWantToReturn someData = e.UserState as DataYouWantToReturn;
MessageBox.Show("Your data my lord: " + someData.someProperty);
}
else//error
{
MessageBox.Show("The following error occurred:" + Environment.NewLine + e.Error.ToString());
}
}
}
#region I would actually move all of this into it's own class
private void threadEntryPoint()
{
//do a bunch of stuff
//when you are done:
//initialize object with data that you want to return
DataYouWantToReturn dataYouWantToReturn = new DataYouWantToReturn();
dataYouWantToReturn.someProperty = "more data";
//signal completion by firing an event
OnThread1Completed(new AsyncCompletedEventArgs(null, false, dataYouWantToReturn));
}
/// <summary>
/// Occurs when processing has finished or an error occurred.
/// </summary>
public event AsyncCompletedEventHandler Thread1Completed;
protected virtual void OnThread1Completed(AsyncCompletedEventArgs e)
{
//copy locally
AsyncCompletedEventHandler handler = Thread1Completed;
if (handler != null)
{
handler(this, e);
}
}
#endregion
}
}
Here are two C# projects that you can probably use to get you started.
MyDownloader: A Multi-thread C#
Segmented Download Manager
Multi-threaded file download
manager
Here are is a mini implementation you can start out with:
C# Threading issue with AutoResetEvent
You will probably want to have more that 1 processing thread, and you will probably need to add some comms back to the piece of data being processed so you can pause etc ...
I have a third party library containing a class which performs a function asynchronously. The class inherits from the Form. The function basically performs a calculation based on data stored in a database. Once it has finished, it calls a _Complete event in the calling form.
What I would like to do is call the function synchronously but from a non-windows form application. The problem is, no matter what I do, my application blocks and the _Complete event handler never fires. From a windows form I can simulate the function running synchronously by using a "complete" flag and a "while (!complete) application.doevents", but obviously application.doevents isnt available in a non-windows form application.
Is there something that would stop me using the class's method outside of a windows form application (due to it inheriting from 'Form') ?
Is there some way I can work around this ?
Thanks,
Mike
At a stab it might be worth trying something like the following which uses a WaitHandle to block the current thread rather than spinning and checking a flag.
using System;
using System.Threading;
class Program
{
AutoResetEvent _autoEvent;
static void Main()
{
Program p = new Program();
p.RunWidget();
}
public Program()
{
_autoEvent = new AutoResetEvent(false);
}
public void RunWidget()
{
ThirdParty widget = new ThirdParty();
widget.Completed += new EventHandler(this.Widget_Completed);
widget.DoWork();
// Waits for signal that work is done
_autoEvent.WaitOne();
}
// Assumes that some kind of args are passed by the event
public void Widget_Completed(object sender, EventArgs e)
{
_autoEvent.Set();
}
}
I've got some more information on this problem (I'm working in the same team as mikecamimo).
The problem also occurs in the Windows Forms application, when replicated correctly. In the original OP, the problem didn't occur in the windows form because there was no blocking. When blocking is introduced by using a ResetEvent, the same problem occurs.
This is because the event handler (Widget_Completed) is on the same thread as the method calling Widget.DoWork. The result that AutoResetEvent.WaitOne(); blocks forever because the event handler is never called to Set the event.
In a windows forms environment this can worked around by using Application.DoEvents to poll the message queue and allow the event the be handled. See below.
using System;
using System.Threading;
using System.Windows.Forms;
class Program
{
EventArgs data;
static void Main()
{
Program p = new Program();
p.RunWidget();
}
public Program()
{
_autoEvent = new AutoResetEvent(false);
}
public void RunWidget()
{
ThirdParty widget = new ThirdParty();
widget.Completed += new EventHandler(this.Widget_Completed);
data = null;
widget.DoWork();
while (data == null);
Application.DoEvents();
// do stuff with the results of DoWork that are contained in EventArgs.
}
// Assumes that some kind of args are passed by the event
public void Widget_Completed(object sender, EventArgs e)
{
data = e;
}
}
In a non windows forms application, such as a Windows Service, Application is not available so DoEvents cannot be called.
The problem is one of threading and that widget.DoWork's associated event handler somehow needs to be on another thread. This should prevent AutoResetEvent.WaitOne from blocking indefinitely. I think... :)
Any ideas on how to accomplish this would be fantastic.
AutoResetEvent _autoEvent = new AutoResetEvent(false);
public WebBrowser SyncronNavigation(string url)
{
WebBrowser wb = null;
wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.ScriptErrorsSuppressed = true;
wb.Navigate(new Uri(url));
while (!_autoEvent.WaitOne(100))
Application.DoEvents();
return wb;
}
void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
//throw new NotImplementedException();
_autoEvent.Set();
}
Do you have the source for the component? It sounds like it's relying on the fact it will be called from a WinForms environment (must be a good reason why a library inherits from Form!), but it's hard to know for sure.