I am trying to make a program that, at the same time, displays text that the user inputted and writes to a text file of that same user input data. I've tried wrapping the the code with Task.Run:
private void button_Click(object sender, RoutedEventArgs e)
{
show.Text = inputText.Text;
//Debug.WriteLine(check1_cont.ToString());
//Debug.WriteLine(check2_cont.ToString());
if (check1_cont && check2_cont == true )
{
show2.Text = inputText.Text;
Task.Run(() => File.WriteAllText(#"A:\temp\name.txt", inputText.Text));
}
}
But I get an exception error after the second text (the one in the if statement) when I press the button:
An exception of type 'System.Exception' occurred in normieap.exe but
was not handled in user code
Additional information: The application called an interface that was
marshalled for a different thread. (Exception from HRESULT: 0x8001010E
(RPC_E_WRONG_THREAD))
I try using StreamWriter:
private void button_Click(object sender, RoutedEventArgs e)
{
show.Text = inputText.Text;
//Debug.WriteLine(check1_cont.ToString());
//Debug.WriteLine(check2_cont.ToString());
if (check1_cont && check2_cont == true )
{
show2.Text = inputText.Text;
using (StreamWriter writer = new StreamWriter(#"A:\temp\name.txt"))
{
writer.WriteLine(inputText.Text);
}
}
}
But I get an error on the line:
using (StreamWriter writer = new StreamWriter(#"A:\temp\name.txt"))
Because '#"A:\temp\name.txt"' cannot convert from 'string' to 'System.IO.Stream'
And when I try just the normal way without any wrappers I get a synchronous error. Any solutions to this problem would be much appreciated.
When you run a task asyncrounously, it isn't guaranteed to run on the UI thread. Take your first example and try this:
private void button_Click(object sender, RoutedEventArgs e)
{
show.Text = inputText.Text;
//Debug.WriteLine(check1_cont.ToString());
//Debug.WriteLine(check2_cont.ToString());
if (check1_cont && check2_cont == true )
{
show2.Text = inputText.Text;
// Copy the text to output
string outputToWrite = inputText.Text;
// use the copied text
Task.Run(() => File.WriteAllText(#"A:\temp\name.txt", outputToWrite));
}
}
What's going on here is that a background thread is trying to access a GUI element. That's generally not allowed in singled threaded UI libraries like Windows Forms, so you need to copy the data out of the control before sending it back to the background thread, otherwise the code will fail as you have seen.
Related
My UI get frozen while I add each line of a text file in a List.
This is my current code :
private void LoadProxies_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "Select your Proxies file";
dialog.Filter = "Text File|*.txt";
DialogResult result = dialog.ShowDialog();
if (result == DialogResult.OK)
{
int list = proxiesList.Count;
Parallel.ForEach(File.ReadLines(file), line =>
{
if (line != null && line.Contains(":"))
{
proxiesList.Add(line);
list++;
InvokeUI(() => { Proxies.Text = list.ToString(); });
}
});
}
}
This is the InvokeUI method :
private void InvokeUI(Action a)
{
BeginInvoke(new MethodInvoker(a));
}
I tried using Parallel.ForEach(File.ReadLines(file), line => ... and await Task.Factory.StartNew(() => ... but it's not fixing my problem.
How can I solve this ? Thanks.
My UI get frozen while I add each line of a text file in a List
The problem with your code is that it is a rather tight loop and if the file is large the following line will be called a great deal of times per second:
BeginInvoke(new MethodInvoker(a));
This would result in the Windows Message Pump being flooded with update UI requests.
A better action is to either:
Since all you are doing is trying to display the numeric value of list on screen, consider updating say once a second by way of a Windows Forms timer rather than calling BeginInvoke
If you must update from a worker thread, don't BeginInvoke for each item, consider updating in batches. In this case perhaps update every 100
Alternatively you may want to consider TPL DataFlow
I'm generating report in C# by using background worker but I'm getting this error.
Source code as follows:
I have to access my datagridview Records to access it's data.
A small window opens up in my datagridview which asks user to enter date from and to generate report, then i access back my datagridview convert into data table write in XML file and generate report.
Global Variables
// This is the form where the data lies, I'm accessing it's instance.
Records TR = new Records();
// This is the form where report will be displayed.
TReportDisplay TRD = new TReportDisplay();
// This is the report.
Treport treport1 = new Treport();
private void button1_Click(object sender, EventArgs e)
{
// FIXED HERE - 1
// FIXED - 2 IN THE ANSWER BELOW.
// Accessing my DataGridView Form Instance.
TR = Application.OpenForms.OfType<Records>().ElementAt(0);
treport1.SetDataSource(TR.ds);
TRD.crystalReportViewer2.ReportSource = treport1;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
TRD.crystalReportViewer2.ReportSource = treport1;
ParameterFieldDefinitions Parameters;
ParameterFieldDefinition Parameter;
ParameterValues Values = new ParameterValues();
ParameterDiscreteValue DiscreteValue = new ParameterDiscreteValue();
DiscreteValue.Value = dateTimePicker1.Text;
Parameters = treport1.DataDefinition.ParameterFields;
Parameter = Parameters["fromdate"];
Values = Parameter.CurrentValues;
Values.Clear();
Values.Add(DiscreteValue);
Parameter.ApplyCurrentValues(Values);
DiscreteValue.Value = dateTimePicker2.Text;
Parameters = treport1.DataDefinition.ParameterFields;
Parameter = Parameters["todate"];
Values = Parameter.CurrentValues;
Values.Add(DiscreteValue);
Parameter.ApplyCurrentValues(Values);
}
}
catch (Exception ex) { MessageBox.Show(ex.Message.ToString(), "Message"); };
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TRD.ShowDialog();
}
There were two issues, first of all updating progress bar from a different thread for which I got answer, another calling form instance after background worker was causing the issue, just put the variable before background worker start async and fixed.
The error message is telling you that the only way to update the controls is via the thread that the controls are running on. You are currently running on a different thread (the one for the back ground worker).
Take a look at the example in this link for another question on SO Invoke(Delegate) . You basically should have a method that you can call, to update the UI, which can check if it is on the correct thread and if it is not gets the correct thread to call it.
This is a snippet of code that was on the link parvee gave above that shows how you can do this.
public void UpdateProgress(int percentComplete)
{
if (!InvokeRequired)
{
ProgressBar.Value = percentComplete;
}
else
{
Invoke(new Action<int>(UpdateProgress), percentComplete);
}
}
I am trying to login to my website automatically using webBrowser control in my C# application. The webpage checks the time required to fill and submit the login form. If the time taken is less than 2 seconds, then it shows an 'error page' saying 'Robots not allowed'.
Now my application is also getting that 'error page' just because the login form is getting filled within 2 seconds. How can I add a delay before firing the 'InvokeMember("click")' so that the time calculated by the webpage to fill the form be more than 2 seconds.
Here is my code
HtmlElement ele = WebBrowser1.Document.GetElementById("username");
if (ele != null)
{
ele.InnerText = "myUsrname";
}
ele = webBrowser1.Document.GetElementById("password");
if (ele != null)
{
ele.InnerText = "myPasswrd";
}
ele = webBrowser1.Document.GetElementById("submit");
if (ele != null)
{
// I want to add a delay of 3 seconds here
ele.InvokeMember("click");
}
Note : I used "Task.Delay(3000);" but it doesn't seems to work.
Edit : This is what I am using now and is working for me.
async void webBrowser1_DocumentCompleted_1(object sender, WebBrowserDocumentCompletedEventArgs e)
{
......//my code
await Task.Delay(3000);// this is where I wanted to put a delay
....
}
But I would like to, Is this a correct way to use it ?
If you want to don't freeze UI when waiting, Consider this sample:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(async () =>
{
await Task.Delay(3000);
MessageBox.Show("1");
button1.Invoke(new Action(() => { this.button1.Text = "1"; }));
});
MessageBox.Show("2");
button1.Invoke(new Action(() => { this.button1.Text = "2"; }));
}
The sample is self describing.
You could use this:
int milliseconds = 2000;
Thread.Sleep(milliseconds)
Greetings,
ST
So, I'm trying to develop a simple application in visual C# which gets data from serial port and displays it in a textbox (to monitor temperature). I'm acquiring and displaying the data successfully, using the DataReceived event to update a global string variable and a timer to update the text field on my text box, as shown:
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
try
{
globalVar.updateTemp = port.ReadLine(); //This is my global string
}
catch (IOException)
{
}
catch (InvalidOperationException)
{
}
catch (TimeoutException)
{
}
}
private void timer1_Tick(object sender, EventArgs e)
{
tempDisplayBox.Text = globalVar.updateTemp; //This is my textbox updating
}
The only issue I have is that the value shown in the textbox keeps flashing, making it hard to read. My timer is set to trigger every 10 ms (which should be fast enough, right?). Is there any way to make it more stable? I realize this may be a newb question, but to be fair I am a newb :) Any help is appreciated! Thanks!
Do you really need it updating every 10ms? What about every 500 ms or if not that then 100ms. 100ms will require your update method run 10 times less and therefore update 10 times less. The flickering you are expiriencing is due to the refresh speed. You could create custom method which will only update the temp only when target Label or textBox value is different than source port. But that will only sort the flickering when temp is steady, when temp will start vary it will bring back the flickering. Good luck ;-)
UPDATE
Hi I tried to reproduce the conditions and could not make my textbox nor Label flash. The way I tested it was by assigning int ntick = 0; and then increment the ++ntick; inside of the timer_tick method. The results didn't make any of the controls flash and were updated even every milisecond at some point. I also tried string.Format to put some load on the method. Is your app responsive?
The trick is to use double buffering. This way the operating system will redraw the Control off-screen, and only show the control when it is fully redrawn.
I have had the same problem, and solved it by extending the TextBox control like this:
public FastLogBox()
{
InitializeComponent();
_logBoxText = new StringBuilder(150000);
timer1.Interval = 20;
timer1.Tick += timer1_Tick;
timer1.Start();
SetStyle(ControlStyles.DoubleBuffer, true);
}
void timer1_Tick(object sender, EventArgs e)
{
if (_timeToClear)
{
_logBoxText.Clear();
_timeToClear = false;
}
if (_logQueue.Count <= 0) return;
while (!_logQueue.IsEmpty)
{
string element;
if (!_logQueue.TryDequeue(out element)) continue;
{
_logBoxText.Insert(0, element + "\r\n");
}
}
if (_logBoxText.Length > 150000)
{
_logBoxText.Remove(150000, _logBoxText.Length - 150001);
}
Text = _logBoxText.ToString();
}
public new void Clear()
{
_timeToClear = true;
while (!_logQueue.IsEmpty)
{
string element;
_logQueue.TryDequeue(out element);
}
}
public void AddToQueue(string message)
{
_logQueue.Enqueue(message);
}
}
I also use a timer and a concurrentQueue to avoid using Invoke to update the control from another thread. I also use a StringBuilder to prepare the string before putting it into the TextBox. StringBuilder is faster when building larger strings.
You can use ReadExisting() to read the whole data at a time.
You need to handle DataReceived Event of SerialPort
serialPort1.ReadExisting();
Sample:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
String myData=serialPort1.ReadExisting();
}
Example Code: Here i would like to show you the code to Read Data(RFID Tag Code which is basically of length 12)
String macid = "";
private void DoWork()
{
Invoke(
new SetTextDeleg(machineExe ),
new object[] { macid });
macid = "";
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string str1;
macid += serialPort1.ReadExisting();
if (macid.Length == 12)
{
macid = macid.Substring(0, 10);
Thread t = new Thread(new ThreadStart(DoWork));
t.Start();
}
}
public void machineExe(string text)
{
TextBox1.Text=text;
}
Thank you so much for the answers! I found a way to work around this issue:
Instead of replacing the contents of my textbox by rewriting the TextBox.Text property - which, as HenningNT implied, refreshes the control and causes the flickering - I'm now using the TextBox.AppendText method. Though, as I want to display only one line of data at a time, I use the textbox in multiline mode and the Environment.NewLine to jump to a new line before appending the text. As for the method of updating, I've gone back to using the timer because with the invoke method was crashing my application when I close the form, for some reason. Also, enabling double buffering didn't do me much good, although I guess I was doing it wrong... It still flickers a bit, but it's much better now :) I know this is not really a perfect solution (much more of a workaround), so I'll keep looking for it. If I find it, I'll be sure to update it here ;) My code:
private void timer1_Tick(object sender, EventArgs e) //Timer to update textbox
{
if (tempDisplayBox.Text != globalVar.updateTemp) //Only update if temperature is different
{
try
{
tempDisplayBox.AppendText(Environment.NewLine);
tempDisplayBox.AppendText(globalVar.updateTemp);
}
catch (NullReferenceException)
{
}
}
}
I have an autocompleate textbox that looks into a data base. Some times while I'm typing I received the following error.
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Here is the code
private void tBSearchName_TextChanged(object sender, EventArgs e)
{
try
{
//test length
if (tBSearchName.Text.Length > 3)
{
//prevent db lookups
if (!tBSearchName.Text.ToLower().Contains(oldName) || oldName == String.Empty)
{
//test for a name + first letter of last name
if (Regex.IsMatch(tBSearchName.Text, #"(\w)+\s(\w)+(\.)*"))
{
tBSearchName.AutoCompleteCustomSource = AccessDB.serachByNemberName(tBSearchName.Text);
tBSearchName.AutoCompleteMode = AutoCompleteMode.Suggest;
//prevent db lookups
oldName = tBSearchName.Text.ToLower();
}
}
}
}
catch
{
}
}
My insight is that I should frezz typing into the application while search is done, can some suggest how to do this. Or any other insight on what is happening
It is a bug in Windows Forms's wrapper of autocomplete APIs. Windows Forms does not protect the AutoCompleteCustomSource object from being replaced while it is being enumerated by a background thread created by autocomplete.
Instead of replacing the data store, you can try replace the autocomplete object or use the IAutoCompleteDropDown interface to reset the enumerator.
You can use lock:
private void tBSearchName_TextChanged(object sender, EventArgs e)
{
lock(this) { /* do magic */
}
Do note that it's bad practice to perform long tasks in the event handlers. If the search takes more then 30ms, better use a worker thread.