mshtml how to hook into document's readystatechange event - c#

I'm converting an IE window's handle to mshtml.HTMLDocumentClass using the way as described here.
There are a couple of really weird things happening here.
When I hook into the onreadystatechange event for frame document (get the frame using document.getElementById and hook into frame onreadystatechange), the event gets fired only once.
Problem #1 disappears when I detach the event handler and reattach it to the same method.
THE MAIN PROBLEM , after doing this the web document freezes, textboxes don't receive focus , buttons can be clicked but they appear like read only buttons All text type controls become readonly, after digging through the problem a little , I found that this behaviour appears as soon as you hook into the frame document's onreadystatechange event. Detaching from the event brings the page back to normal behaviour.
Here's the code which I'm using.
this.GuideDocument = this.GetHtmlDocumentByWindowHandle(handle) as mshtml.HTMLDocumentClass;
var frame = (this.GuideDocument.frames.item(0) as mshtml.IHTMLWindow2).frames.item(1) as mshtml.IHTMLWindow2;
var frameDocument = frame.document as mshtml.HTMLDocumentClass;
frameDocument.HTMLDocumentEvents2_Event_onreadystatechange += frameDocument_HTMLDocumentEvents2_Event_onreadystatechange;
void frameDocument_HTMLDocumentEvents2_Event_onreadystatechange(IHTMLEventObj pEvtObj)
{
frameDocument.HTMLDocumentEvents2_Event_onreadystatechange -= frameDocument_HTMLDocumentEvents2_Event_onreadystatechange;
frameDocument.HTMLDocumentEvents2_Event_onreadystatechange += frameDocument_HTMLDocumentEvents2_Event_onreadystatechange;
this.DisableButton("btnClose", string.Empty, framedocument);
}
I'm not exactly sure as to what's missing here, Do you see anything wrong here, or any alternate approach. This web page is composed of many framesets and thus frames. any insights into this will be a gr8 help.
I somehow feel, that it requires some base.onreadystatechange thing, but I don't know if there is one available. Or some other way to let the original event get called etc..
Thanks,

Related

How to know when a navigation transition ends using UWP's Frame?

