i'm writing a simple filestream program in C# using asynchronous reading which uses a thread other than the main thread for its callback.but i'm getting cross-thread exception when i try to write my filecontent in a text box.
here is my program:
using System;
namespace Filestream
{
public partial class Form1 : Form
{
FileStream fs;
byte[] fileContents;
AsyncCallback callback;
public Form1()
{
InitializeComponent();
}
private void synbtn_Click(object sender, EventArgs e)
{
openFileDialog1.ShowDialog();
callback = new AsyncCallback(fs_StateChanged);
fs = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
fileContents = new Byte[fs.Length];
fs.BeginRead(fileContents, 0, (int)fs.Length, callback, null);
}
public void fs_StateChanged(IAsyncResult ar)
{
if (ar.IsCompleted)
{
*textBox1.Text = Encoding.UTF8.GetString(fileContents);*
fs.Close();
}
}
}
}
the part with the star is the part that i'm getting the exception.i tried to use the invoke but i had no luck.can someone correct this part of the code with invoke so i don't get the error.
thanks.
try this.
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new MethodInvoker(() => textBox1.Text = Encoding.UTF8.GetString(fileContents)));
}
else
{
textBox1.Text = Encoding.UTF8.GetString(fileContents);
}
To Expand on Ram's answer
//Can this thread make updates to textbox1?
if(textbox1.InvokeRequired)
{
//No then use the invoke method to update textbox1
textbox1.Invoke(new MethodInvokernew MethodInvoker(() => textBox1.Text = Encoding.UTF8.GetString(fileContents)));
}else{
//Yes then update textbox1
textBox1.Text = Encoding.UTF8.GetString(fileContents);
}
Explanation:
Updates to a UI control must be done on the thread that created the UI control. To test if the current thread is allowed to update a particular UI control call the InvokeRequired method on the control. Invoke can then be used to call a method on using the thread that can update the control
Related
I am trying to convert two different .txt files from lower case to upper case and the main objective is to measure and display the execution time.
Everything goes well if the files are saved with upper cases in my predefined path and the program displays the execution time. In my GUI however, texts do not convert because of the following exception in text-boxes:
System.InvalidOperationException: Cross-thread operation not valid: Control "textBox2" accessed from a thread other than the thread it was created on.
namespace Threads
{
public partial class Form1 : Form
{ String prim= #"C:\Users\Wheelz\Desktop\Laborator09\fis1.txt";
String secund= #"C:\Users\Wheelz\Desktop\Laborator09\fis2.txt";
public Form1()'
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var read = File.ReadAllText(prim);
textBox1.Text = read;
}
private void button2_Click(object sender, EventArgs e)
{
var read = File.ReadAllText(secund);
textBox2.Text = read;
}
private void modifica1()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis1upper.txt", read);
textBox1.Text = textBox1.Text.ToUpper();
}
private void modifica2()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis2upper.txt", read);
textBox2.Text = textBox2.Text.ToUpper() ;
}
private void timp_Click(object sender, EventArgs e)
{
Thread firstThread = new Thread(new ThreadStart(modifica1));
Thread secondThread = new Thread(new ThreadStart(modifica2));
var ceas= new Stopwatch();
ceas.Start();
firstThread.Start();
secondThread.Start();
ceas.Stop();
if (ceas.ElapsedMilliseconds == 1)
{
cron.Text = ceas.ElapsedMilliseconds.ToString() + " milisecundă";
}
else
{
if ((ceas.ElapsedMilliseconds < 20))
cron.Text = ceas.ElapsedMilliseconds.ToString() + " milisecunde";
else
cron.Text = ceas.ElapsedMilliseconds.ToString() + " de milisecunde";
}
}
}
}
Yes, you can't share the form called in mainthread into a subthread.
You must use an Delegate to mainthread to update the textboxes.
READ:
Invoke(Delegate)
Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread. This property can be used to determine if you must call an invoke method, which can be useful if you do not know what thread owns a control.
You can also work with backgroundworker or async
use "BeginInvoke" for update control value in Thread. like ...
private void modifica1()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis1upper.txt", read);
this.BeginInvoke(new MethodInvoker(() =>
{
textBox1.Text = textBox1.Text.ToUpper();
}));
}
private void modifica2()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis2upper.txt", read);
this.BeginInvoke(new MethodInvoker(() =>
{
textBox2.Text = textBox2.Text.ToUpper();
}));
}
I am working on C# win form application.My problem is when i click on menu i created one separate thread which is showing the progress(splash progress form). When i abort thread, progress form still display..! but when i move mouse pointer over the form it disappear
immediately..!
Following is my code
Thread progressThread = new Thread(() => Application.Run(new frmOperationProgress()));
progressThread.IsBackground = true;
progressThread.Start();
//Some work
progressThread.Abort();
How to close this progress form object in c#
The problem is using Abort - it's not generally recommended because there is no guarantee that it's going to do what you expect (in your case hide the form).
Best to add proper cancellation support into your thread and handle hiding the splash screen directly.
Please never ever use Abort(). This kind of work is best done trough BackgroundWorker; if you insist on Thread
Try:
var form = new frmOperationProgress();
Thread progressThread = new Thread(() => Application.Run(form));
progressThread.IsBackground = true;
progressThread.Start();
//Some work
form.ExternalClose();
Where ExternalClose is method of form like this:
public void ExternalClose() {
if (InvokeRequired) {
Invoke(new MethodInvoker(() => { ExternalClose(); }));
} else {
Close();
}
}
Solution using BackgroundWorker:
In backround worker you have to do UI stuff in ProgressChanged event (which is running in UI thread) and do the dirty work in DoWork event (background thread).
FormMain.cs: (Form with single BackgroundWorker control, named "backgroundWorker1", with wired up events backgroundWorker1_DoWork, backgroundWorker1_ProgressChanged and WorkerReportsProgress set to true)
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleApplication1 {
public partial class FormMain : Form {
private FormProgress m_Form;
public FormMain() {
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
backgroundWorker1.ReportProgress(0, "hello");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(20, "world");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(40, "this");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(60, "is");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(80, "simple");
backgroundWorker1.ReportProgress(100, "end");
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
if (e.ProgressPercentage == 0 && m_Form == null) {
m_Form = new FormProgress();
m_Form.Show();
}
if (e.ProgressPercentage == 100 && m_Form != null) {
m_Form.Close();
m_Form = null;
return;
}
var message = (string)e.UserState;
m_Form.UpdateProgress(e.ProgressPercentage, message);
}
}
}
Where FormProgress is simple form with ProgressBar progressBar1 and Label label1 and one extra method:
public void UpdateProgress(int percentage, string message) {
this.progressBar1.Value = percentage;
this.label1.Text = message;
}
You can just close your form and the thread (which is blocked by the message loop of that form) will be ended naturally:
var yourForm = new frmOperationProgress();
//Start it
Thread progressThread = new Thread(() => Application.Run(yourForm));
progressThread.IsBackground = true;
progressThread.Start();
//....
//close it
yourForm.Invoke((Action)(() => yourForm.Close()));
I was trying to solve the problem in this Question but I ended up having another problem
in short words that question was asking how to load a huge file into textBox chunk by chunk,
so in back ground worker Do_work event I did this:
using (FileStream fs = new FileStream(#"myFilePath.txt", FileMode.Open, FileAccess.Read))
{
int bufferSize = 50;
byte[] c = null;
while (fs.Length - fs.Position > 0)
{
c = new byte[bufferSize];
fs.Read(c , 0,c.Length);
richTextBox1.AppendText(new string(UnicodeEncoding.ASCII.GetChars(c)));
}
}
that didn't work because a backgroundWorker can't affect UI elements and I need to use BeginInvoke to do it.
so I changed the code:
delegate void AddTextInvoker();
public void AddText()
{
using (FileStream fs = new FileStream(#"myFilePath.txt", FileMode.Open, FileAccess.Read))
{
int bufferSize = 50;
byte[] c = null;
while (fs.Length - fs.Position > 0)
{
c = new byte[bufferSize];
fs.Read(c , 0,c.Length);
richTextBox1.AppendText(new string(UnicodeEncoding.ASCII.GetChars(c)));
}
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
this.BeginInvoke(new AddTextInvoker(AddText));
}
there are two problems with this code.
1- it's taking longer and longer time to append the text (I think because of string immutability replacing the text over time will take longer)
2- on every addition the richTextBox will scroll down to the end which causing application hang.
the question is what can I do to stop the scrolling and application hang?
and what can I do to enhance string concatenation here?
Edit: after some testing and using Matt's answer I got this:
public void AddText()
{
using (FileStream fs = new FileStream(#"myFilePath.txt", FileMode.Open, FileAccess.Read))
{
int bufferSize = 50;
byte[] c = null;
while (fs.Length - fs.Position > 0)
{
c = new byte[bufferSize];
fs.Read(c , 0,c.Length);
string newText = new string(UnicodeEncoding.ASCII.GetChars(c));
this.BeginInvoke((Action)(() => richTextBox1.AppendText(newText)));
Thread.Sleep(5000); // here
}
}
}
when the loading pauses I can read and write without problems or hanging, once the text exceeded the the richTextBox size the loading will scroll down and will prevent me from continue.
One problem I see is that your background worker is, well, not doing any work in the background. It's all running on the UI thread. This may be why the UI thread is non-responsive.
I would refine your DoWork handler like so:
public void AddText()
{
using (FileStream fs = new FileStream(#"myFilePath.txt",
FileMode.Open, FileAccess.Read))
{
int bufferSize = 50;
byte[] c = null;
while (fs.Length - fs.Position > 0)
{
c = new byte[bufferSize];
fs.Read(c , 0,c.Length);
string newText = new string(UnicodeEncoding.ASCII.GetChars(c));
this.BeginInvoke((Action)(() => richTextBox1.AppendText(newText));
}
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
AddText();
}
What I've done is localized the use of BeginInvoke to the single UI call made in the handler. That way, all of the other work is done in the background thread. Maybe that will help with the UI thread becoming non-responsive.
Just call Application.DoEvents. That's the simplest thing, no need to worry about manually creating or synchronizing threads or background workers, yet your app stays responsive.
Also, try using File.ReadLines, which is a lazy-loaded enumerable, rather than manually using a FileStream. This, for example, works for me, and gives you everything you need in a loop and two lines of code.
private void button1_Click(object sender, EventArgs e)
{
foreach (var line in File.ReadLines(#"C:\Users\Dax\AppData\Local\Temp\dd_VSMsiLog0D85.txt", Encoding.ASCII))
{
richTextBox1.AppendText(line + "\r\n");
Application.DoEvents();
}
}
Alternately you can specify your chunk size and load it by that. This will run a bit faster, but take a bit longer (less than a second though) to read the full file at first.
private void button1_Click(object sender, EventArgs e)
{
var text = File.ReadAllText(#"C:\Users\Dax\AppData\Local\Temp\dd_VSMsiLog0D85.txt", Encoding.ASCII);
const int chunkSize = 1000000;
for (var i = 0; i < text.Length / chunkSize; ++i)
{
richTextBox1.AppendText(text.Substring(chunkSize * i, chunkSize));
Application.DoEvents();
}
}
Try this third option and see if your hang is caused by the file or by the loop:
void button1_Click(object sender, EventArgs e)
{
while(true)
{
richTextBox1.AppendText("a");
Application.DoEvents();
}
}
Ok that's not exactly the best solution but it do what I want, instead of using AppendText which will surly scroll down I used +=, and I still got the hang so needed the Sleep(100)
public void AddText()
{
using (FileStream fs = new FileStream(#"myFilePath.txt", FileMode.Open, FileAccess.Read))
{
int bufferSize = 50;
byte[] c = null;
while (fs.Length - fs.Position > 0)
{
c = new byte[bufferSize];
fs.Read(c , 0,c.Length);
string newText = new string(UnicodeEncoding.ASCII.GetChars(c));
this.BeginInvoke((Action)(() => richTextBox1.Text += newText));
Thread.Sleep(100);
}
}
}
this will actually not allow me to scroll down until loading is done,
I couldn't come up with a better idea.
Edit: And a work around to be able to read the text before loading is done is to set scrollBasrs on richTextBox to None and set the form autoscroll to true and in TextChanged event:
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
using (Graphics g = CreateGraphics())
{
SizeF size = g.MeasureString(richTextBox1.Text, richTextBox1.Font);
richTextBox1.Width = (int)Math.Ceiling(size.Width) >
richTextBox1.Width ? (int)Math.Ceiling(size.Width) : richTextBox1.Width;
richTextBox1.Height = (int)Math.Ceiling(size.Height) >
richTextBox1.Height ? (int)Math.Ceiling(size.Height) : richTextBox1.Height;
}
}
this way I will be able to scroll down while loading. I hope someone find a better solution.
Try this:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
using (FileStream fs = new FileStream(
#"myFilePath.txt", FileMode.Open, FileAccess.Read))
{
int bufferSize = 50;
byte[] c = null;
while (fs.Length - fs.Position > 0)
{
c = new byte[bufferSize];
fs.Read(c, 0, c.Length);
Invoke(new Action(() =>
richTextBox1.AppendText(
new string(UnicodeEncoding.ASCII.GetChars(c)))));
}
}
}
The problem was the BeginInvoke, that call the AppendText async, see
http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspxhttp://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx
and
http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
Keep in mind that the Invoke method can throw exception, eg: if you close the form while loading text, so put it in try catch block and handle it.
BackgroundWorker CAN affect elements on the UI thread, since its events 'ProgressChanged' and 'RunWorkerCompleted' are executed on the calling thread. The following example increments an integer variable up to 10 on a separate thread, and communicates each increment back to the UI thread.
BackgroundWorker _worker;
private void button1_Click(object sender, EventArgs e)
{
_worker.RunWorkerAsync();
}
private void Form1_Load(object sender, EventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.DoWork += new DoWorkEventHandler(worker_DoWork);
_worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
for (int i = 0; i < 10; i++)
{
// pass value in parameter userState (2nd parameter), since it can hold objects
worker.ReportProgress(0, i); // calls ProgressChanged on main thread
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// get value, passed in DoWork, back from UserState
richTextBox1.AppendText(e.UserState.ToString());
}
So I am currently uploading files via a php script on my webserver using the following code:
string file = "dp.jpg";
System.Net.WebClient Client = new System.Net.WebClient();
Client.Headers.Add("Content-Type", "binary/octet-stream");
byte[] result = Client.UploadFile("http://localhost/upload.php", "POST", file);
String response = System.Text.Encoding.UTF8.GetString(result, 0, result.Length);
What I want to know is how would I use this, or a different method to track how much it has uploaded and display it on a progress bar?
Thanks.
Use UploadFileAsync.
Suscribe to wcUploader.UploadFileCompleted and
wcUploader.UploadProgressChangedevents so you can get the upload progress
and the upload completion.
In the following code you can check how we suscribe to UploadProgressChanged and
we can get e.ProgressPercentage value.
Check the following snippet:
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private readonly WebClient wcUploader = new WebClient();
public Form1()
{
InitializeComponent();
wcUploader.UploadFileCompleted += UploadFileCompletedCallback;
wcUploader.UploadProgressChanged += UploadProgressCallback;
}
private void UploadFileCompletedCallback(object sender, UploadFileCompletedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = Encoding.UTF8.GetString(e.Result);
button1.Enabled = true;
}));
}
private void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = (string)e.UserState + "\n\n"
+ "Uploaded " + e.BytesSent + "/" + e.TotalBytesToSend
+ "b (" + e.ProgressPercentage + "%)";
}));
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
button1.Enabled = false;
string toUpload = openFileDialog1.FileName;
textBox1.Text = "Initiating connection";
new Thread(() =>
wcUploader.UploadFileAsync(new Uri("http://anyhub.net/api/upload"), "POST", toUpload)).Start();
}
}
}
}
Code snippet extracted from here:
UploadFileAsync not asynchronous?
I have a thread in Winform. After I exit the application or shut down the server console application, the thread continues to work. Here is the code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
StreamReader sr;
StreamWriter sw;
TcpClient connection;
string name;
private void Form1_Load(object sender, EventArgs e)
{
connection = new TcpClient("127.0.0.1", 5000);
sr = new StreamReader(connection.GetStream());
sw = new StreamWriter(connection.GetStream());
ChatterScreen.Text = "Welcome, please enter your name";
}
private void button3_Click(object sender, EventArgs e)
{
//Thread t2 = new Thread(Reader);
//t2.IsBackground = true;
//t2.Start(connection);
ThreadPool.QueueUserWorkItem(Reader,connection);//How do i kill this thread
name = InputLine.Text;
}
string textinput;
private void button2_Click(object sender, EventArgs e)
{
textinput = InputLine.Text;
sw.WriteLine(name+":"+textinput);
sw.Flush();
}
string msg;
string allMessages;
public void Reader(object o)
{
TcpClient con = o as TcpClient;
if (con == null)
return;
while (true)
{
msg = sr.ReadLine() + Environment.NewLine;
allMessages += msg;
Invoke(new Action(Output)); // An exception is thrown here constantly. sometimes it is thrown and sometimes if i quite the server application , the winform application freezes.
Invoke(new Action(AddNameList));
}
}
public void Output()
{
ChatterScreen.Text = allMessages;
}
}
There is no safe way to kill a thread without doing a bit of work: you should never call Abort on a thread; what you need to do is to detect in the thread that it is required to terminate prior to completing its normal execution and then you need to tell it how to carry out this termination.
In C# the easiest way to do this is to use a BackgroundWorker which is essentially an object that executes code in a background thread; it is similar to calling invoke, except you have more control over the thread's execution. You start the worker by calling RunWorkerAsync() and you instruct it to cancel by calling RunWorkerAsync(). After calling RunWorkerAsync(), the CancellationPending property of the background worker is set to true; you watch for this change in your code (i.e. in your while loop) and when it is true you terminate (i.e. exit your while loop)
while (!CancellationPending )
{
// do stuff
}
Personally I do all threading through BackgroundWorkers because they are easy to understand and offer easy ways to communicate between background and main threads
You should put an ManualResetEvent in your Reader function. Instead of while(true), do while(!mManualReset.WaitOne(0)). Then before you quit the program do mManualReset.Set() This will let the thread exit gracefully.