C# Web Browser Control blocks parent's Load event - c#

This may come across as incredibly stupid, but I cannot figure out if:
I am an idiot
I have misunderstood something
The MS Web Browser control is bugged
I prefer to think that it is the latter.
I have a Web Browser control in a WinForms user control. It has been added to the control at design time, and in theory, in the Load event of the control it should navigate to Google.
Seems straightforward.
However.
public partial class TVHost : UserControl
{
public TVHost()
{
InitializeComponent();
}
private void TVHost_Load(object sender, EventArgs e)
{
webBrowser1.Navigate("http://google.co.uk");
}
}
This doesn't work. No error, just nothing. Inserting a breakpoint/debug lines shows me that the Load event doesn't even get called.
I decided at this point to check that the Load event is being set correctly in the Designer.cs file.
this.Load += new System.EventHandler(this.TVHost_Load);
Seems legit.
If I remove the web browser control from the form, the load event fires.
I don't understand this one bit, how can a control prevent a method which uses it from firing in the first place?
Moving on, I found this:
http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvcs/thread/d6e427b2-9cc9-4318-bb05-11363025e3f7/
TL;DR for the link is as follows: "Load won't work if you have a webbrowser on the form which is set to Visible = true"
So sure as hell, if I change the default visibility of the webbrowser to false, the load event of the control fires. I can work around the problem by setting the visibility of the browser in the load event.
private void TVHost_Load(object sender, EventArgs e)
{
webBrowser1.Visible = true;
webBrowser1.Navigate("http://google.co.uk");
}
Very odd.
Whilst this "fix" works, I find it incredibly hacky and was wondering if anybody has any explaination for this behaviour?
Amazingly I have found this bug in MS Connect, left over from 2005 - http://connect.microsoft.com/VisualStudio/feedback/details/116535/when-adding-a-webbrowser-control-to-a-user-control-the-load-will-not-fire#

From the discussion in the Connect bug you linked to:
For now, if you want to get the Load event to fire, you can set the URL property of the WebBrowser control in the property grid. The URL can be anything you want, even about:blank if you don't want it to start with a page loaded.
So if you go into the designer and set the WebBrowser's Url property to the string about:blank (which tells the WebBrowser to load an empty page), then your user control should start getting its Load event again.

Related

Click button after webBrowser1_DocumentCompleted event

I have a C# 4.0 WinForms application, which has a WebBrowser control and 2-buttons.
Clicking the first button sends a URL to the browser to navigate to a specified webSite.
Clicking the second button parses the OuterHtml of the webBrowser1.Document, looking for an "https://..." link for File Download.
The code then uses a webClient.DownloadFileAsync to pull down a file for further use in the application.
The above code successfully works, if I manually click those buttons.
In an effort to automate this for the end-user, I place the first button's click event, i.e. btnDisplayWeb.PerformClick(); in the form's Form1_Load event. This also works, allowing the webBrowser1 to populate its Document with the desired webSite.
However, I am unable to programatically click the 2nd button to acquire the web link for file download.
I have tried to place the 2nd buttons click event within the browser's DocumentCompleted event, as shown below.
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
btnMyUrl.PerformClick();
}
However, from what I've read on StackOverFlow and other sites, it's possible that this particular event gets called more than once, and hence it fails.
I've also attempted to loop for a number of seconds, or even use a Thread.Sleep(xxxx), but the browser window fails to populate until the sleep or timer stops.
I attempted to use the suggestions found on the following StackOverFlow site shown below.
How to use WebBrowser control DocumentCompleted event in C#?
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
string url = e.Url.ToString();
if (!(url.StartsWith("http://") || url.StartsWith("https://")))
{
// in AJAX
}
if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)
{
// IFRAME
}
else
{
// REAL DOCUMENT COMPLETE
}
}
However, in parsing the OuterHtml, nothing is returned in the first two sections, and in the third section, other elements are returned instead of the desired "https://..." link for File Download.
Interestingly, if I use a webBrowser1.ReadyState event, as shown below, and place a MessageBox inside DocumentCompleted, this seems to allow the browser document to complete, because after clicking the OK button, the parsing is successful.
if (webBrowser1.ReadyState == WebBrowserReadyState.Complete)
{
MessageBox.Show("waiting", "CHECKING");
btnMyUrl.PerformClick();
}
However, I then have the difficulty of finding a way to click the OK button of the MessageBox.
Is there another event that occurs after the DocumentCompleted event.
OR, can someone suggest how to programmatically close the MessageBox?
If this can be done in code, then I can perform the buttonClick() of the 2nd button in that section of code.
After finding that the addition of a MessageBox allows the webBrowser1.Document to complete, and using webBrowser1.ReadyState event within the webBrowser_DocumentCompleted event, all I needed to do, was to find a way to programmatically close the MessageBox.
Further searching on StackOverFlow revealed the following solution on the site below.
Close a MessageBox after several seconds
Implementing the AutoClosingMessageBox, and setting a time interval, closed the MessageBox and allowed my button click, i.e. btnMyUrl.PerformClick(); to successfully parse the OuterHtml and now the code works properly.
Hopefully, if someone else discovers that placing a MessageBox within the webBrowser_DocumentCompleted event allows the document to complete; the aforementioned AutoClosingMessageBox will assist them as well.