I'm using Frame.Navigate and I want to know when the resulting navigation transition (animation) ends.
My code looks like this:
myframe.Navigate(typeof(MyPage), parameter: null, new SlideNavigationTransitionInfo()
{
Effect = SlideNavigationTransitionEffect.FromRight
});
Using SlideNavigationTransitionInfo works correctly and I can see the slide animation.
However I can't find the way to be notified once the animation completed.
I tried subscribing to Frame.Navigated, but that event isn't related to the animation. It fires once the view is available from the Frame.Content property, which is too soon in my situation.
I also tried subscribing to Page.Loaded, but that is also too soon because that event is fired when the view entered the visual tree. (I say it's too soon because the view is visible during the animation, which means it had time to load before the animation.)
I don't know what to try next.

Winform app, force execute OnLoad Event when focus is on another tab

I have a WinForm app, the form has TabControl, control has three tabs tabPage1,tabPage2,tabPage3.
The Tab 'tabPage3' is hosting a User defined control which internally has one or more child controls.
Now my problem lies in tabPage3,
I know it is a pure Winforms behavior, until your parent is not activated child controls Onload event won't fire.
I have a requirement to force the Onload event to fire when the focus is on tabPage1, tabPage2. Is there any way to force the Onload event to fire.
I have already visited following links but didn't find any clue. Link Link Link
This is a very unusual requirement, strongly smells like an XY problem. The Load event is heavily over-used in Winforms, a side-effect of it being the default event for a Form or UserControl. One of the behaviors inherited from VB6, the Load event was a big deal in that language. What you want can easily be accomplished by not giving Winforms a choice:
public UserControl3() {
InitializeComponent();
CreateHandle();
}
The CreateHandle() call does the forcing, OnLoad will immediately run. But do be aware that this happens very early, too early to do the kind of things that you'd really want to use OnLoad() or the Load event for. Which are rather limited, it is only truly necessary to discover the actual Location and Size of the control. Anything else belongs in the constructor. Surely including the code that you now run in OnLoad().
Strongly favor using the constructor instead.
I had a similar problem for a previous project, for my needs I managed to just iterate over every tab page in the forms constructor (or possibly OnLoad I can't remember) and then reset the index back to 0 before ever showing the end user.
Something similar to:
for(int i = 1; i < tabControl.TabCount; i++)
tabControl.SelectTab(i);
tabControl.SelectTab(0);

How do I perform sequential BringIntoView calls in WPF?

We have a TreeView in our application with the following requirements:
When an item is added:
The newly-added item is scrolled into view
The parent of the newly added item is also scrolled into view.
If they are too far away to both be seen at the same time, the item takes precedence.
This seems easy, simply scroll the parent into view first, then scroll the child.
The problem is when you call it like this:
parent.BringIntoView();
child.BringIntoView();
...only the second one seems to have any effect. The first one is basically ignored.
I then tried wrapping the second call in a BeginInvoke() call like this:
parent.BringIntoView();
Dispatcher.BeginInvoke((Action)(() => {
child.BringIntoView();
}));
Which does work, but now you can visibly see the TreeView scroll twice; once for the parent, then a moment later, for the child, which just looks bad.
So how can I call BringIntoView back-to-back but without the double-refresh issue of using the dispatcher?
Try using the Loaded event instead of the dispatcher. According to this article, it's a perfect fit for situations like this:
... we initially implemented the Loaded event so that
it would fire just after the window was rendered, but before any input
was processed. We figured that if it was ready enough for input, it
was ready enough for load-time initialization. But then we started to
trigger animations off of the Loaded event, and saw the problem; for a
split second you’d see the content render without the animation, then
you’d see the animation start. You might not always notice it, but it
was especially noticeable when you run the app remotely.
So we moved
Loaded so that it now fires after layout and data binding have had a
chance to run, but just before the first render. (And note that if
you do anything in your Loaded event handler that invalidates layout,
it might be necessary to re-run it before rendering.)
In other words, on Loaded you have the most up to date information about the physical layout of the element, but it hasn't actually rendered yet, so you should be safe from any "screen flicker" issues.
EDIT: To answer your question in the comments, you can wire up events "local" to the current method using a closure, like this:
EventHandler handler = null;
handler = (sender, e) => {
this.LayoutUpdated -= handler; // only run once
child.BringIntoView();
};
this.LayoutUpdated += handler;
By defining the handler inside the method, you are able to access the method's local variables (child) from within. Very similar to the Dispatcher call.
I'm not sure if relying on LayoutUpdated is a good idea, actually. It happens quite often so it may end up firing sooner than you need. It happens twice for individual Width and Height settings, for example. Another one to look into is ScrollViewer.ScrollChanged. Or you could avoid BringIntoView altogether and try manually examining the element sizes to calculate where to scroll to.

Maps control SetView works only on the page's first load

I have a problem with Bing Maps control I use in my WP7 application.
When I'm navigated to the page with map control, it starts the GeoCoordinateWatcher. When GeoCoordinateWatcher has some location data for me, it calls a SetView() method to center the map at current location. There is also a button on ApplicationBar that's also starting the GeoCoordinateWatcher.
Now, the thing is, when I first navigate to this page, all works fine: GeoCoordinateWatcher starts, gives me the location data after some time, calls SetView() and the map centers when I need it too. The same with the button.
But if I press the Back button to get back to the main page and then try to navigate again to the map page, SetView() stops working. In debugging I see that the code surrounding it works as expected and the data passed to SetView() is correct, but nothing happens with the control and events for changing view don't fire either.
I'm assuming there may be something wrong with map initialization (or disposing on navigating from), but I don't know where to dig.
Edit:
I've tried changing Map.Center directly and it doesn't work in exactly the same way the SetView() doesn't: it works fine the first time page is navigated to and doesn't work on the other times.
Edit 2:
Ok, it gets weirder. In debugging I see that my map's center actually gets set to a correct value. But an actual control shows absolutely different location and me, moving map around, doesn't change the value that I see from code.
Edit 3:
I've added a button to ApplicationBar that just calls SetView() and it works fine. Apparently, the problem appears when I call SetView() as a result of GeoCoordinateWatcher.PositionChanged event raising. How could I work around that?
Try using map.Center = loc.CoOrdinates;
I've found the solution.
In my original code I've subscribed to the GeoCoordinateWatcher.Position property change event in the page constructor: App.PropertyChanged += AtmInfoPageOnAppPropertyChanged
All I had to do was to move that to the OnNavigatedTo event handler and add App.PropertyChanged -= AtmInfoPageOnAppPropertyChanged to the OnNavigatedFrom event handler.
I think, the problem was that keeping the old page subscribed to the event didn't allow it to be disposed, and at the same time didn't allow the new page to subscribe to that same event, thus causing the code to be called for the different page and different map control than the one displayed on the screen.

how to determine when page is loaded completely in webbrowser control in csharp?

Hi
Im developing an IE toolbar in C Sharp that accesses the html source of page displayed in IE and process it. I'm relying on the ondocumentcomplete event handler. When it is fired, the html source is taken. But i have a trouble with pages having frames since ondocumentcomplete is fired for each frame, the last being the outter frame, the window. In order to tackle this I did like this -
public void OnDocumentComplete(object pDisp, ref object URL)
{
if (pDisp.Equals(Explorer.IWebBrowser_Parent))
{
// check for frames n get the source of all
}
else
// ignore
}
Like this i was able to get the source of all the frames.
But the problem is for certain pages that have frame or iframe, ondocumentcomplete is not fired for the window. so the above code would not process any such e.g -
http://www.w3schools.com/html/tryit.asp?filename=tryhtml_intro
fires an ondocumplete for that url,
however clicking on edit and click me button though reloads the frame, doesnt fire ondocumentcomplete for the window. How to work around this?
The question seems to be: How do I know when I stop receiving data from my request?
If you phrase it like that, you may be able to apprehend when you do, aka, you are not getting any more data (set up a timer to check for that), or the connection to the page closes, etc.

Categories

Resources