I try to record some internet activity through mouse hook in webbrowser control in winform application. Vary rare happens to click to a link and to not record that click just because everything happens too quickly and my code in
public void WebBrowser1Document_Click(object sender, HtmlElementEventArgs e)
{
tempHtmlElement = webBrowser1.Document.ActiveElement;
...
method couldn't reach to the end. In 90% record happens but some time it passes webBrowser1_Navigating event after that as I said passes WebBrowser1Document_Click just the beginning and webBrowser1.Document.ActiveElement breaks. It doesn't matter I have variable for the ActiveElement this variable looses access to some properties as Name for example. So my question is how to pause webbrowser1 activity until record is made. It's a STA application.
Assuming that Navigation occurs before Click method completes, You can pause by a tricky method. Use a global variable CanNavigate.
bool canNavigate;
public void WebBrowser1Document_Click(object sender, HtmlElementEventArgs e)
{
canNavigate=false;
tempHtmlElement = webBrowser1.Document.ActiveElement;
...
...
//At the End
canNavigate=true;
}
void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
do
{
Application.DoEvents(); Thread.Sleep(100);
} while (!canNavigate);
}
Related
In a C# desktop application, a backgroundworker responsible for saving application state is being called in 2 situations. Once while the application is running. That works fine. Other when application is being closed, backgroundworker is called to save the application state but before it starts saving, the application is closed and nothing gets saved.
I tried to solve it by using the AutoReset event class in DoWork and RunWorkerCompleted but didnt work because application closed before backgroundworker could save any thing.
Question is - how can I make the main thread wait until backgroundworker finishes saving?
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
this.saveAHToolStripMenuItem_Click(this, e);
}
private void saveAHAsToolStripMenuItem_Click(object sender, EventArgs e)
{
this.backgroundWorkerMain1.RunWorkerAsync(args);
}
private void backgroundWorkerMain1_DoWork(object sender, DoWorkEventArgs e)
{
saveMethod();
}
private void backgroundWorkerMain1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
showResultOfSaving();
}
Is it WinForms?
Maybe you could register to the OnClosing event.
Within it, set a private property IsClosing to true.
Mark the eventhandler e as e.Handled = true.
Register to the BackgroundWorker event RunWorkerCompleted. Within it, check if the IsClosing property is set and in that case MainForm.Close() the application.
Edit:
using System;
using System.Threading;
using System.Windows.Forms;
using System.ComponentModel;
namespace BgWorker
{
public partial class Form1 : Form
{
BackgroundWorker _bgWorker;
bool _iNeedToCloseAfterBgWorker;
public Form1()
{
InitializeComponent();
}
void Form1_Load(object sender, EventArgs e)
{
_bgWorker = new BackgroundWorker();
_bgWorker.DoWork += _bgWorker_DoWork;
_bgWorker.RunWorkerCompleted += _bgWorker_RunWorkerCompleted;
}
void _bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done!");
if (_iNeedToCloseAfterBgWorker)
Close();
}
void _bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do long lasting work
Thread.Sleep(2000);
}
void btnWorkIt_Click(object sender, EventArgs e)
{
// Note how the Form remains accessible
_bgWorker.RunWorkerAsync();
}
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_iNeedToCloseAfterBgWorker || _bgWorker.IsBusy) return;
e.Cancel = true;
_iNeedToCloseAfterBgWorker = true;
_bgWorker.RunWorkerAsync();
}
}
}
It would be cleaner to place the SaveState code in a separate method, which is called from the background worker and (synchronously) when you close the application.
Either that, or you can block the application from closing based on a manual reset event (ManualResetEvent-class).
Just throwing an idea out there.
Perhaps a volatile DateTime property on the MainForm.
(Note - DateTimes cannot be volatile it appears, so you could use the string representation instead)
public volatile string _lastUpdated;
This property gets updated everytime the save event is carried out on the background worker thread.
OnClose will check the Time Difference between DateTime.Now and the stored date time.
Eg:
var timeDiff = DateTime.Now - _lastSavedTime;
If the OnClose detects that the timeDiff.TotalSeconds is < 30 (less than 30 seconds)
You can then trigger the save event manually from the main thread, prior to the close event being completed.
This however won't protect against Process.Kill - very little can protect against that.
All I can suggest is that you manage your saves in a smart way.
Eg: Save to a new File each time, keeping the last 5 saves.
When the 6th save is made, delete the oldest save etc
This is to account for the potential corruption that may happen from a Process.Kill scenario. Means you will still have at least a 60 second backup, in case the 30 second backup fails.
My solution to this problem is to wait untill backgroundworker is fnished by adding following after backgroundworker async call.
while (this.backgroundWorkerMain1.IsBusy)
{
Application.DoEvents();
}
I have a form with two text fields, A and B that are supposed to behave in the following way:
Typing something into A should set B.Text = f(A.Text)
Typing something into B should set A.Text = g(B.Text)
...for some arbitrary and potentially unrelated functions f and g.
The problem I'm facing is that the naive implementation of simply throwing the above code into each field's handler will create an infinite loop as A's handler will update B's value and call B's handler, which will update A, etc.
What would be the correct (and preferably thread-safe) way to handle this? Either somehow determining whether a change was done manually or programmatically, somehow suppressing events firing when changing the value, or some other way.
Use a flag to signal that you are doing changes
private bool updating;
private void A_TextChanged(object sender, EventArgs e)
{
if (!updating) {
updating = true;
B.Text = f(A.Text);
updating = false;
}
}
private void B_TextChanged(object sender, EventArgs e)
{
if (!updating) {
updating = true;
A.Text = g(B.Text);
updating = false;
}
}
You don't have to care about thread-safety as this all happens in the unique UI-thread. UI events never create new threads; i.e. an event (click, text changed, etc.) never interrupts another one!
If you want to be sure that the flag is reset, you can use the try-finally statement. The finally block is ensured to run, even if an exception should occur within the try block (unless the application is terminated unexpectedly).
if (!updating) {
updating = true;
try {
A.Text = f(B.Text);
} finally {
updating = false;
}
}
I assume you're using TextChanged event, try this then:
private bool callB=true;
private bool callA=false;
private void A_Click(object sender, System.EventArgs e)
{
callB=true;
callA=false;
}
private void B_Click(object sender, System.EventArgs e)
{
callB=false;
callA=true;
}
private void A_textchanged(object sender, RoutedEventArgs e)
{
if(callB)
B.text=f(A.text);
}
private void B_textchanged(object sender, RoutedEventArgs e)
{
if(callA)
A.text=g(B.text);
}
Anyway, a better way to just edit A when the user is finished with B(finished whatever he wanted to write in it), that's because if expression will be evaluated at every character the user inputs.
By the way, changing a text while the user writes might be surprising to him, so better to avoid textchanged event in this case.
I'm a beginner and have an assignment in which I must program the game of NIM. I begin with 15 "tokens" and at each turn a maximum of three can be removed, or "hidden". So far I am hiding these tokens on click by doing the following.
private void button1_Click(object sender, EventArgs e)
{
button1.Visible = false;
}
private void button2_Click(object sender, EventArgs e)
{
button2.Visible = false;
}
I simply copied and pasted that multiple times and changed the button numbers so that my buttons will close on click. This might be obvious, but is there a more efficient way to do this, instead of having 15 button close methods?
You can use the same click event for every single button, and make use of the sender object, casting it to Button:
private void buttonsToClose_Click(object sender, EventArgs e)
{
((Button)sender).Visible = false;
}
Then just add that handler to every single button you want to close itself on click.
Note, though, that this will throw an InvalidCastException if you or anyone else uses this handler on an object that is not a Button, so if you're actually going to use this code I would add some sort of conditional to check the real type of the sender.
Additionally, you could reuse this for any Control object by casting sender to Control instead, given that Button inherits from Control, and all Control objects have the Visible property. Here's an example, with a conditional to guard against an invalid cast:
private void controlToMakeInvisible_Click(object sender, EventArgs e)
{
if (sender.GetType() == typeof(Control))
{
((Control)sender).Visible = false;
}
}
A final note - it seems from your post like you may have a slight misunderstanding about the way events are created and wired in with objects in Windows Forms. If you go into the Designer, add a click event, and see it pop into your Form code as follows:
private void button1_Click(object sender, EventArgs e)
the name of this method has no bearing on its function. The button1 part of button1_Click doesn't actually have any logical linkage with the Button button1 - it's just the default name assigned by the Designer. The actual assignment of the method button1_Click to the Button.Click event is auto-generated into your Form's Designer.cs method.
The point of this is that if you copy and paste button1_Click and change every incidence of button1 with button2, like so:
private void button2_Click(object sender, EventArgs e)
{
button2.Visible = false;
}
it's not going to fire when button2 gets clicked. In actual fact, it's never going to fire at all, because the method hasn't actually been connected to any controls/events.
just call your event in a foreach loop.
private void Form1_Load(object sender, EventArgs e)
{
foreach (var button in Controls.OfType<Button>())
{
button.Click += button_Click;
}
}
void button_Click(object sender, EventArgs e)
{
((Control) sender).Visible = false;
}
if you change:
Controls.OfType<Button>()
to
Controls.OfType<Control>()
it will set visible to false for any Control. so you can control what item you want the event to be raised for easily.
OfType summary: Filters the elements of an IEnumerable based on a specified type.
private void AddMyScrollEventHandlers()
{
VScrollBar vScrollBar1 = new VScrollBar();
}
private void button1_Click(object sender, EventArgs e)
{
while (true)
{
if (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
vScrollBar1.Value = vScrollBar1.Value + 1;
label1.Text = vScrollBar1.Value.ToString();
}
else
{
break;
}
System.Threading.Thread.Sleep(200);
}
}
private void button2_Click(object sender, EventArgs e)
{
// vScrollBar1.Scroll
}
I am new in C#. I was working on scroll. What I wanted here is, if anyone click button1 then scroll automatically move to the end and I wanted to show gradual value in label1. Also when someone click button2 scrolling stop.
Now the problem is label1 do not show gradual change in value. It shows value once when the scrolling stop.
Also when scrolling continue i,e when while loop is working I can not click on button2. Actually I can not click on the form even.
Someone please give me some idea how to do this.
This happens because the thread that is performing the task is busy, and it's the same thread that updates the UI. You can use a multithreading solution. Take a look at
BackgroundWorker
All the UI events run in the main thread of the application, so the application can only process one event at a time. When the application is processing an event, no other event will be processed.
Since you are doing a UI related work periodically, the best option is to use the Timer class:
Drop Timer from the toolbox into the form.
In the properties window, set the interval to 200.
Double click the timer object to create the Tick event handler.
Put this code in the newly created timer1_Tick method:
if (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
vScrollBar1.Value = vScrollBar1.Value + 1;
label1.Text = vScrollBar1.Value.ToString();
}
else
{
timer1.Stop();
}
Change your methods as below:
private void AddMyScrollEventHandlers()
{
timer1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
timer1.Stop();
}
Now you're done.
I would recommend using BackgroundWorker control, as suggested by Agustin Meriles. However, one more important thing to note is that You should use Control.Invoke(...) method to update controls from another thread.
I've modified Your code, tested it in a sample application and it seems to work correctly.
First, add a new BackgroundWorker control to Your form and assign backgroundWorker1_DoWork to its DoWork event.
Then, You can use the code below:
private void button1_Click(object sender, EventArgs e)
{
//code from here is moved to BackgroundWorker control
backgroundWorker1.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//while (true)
//the condition directly in the while looks more clear to me
while (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
//update controls using Invoke method and anonymous functions
vScrollBar1.Invoke((MethodInvoker)delegate() { vScrollBar1.Value += 1; });
label1.Invoke((MethodInvoker)delegate() { label1.Text = vScrollBar1.Value.ToString(); });
//when called inside BackgroundWorker, this sleeps the background thread,
//so UI should be responsive now
System.Threading.Thread.Sleep(200);
}
}
If You have any problems when using this code, please let me know.
Update
As mentioned in the comments, You could also use ProgressChanged event of the BackgroundWorker. It requires some more changes in the code, but is more suitable in this case. You can find some information about it here: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged.aspx.
If You are not going to add any other code with more processing in the while loop, You can also use Timer control, as suggested by MD.Unicorn in his answer.
I am new to windows.forms programming. I started making an application that has the following flow of events:
click a button that opens a file - extract its contents in some list of strings - visit some websites - parse their content - etc.
So because everything in my app happens after i click a button to open a file, I have put all my code on the click event of the button. However I do know this is bad coding practice, because I realised I ended up having ALL the program flow inside that click event. I know that the event should only contain code related to the button, but where to place the code that follows, if not inside the event? Is there another event that I should use instead of just writing all in the button click?
I hope I've made my question clear. If not then I'll retry to explain my problem. I simply don't know where to write the code that follows the click event. If I put it in:
public Form1()
{
InitializeComponent();
}
..then it executes before the click event which is wrong.
Thank you in advance.
The typical way to do this is to write one or more methods that perform the action, and call those from the click event. For any long-running actions, do them in a background worker thread.
For example:
public void myButton_OnClick(EventArgs e, object sender)
{
VisitWebSites();
}
private void VisitWebSites()
{
var webSiteList = GetWebSitesFromFile();
foreach (var w in webSiteList) {
StartVisitingWebSite(w);
}
}
private IEnumerable<string> GetWebSitesFromFile()
{
// whatever
}
private void StartVisitingWebSite(string url)
{
backgroundWorker1.RunWorkerAsync(url);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string url = (string)e.Argument;
e.Result = VisitWebSite(url);
}
private string VisitWebSite(string url)
{
// This is called in background thread. Do whatever you do to return data.
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error || e.Cancelled)
return;
string result = e.Result.ToString();
// Do whatever you do with the result
}
Look at the BackgroundWorker documentation to see how to perform actions in a background thread and then handle their results.
You can encapsulate all of the work that you want to do into another function in the form's class. Its modification access would be PRIVATE of course (unless you want another class to be able to access the method). From inside of your button_click event handler, you can call this new function. That is simplest way to do this. Otherwise, you can use the example provided in the link above by #Robert S.
You should look into the Model View Presenter pattern. http://msdn.microsoft.com/en-us/magazine/cc188690.aspx