c# webbrowser navigate loop freezing form - c#

I'm trying to display a website in a form and automatically reload the website to see if a certain text is present. I'm doing this in a C# form and try to call a while with an button. This is the code I made:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnStartLoop_Click(object sender, EventArgs e)
{
lblStatusLoop.Text = "Status loop: Started";
lblStatusLoop.ForeColor = System.Drawing.Color.Green;
int i = 0;
while (i < 10)
{
Browser.Navigate("www.google.com");
Browser.DocumentCompleted += Browser_DocumentCompleted;
Thread.Sleep(5000);
i++;
}
}
void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
string ZoekText = txtSearchbox.Text.ToString();
if (Browser.DocumentText.Contains(ZoekText))
{
lblSearchResult.Text = "Text found";
}
else
{
lblSearchResult.Text = "Text not found";
}
}
}
As soon as I push the button the appliction freezes and I have to force quit it. I've done some research on google and suggestions were to wait for the document to complete loading but it has no effect.
I don't have much programming experience and do not have someone to fall back on so I hope someone here can point me into right direction.

Related

C# Serial Port, wait for correct Response before allowing a new message

First I'd like to say sorry for doubleposting. I'm afraid the context and questions I posted before didn't clearify it enough and although one of the solutions worked I am still struggling to grasp the concept, that's why I decided to make a new one. Also because there are apparently many different opinions of how it should be done in the right manner.
First here is a clean running code example that I`m using to figure this out:
namespace serialInterfaceTest
{
public partial class Form1 : Form
{
string serialDataIn;
bool sent = false;
public Form1()
{
InitializeComponent();
serialPort.PortName = "COM3";
serialPort.BaudRate = 9600;
serialPort.Open();
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
serialDataIn = serialPort.ReadExisting();
this.Invoke(new EventHandler(saveData));
}
public void saveData(object sender, EventArgs e)
{
string trimmedMsg = serialDataIn;
richTextBox.Text += trimmedMsg;
if(trimmedMsg.Contains("*")) button_sendMsg.Enabled = true;
}
private void richTextBox_TextChanged(object sender, EventArgs e)
{
richTextBox.SelectionStart = richTextBox.Text.Length;
richTextBox.ScrollToCaret();
}
private void button_sendMsg_Click(object sender, EventArgs e)
{
send(textBox_message.Text);
button_sendMsg.Enabled = false;
//WAIT FOR RESPONSE BEFORE ALLOWING THE USER TO SEND ANOTHER MESSAGE
}
private void button_loopMsg_Click(object sender, EventArgs e)
{
button_loopMsg.Enabled = false;
for (int i = 0; i < 10; i++)
{
send(textBox_message.Text);
//WAIT FOR RESPONSE BEFORE CONTINUING THE LOOP
}
button_loopMsg.Enabled = true;
}
void send(String message)
{
serialPort.Write(message);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort.IsOpen)
{
try
{
serialPort.Close();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
}
}
It is a very simple GUI with a richTextBox to receive messages, a textbox to enter a message and two buttons that will send the message in two different ways.
The first case, where the user is sending the message manually is simple. Just deactivate the button so you can't spam it. The second case where I'm trying to send multiple messages in a loop seems way more complicated than I initially thought.
I try to outline my needs:
richTextBox always get's updated when a message arrives over serial
when user want's to send a message he has to wait for a reply before sending the next
when sending messages consecutively, the method for sending the message need's to stop and wait for a reply before proceeding with
the next message or send a timeout and stop the execution of the
method
answers from the serial port always end with a '*'
an answer can either resume a loop as described above or trigger a method from my GUI
all communication on serial is in ASCII.
GUI should stay responsive
Using the localHandler in the approved solution from my other question works and for one simple case that's fine but I quickly realized that it is not flexible enough. I tried to figure it out but didn't get anywhere.
In my code example that I posted above, I separated the serialPort.Write in it's own method. I'm thinking something in the terms of this:
UI is running in it's own thread and serialPort_DataReceived is running in it's own thread, which it is doing anyways as my reasearch showed. So now when I'm receiving data everything is fine, the UI gets updated everytime I receive a message from the serial port. Now for the sending part I guess the best way is to give it it's own thread as well? So I can simply pause the thread where my message is being sent, wait for a reply on the main thread and then continue. Or which other concept would fulfill my need here?
I'm new to object-oriented programming, most of the stuff I have done so far is C based so I could use any help here. Thanks for considering and again, sorry for the double post. I just hope my question is more clear now.
After 4 days of almost no sleep I figure it out and want to post the solution to anybody who is trying to have some sort of a flow control in their serial communication. In the end I was using async methods. I think this is as simple as it can get for somebody who doesn't have a lot of C# experience. Here is the code for the form:
namespace serialInterfaceTest
{
public partial class Form1 : Form
{
string serialDataIn;
public Form1()
{
InitializeComponent();
serialPort.PortName = "COM3";
serialPort.BaudRate = 9600;
serialPort.Open();
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
serialDataIn = serialPort.ReadExisting();
this.Invoke(new EventHandler(saveData));
}
public void saveData(object sender, EventArgs e)
{
string trimmedMsg = serialDataIn;
richTextBox.Text += trimmedMsg;
}
private void richTextBox_TextChanged(object sender, EventArgs e)
{
richTextBox.SelectionStart = richTextBox.Text.Length;
richTextBox.ScrollToCaret();
}
private async void button_sendMsg_Click(object sender, EventArgs e)
{
button_sendMsg.Enabled = false;
await Task.Run(() => send(textBox_message.Text));
button_sendMsg.Enabled = true;
//WAIT FOR RESPONSE BEFORE ALLOWING THE USER TO SEND ANOTHER MESSAGE
}
private async void button_loopMsg_Click(object sender, EventArgs e)
{
button_loopMsg.Enabled = false;
for (int i = 0; i < 10; i++)
{
await Task.Run(() => send(textBox_message.Text));
//WAIT FOR RESPONSE BEFORE CONTINUING THE LOOP
}
button_loopMsg.Enabled = true;
}
private async Task send(String message)
{
serialDataIn = "";
serialPort.Write(message);
while (!serialDataIn.Contains("*"))
{
//PROCESS ANSWERS HERE
serialDataIn = serialPort.ReadExisting();
if (serialDataIn.Contains("*"))
{
this.Invoke(new EventHandler(saveData));
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort.IsOpen)
{
try
{
serialPort.Close();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
}
}
I have the async method send data, and the two buttons are async as well. When I press them I'm just waiting for the task to complete before another input is allowed. I think this should be a good starting point for other projects as well. UI stays responsive, messages don't get queued up. The richTextBox on the UI get`s updated via Invoke so messages are displayed as soon as they arrive.
Here is the test code for an arduino:
#define println Serial.println
char serialIn;
String appendSerialData;
void setup()
{
Serial.begin(9600);
}
void loop()
{
appendSerialData = "";
while (Serial.available() > 0)
{
serialIn = Serial.read();
appendSerialData += serialIn;
}
/* asterisk functions as message identifier */
delay(1000);
if (appendSerialData != "") println(appendSerialData + " *");
for (int i = 0; i < 5; i ++)
{
println(i);
}
delay(100);
}
If there are any improvements I can make to this I`m happy to hear about it.

C# Writing to file hangs UI even when using await Task

I'm trying to save a rather large text file when the user hits the save button. It can be up to 30MBs. After pressing the button, I'd like the texbox to display "Saving..." as it's saving the file and when it completes, display "Saved". However I can't get this to work. I've tried using Task.run, await task.Run, and using a background worker. All these options hang the UI until the save completes. The textbox does not display "Saving..." until after it saves and the program is unresponsive until then. How can I fix this?
private async void btnSave_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.ShowDialog();
// If the file name is not an empty string open it for saving.
if (saveFileDialog1.FileName != "")
{
logFileName = saveFileDialog1.FileName;
btnOpenFile.IsEnabled = false;
btnSave.IsEnabled = false;
tbText1.Text += "\n\n***Saving...***\n";
tbText1.ScrollToEnd();
await Task.Run(() => File.WriteAllText(logFileName, Results.ToString()));
tbText1.Text += "\n\n***SAVED***\n\n";
tbText1.ScrollToEnd();
btnOpenFile.IsEnabled = true;
btnSave.IsEnabled = true;
}
As discussed in the comments, the problem is with Results.ToString().
I tried to reproduce the issue with this code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
for (int i = 1; i < 40536; i++)
{
stringBuilder.Append(new string('a', i));
}
}
readonly StringBuilder stringBuilder = new StringBuilder();
int tickNumber = 0;
private void sync_Click(object sender, EventArgs e)
{
button1.Enabled = false;
stringBuilder.ToString();
button1.Enabled = true;
}
private async void async_Click(object sender, EventArgs e)
{
button2.Enabled = false;
await Task.Run(() => stringBuilder.ToString());
button2.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
tickNumber %= 50;
tickNumber++;
label1.Text = new string('.', tickNumber);
}
}
But it works as expected:
Sometimes UI hands for a little bit though. Is this what are you talking about?
Try moving code that generates contents for StringBuilder inside the task (so this StringBuilder only exists in background thread)

Show message with timer

I have an application with 2 form,
1 form reading from .csv file to insert to SQL Server,
1 form will be show when trouble happen ( Can not reading .csv file or reading error)
My code as below:
public void Timer_tick ( object sender eventargs e)
{
PushData(); // with time interval =100
}
public void PushData()
{
Form2 fr = new Form2();
// function to reading .csv file
if ( checkData(name) == "OK")
{
//update data to SQL Server
}
else
{
timer1.Stop();
fr.Show(); // User need to click OK button to hide Form2 and come back Form1
this.Refresh();
}
}
It's only one time show Form2.
But after User click button at Form2 to comback Form1. But Form1 not working normal with timer.
Please help me to solve this application.
Thank you!
Show the form as dialog, and then restart the timer when finished:
timer1.Stop();
fr.ShowDialog(); // User need to click OK button to hide Form2 and come back Form1
timer1.Start();
You can use the message box rather than using form2 and display error message. Refer the similar and sample code below.
{
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
PushData();
}
public void PushData()
{
Form2 fr = new Form2();
// function to reading .csv file
if (checkBox1.Checked==true)
{
progressBar1.Value += 1;
//update data to SQL Server
}
else
{
timer1.Stop();
MessageBox.Show("Error message");
timer1.Start();
}
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
}
}
Try the below code and let me know is it working or not
public void Timer_tick ( object sender eventargs e)
{
PushData(); // with time interval =100
}
public void PushData()
{
Form2 fr = new Form2();
frm.Closed += fr_Closed;
// function to reading .csv file
if ( checkData(name) == "OK")
{
//update data to SQL Server
}
else
{
timer1.Stop();
fr.Show(); // User need to click OK button to hide Form2 and come back Form1
this.Refresh();
}
}
void fr_Closed(object sender, EventArgs e)
{
timer1.Start();
}

web browser cannot work with 1 click

I have a situation which occurs in WebBrowser with C#.
I am trying to do downloader through a site However , when i click first time, it doesn't work but if I click second times it works.
How can i solve this problem .
Sincerely.
Codes :
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
webBrowser1.Document.GetElementById("youtube-url").SetAttribute("value", textBox1.Text);
webBrowser1.Document.GetElementById("submit").InvokeMember("click");
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
label3.Text = "Video Alındı , indirme işleminin hazır olması bekleniyor";
}
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
HtmlElementCollection col = webBrowser1.Document.GetElementsByTagName("a");
String link = "";
foreach (HtmlElement el in col)
{
if (el.InnerText == "Download")
{
link = el.GetAttribute("href");
Download(link);
label3.Text = "Video indiriliyor";
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
webBrowser1.ScriptErrorsSuppressed = true;
webBrowser1.Navigate("http://www.youtube-mp3.org/tr");
}
void Download(String link)
{
WebClient downloader = new WebClient();
downloader.DownloadFileAsync(new Uri(link),#"D:\a.mp3");
downloader.DownloadProgressChanged += new DownloadProgressChangedEventHandler(downloader_DownloadProgressChanged);
}
void downloader_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label3.Text = "Video İndiriliyor : " + progressBar1.Value + "%";
if (progressBar1.Value == 100)
label3.Text = "Video İndirildi";
}
You're blocking yourself from investigating what the problem is. It's never a good idea to disable script errors for WebBrowser control (as you do with ScriptErrorsSuppressed = true), unless you handle them internally in your host app. Do the following:
enable script errors (ScriptErrorsSuppressed = false);
enable script debugging for all applications;
implement WebBrowser Feature Control (FEATURE_BROWSER_EMULATION), so the web page is getting the same (or close) experience and HTML features as with full-featured IE browser.
Then, hopefully, you can find out what's going wrong when you're simulating a button click, and debug it.

C#: Stop loop until an event

I have a for loop and inside there is a navigate method for a browser. and it's suppose to load diffrent sites, but the problem is that it will start to load 1 site and before it will load it, it'll load another site. so I need to like pause it until it's completed.
I started to write an event to when the ProgressChanged event is at 100%.. than I figured I don't have any idea what to do next but I think it's a start.
Please help, Thanks!
Edit: I am using Forms as Roland said.
I assume you are doing windows forms programming. The event you want is DocumentCompleted Here's an example:
public Uri MyURI { get; set; }
public Form1()
{
InitializeComponent();
MyURI = new Uri("http://stackoverflow.com");
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
webBrowser1.Url = MyURI;
}
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if(e.Url == MyURI)
MessageBox.Show("Page Loaded");
}
For a list of URIs it's straight forward.
public int CurrentIndex = 0;
List<Uri> Uris;
public Form1()
{
InitializeComponent();
Uris = new List<Uri> { new Uri("http://stackoverflow.com"), new Uri("http://google.com/") };
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
webBrowser1.Url = Uris[CurrentIndex];
}
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser browser = (WebBrowser)sender;
if (e.Url == Uris[CurrentIndex])
{
CurrentIndex++;
if (CurrentIndex < Uris.Count)
{
browser.Url = Uris[CurrentIndex];
}
}
}

Categories