MessageBox changes something seemingly unrelated

Here's my setup: I have a TabControl with numerous tabs, and on the last tab, I have a UserControl that is added to a TabPage. This UserControl has a VisibleChanged event that is supposed to check if it is visible, and if it is displays certain information using CustomCommand().
void MyUserControl_VisibleChanged(object sender, EventArgs e)
{
//MessageBox.Show("");
UserControl us = sender as UserControl;
if (us.Visible)
{
CustomCommand();
}
//MessageBox.Show("");
}
Here is the problem: this code as-is will incorrectly think the UserControl is visible when it is not, and the CustomCommand() will run when it's not supposed to. When the commented-out MessageBox function is un-commented in either spot, the UserControl is correctly seen as not visible until, of course, the TabPage is selected. Does anyone have any idea why this would be?
Just putting it on a TabPage doesn't explain this problem. There must be other layout events involved that make the control actually visible later. The MessageBox.Show() call provides the time machine to get the Visible property checked with a delay.
The standard technique to get code to run later, the way MessageBox does, is by using the Control.BeginInvoke() method. The delegate target runs when all events have been fired and processed and the UI thread goes idle again. Like this:
void MyUserControl_VisibleChanged(object sender, EventArgs e)
{
UserControl us = sender as UserControl;
this.BeginInvoke(new Action(() => {
if (us.Visible)
{
CustomCommand();
}
});
}
Not sure if this will help you, but there is a strange, and afaik undocumented, asymmetrical behaviour with the Visible_Changed event:
It does get raised whenever the Visible property of the control changes, either to true or to false.
It also get raised whenever the Parent's Visible property changes to true and only to true!
I could not raise the event by hiding the Control by another control, or for that matter by the MessageBox, though.
I don't know how your application works, so I could not reproduce. But I did notice that indeed, when changing tabs the Visible_Changed event of a Control on the TabPage does get raised, whenever the page is selected (but not when it is unselected.) I didn't know either.
You can try to catch the other direction by either going for the SelectionIndexChanged or by hooking into the VisibleChanged event of the TabPage. This is not visible in the IDE, but it does work both ways:
tabPagexyz2.VisibleChanged += tabPagexyz_VisibleChanged;
void tabPage2_VisibleChanged(object sender, EventArgs e)
{
// do something
}
I saw wild guesses about UI race conditions being behind it, which I doubt. This would at least explain why a MessageBox would habe an influence. If you want to test, you could replace it with one the evil Applictaion.DoEvents.

How to ignore viewstate of a previous request for particular control?

I am dynamically generating controls, and sometimes I want to create a control and have it ignore the viewstate. For example, sometimes the user has clicked a button indicating they want a different form loaded, so the control tree I generate on postback is different from the original control tree. This is fine, except when I call Controls.Add then it tries to load the viewstate form the old controls into the new controls if the control tree structure is similar, and I want them to instead ignore that viewstate(and also ignore the postback values for input controls as well).
Can I do something like set the IDs of the controls or something that would allow me to conditionally prevent them from getting the viewstate/postback data of the previous request?
Edit: If I let the user of the control load the form on demand in postback handler, the postback data is not applied when I call Controls.Add(this really seems like a flaw in ASP.NET, because I would think if you're going to apply viewstate data "after the fact" through Controls.Add, it'd seem you would then apply the postback data automatically as well after the viewstate data is loaded). The real problem I run up against is my control is very dynamic, but the user of my control can't really tell it what to do until their postback handler fires, because one of the things a user can do is select different forms to be loaded via some link buttons. So it's not until the postback handler runs that they know what the uesr requested, and thus can ask my control to load a certain form. So I have to ask them to do convaluted things like saved the formID that identifies the last form to a session variable, and in OnInit they tell my form what the old formID was via a property. My control then loads the form in OnLoad so that it can consume the viewstate and postback data, and later in the programmer's postback handler, they can choose to clear the form and load a different one if they want.
Edit2: FYI Generating IDs for each control unique to the form works great, so I thought I could eliminate the pointless loading of the old form until the programmer requests a form be loaded in his postback handler. But as I mentioned above, what I found was that loading the form after postback data handling has occurred means that data is lost. Whereas viewstate gets loaded via Contorls.Add, playing catch up in the page lifecycle, it seems postback data does not! So it seems I am defeated at every turn.
You're going to avoid problems if you play along with the control lifetime. Basically, whenever you have a control that renders, it's best to ensure that control is recreated on the next postback, even if you aren't going to need it anymore. The first goal of a postback should be to restore the previous state -- only THEN do you make changes to it.
I described it best in this answer:
Wrong state in Server Controls with same ID's when dynamically adding UserControl to UpdatePanel
Giving the controls different ID's would certainly prevent ViewState from being loaded, that would be one way.
You may also be able to manipulate the ViewStateMode property of your controls by setting it to "Disabled". I'm not sure if this prevents it from loading (it definitely prevents them from saving viewstate), but you could try it.
Have you tried just calling controls.clear prior to adding in the new ones?
UPDATE
I'm starting to believe that you are generating the controls at the wrong point in the page lifecycle. What is your flow?
You must be generating dynamic controls on postback in pageload:
protected void Page_Load(object sender, EventArgs e)
{
--Generate Dynamic Controls--
}
You need to do it like:
protected override void OnInitComplete(EventArgs e)
{
base.OnInitComplete(e);
}
protected void OnInit()
{
--Generate Dynamic Controls--
}
protected void Page_Load(object sender, EventArgs e)
{
}

ASP .NET Button event handlers do not fire on the first click, but on the second click after a PostBack

Background: I am customizing an existing ASP .NET / C# application. It has it's own little "framework" and conventions for developers to follow when extending/customizing its functionality. I am currently extending some of it's administrative functionality, to which the framework provides a contract to enforce implementation of the GetAdministrationInterface() method, which returns System.Web.UI.Control. This method is called during the Page_Load() method of the page hosting the GUI interface.
Problem: I have three buttons in my GUI, each of which have been assigned an Event Handler. My administration GUI loads up perfectly fine, but clicking any of the buttons doesn't do what I expect them to do. However, when I click them a second time, the buttons work.
I placed breakpoints at the beginning of each event handler method and stepped through my code. On the first click, none of the event handlers were triggered. On the second click, they fired.
Any ideas?
Example of Button Definition (within GetAdministrationInterface)
public override Control GetAdministrationInterface()
{
// more code...
Button btn = new Button();
btn.Text = "Click Me!";
btn.Click += new EventHandler(Btn_Click);
// more code...
}
Example of Event Handler Method Definition
void Btn_Click(object sender, EventArgs e)
{
// Do Something
}
Page_Load Method that calls GetAdministrationInterface
protected void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsAsync)
{
List<AdministrationInterface> interfaces = <DATABASE CALL>;
foreach(AdministrationInteface ai in interfaces)
{
placeholderDiv.Controls.Add(ai.GetAdministrationInterface());
}
}
}
Good grief! I knew it was going to be something this stupid. Purely my fault of course and my lack of knowledge in ASP .NET.
After doing a multitude of Google searches and eventually being blocked by Google on suspicion of being a bot running automated scripts, I managed to squeeze in one last search in and stumbled across this article. Already at the point of giving up, I tried my best to read the article without skipping 10 lines at a time or looking for pretty pictures. In the section titled Assigning IDs to Dynamically Created Controls, I read these magical and most joyful words:
If you view the source HTML before you click the not-working button and after you have clicked it, you will notice a small difference. The buttons have different HTML IDs before and after the post-back. I got ctl04 and ctl05 before the post-back and ctl02 and ctl03 after the post-back.
ASP.NET button recognizes events by checking for a value for its ID in the Request.Form collection. (In truth it happens differently and controls do not check Request.Form collection by themselves. Page passes post data to controls by their IDs and to controls that are registered to be notified about post data). ASP.NET does not fire the Click event, because the button's ID has changed between the post-backs. The button you have clicked and the button you see after are different buttons for ASP.NET.
Sure enough, when I viewed the HTML the first time, my button had the ID ctl04$ctl36. After clicking the button, my button had the ID ctl04$ctl33.
So there you have it! All I had to do was set the ID on the buttons and presto! My event handlers are now being called!
Sample Solution:
public override Control GetAdministrationInterface()
{
// more code...
Button btn = new Button();
btn.Text = "Click Me!";
// !!THE BANE OF MY EXISTENCE!!
btn.ID = "The_Bane_of_My_Existence";
// !!THE BANE OF MY EXISTENCE!!
btn.Click += new EventHandler(Btn_Click);
// more code...
}
What a great way to spend two days...
I had the same problem, but the accepted answer here was not causing it. I had a text box and a search button, and clicking the button the first time didn't perform the search. The event handler of the button wasn't being hit. But clicking the button a second time did trigger the event on the server. Here is why:
If you have an <asp:Textbox> with its AutoPostBack set to true, after typing in the text box and then moving to click a button, the text box causes a post-back immediately the moment it loses focus. So the click even of the button doesn't count (the page is already posted-back as a result of the text box's event). That's why when you click the button a second time, it works because the text box is not involved in the second post-back.
Set the AutoPostBackproperty of the <asp:Textbox> to false to fix this issue.
A quick fix is to set an ID to the ASCX control your are loading on a page. For example, if your code is like this:
UserControl SpecsControl = (UserControl)Page.LoadControl("../name.ascx");
SpecsContainer.Controls.Add(SpecsControl);
then you need to add a line (before Controls.Add):
SpecsControl.ID = "Aribtrary_Name";
Then your handler method is fired at the first click.
I was facing the same problem. My button froze after my first click. For me this annoying problem got solved when I disabled the button's EnableViewState attribute.
For me it was the UpdatePanel , my Button and my TextBox were both inside an UpdatePanel , so when I post-back , it caused some weird behavior . It took it outside of the UpdatePanel and that fixed it .
Even i had the same problem. the cause was "localhost:1656/secure/login.aspx?ReturnUrl=%2f".
if the request contain %2f as query string, the first post will not be succeeded even though "%2f" is representing "/".
one way to avoid this by having a condition check in pageload
protected void Page_Load(object sender, EventArgs e)
{
string queryString = Request.QueryString.ToString();
if(queryString == "ReturnUrl=%2f")
{
Response.Redirect("/secure/login.aspx");
}
}
Whilst its hard to know exactly without seeing the full Page_load method it does smell a little bit like the event handlers are not hooking up until the page is reloaded.
eg:
if (IsPostBack) {
// Add handlers here ...
}
I had same problem. And I searched on internet i didnt find a solution. After that i found sample code and I used it. It worked for me. Web site link is below:
http://www.c-sharpcorner.com/UploadFile/abhikumarvatsa/calling-an-Asp-Net-C-Sharp-method-web-method-using-javascript/

Why would my child controls be uninitialized at the time of event attachment?

I have a page and a user control — we'll call them Detail.aspx and Selector.ascx.
Let's say the page shows the details of individual records in a database. The user control basically consists of a DropDownList control and some associated HTML. The DropDownList displays a list of other records to switch to at any time.
When the DropDownList fires its SelectedIndexChanged event, I'd like the parent page, Detail.aspx in this case, to handle it. After all, he'll need to know what was selected so that he can appropriately change the URL and the details shown, etc.
To do that, I've done what I usually do, which is also what the top answer says to do in this StackOverflow question:
public event EventHandler DropDownSelectedIndexChanged
{
add
{
MyDropDownList.SelectedIndexChanged += value;
}
remove
{
MyDropDownList.SelectedIndexChanged -= value;
}
}
The above code appears in the Selector.ascx.cs codebehind file.
As a result, on Detail.aspx, I can use it like so:
<cc1:RecordSelector ID="RecordSelector1" runat="server"
OnDropDownSelectedIndexChanged="RecordSelector1_DropDownSelectedIndexChanged" />
So far nothing fancy or surprising.
Here is my problem:
This causes a NullReferenceException when the browser hits Detail.aspx.
Debugging the problem shows that when the page is first hit, the public event I've shown above tries to add the event, but MyDropDownList is null, thus throwing the exception. From what I can tell, the events are added (or attempted to be added) before the Selector user control's Load event fires and thus also before the DropDownList's Load event fires.
Curiously, if I omit the OnDropDownSelectedIndexChanged attribute from Detail.aspx and instead put the following in the Page_Load event in Detail.aspx.cs:
protected void Page_Load(object sender, EventArgs e)
{
RecordSelector1.DropDownSelectedIndexChanged += new EventHandler(RecordSelector1_DropDownSelectedIndexChanged);
}
It works exactly as expected. The events are attached and handled just fine. No problems.
But this means several bad things:
I have to remember not to use the designer to add said event onto my user control
I have to remember not to add the event via attributes when working in source view
Worst of all, as the control's author I need to make sure everybody else using my control knows 1 and 2
So what am I doing wrong? Every example I've seen thus far shows similar usage of exposing child controls' events through a user control.
The reason this works:
protected void Page_Load(object sender, EventArgs e)
{
RecordSelector1.DropDownSelectedIndexChanged
+= new EventHandler(RecordSelector1_DropDownSelectedIndexChanged);
}
and this does not:
<cc1:RecordSelector ID="RecordSelector1" runat="server"
OnDropDownSelectedIndexChanged="RecordSelector1_DropDownSelectedIndexChanged" />
is because the first one adds the handler after the control has been initialized (via the page's Init). The second example gets parsed much earlier and as such the page is attempting to add the handler before the control has initialized.
Due to the nature of the page's life cycle I think you may have to live with adding the event handler in the code-behind. There will be no way to add the handler before the control is initialized because that control will always be null prior to initialization.

Categories