Wait until a page is fully loaded in WebBrowser Delphi - delphi

I am using the code below to wait for a page to be fully loaded when I browse by code, and that works perfectly, but when I click on a link in a page, the WebBrowser1DocumentComplete or WebBrowser1NavigateComplete2 event fires several times if the page contains frames, of JS etc.
I would like to be able to wait for the page to load completely when I click on the link, but I don't know how.
WebBrowser1.Navigate(URL);
repeat
Application.ProcessMessages;
Sleep(0);
until WebBrowser1.ReadyState = READYSTATE_COMPLETE;

First, you should not be using such a loop at all, you shoud be using the TWebBrowser.OnDocumentComplete event to detect when the document is ready.
Second, getting multiple OnDocumentComplete/OnNavigateComplete2 events is expected behavior when frames are used in the HTML. You get an event for each frame, and a final event from the main window.
This behavior is documented on MSDN:
https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa768282(v=vs.85)
The WebBrowser Control fires the DocumentComplete event when the document has completely loaded, and the READYSTATE property has changed to READYSTATE_COMPLETE. Following are some important points about the firing of this event.
In pages with no frames, this event fires one time after loading is complete.
In pages where multiple frames are loaded, this event fires for each frame where the DownloadBegin event has fired.
This event pDisp parameter is the same as the IDispatch interface pointer of the frame in which this event fires.
In the loading process, the highest level frame, which is not necessarily the top-level frame, fires the final DocumentComplete event. At this time, the pDisp parameter is the same as the IDispatch interface pointer of the highest level frame.
And even in Embarcadero's DocWiki:
http://docwiki.embarcadero.com/Libraries/en/SHDocVw.TWebBrowser.OnDocumentComplete
Write an OnDocumentComplete event handler to take specific action when a frame or document is fully loaded into the Web browser. For a document without frames, this event occurs once when the document finishes loading. On a document containing multiple frames, this event occurs once for each frame. When the multiple-frame document finishes loading, the Web browser fires the event one final time.
So, in the TWebBrowser.OnDocumentComplete, you need to check the pDisp parameter to see whether it is the IDispatch of the main document or not.

Related

Delphi 11 VCL TTrackBar does not have Tracking property, events fire on each tick change

In Delphi FMX, TTrackBar has two events for tracking changes - OnChange and OnTracking.
Those two events only do the same thing when Tracking is enabled (by default). Disabling the Tracking will make it so that OnChange is only fired after the user is finished.
My problem is that using Delphi 11 for a VCL application, the Tracking property does not exist, and the events fire with each change/tick instead at the end (resulting in sending multiple messages).
I would prefer only the end change / last value), according to this older post:
Delphi TTrackBar doesn't have on complete event
TTrackBar in VCL does not have an event when tracking is finished. However, the underlying Win32 Trackbar control does send such notifications.
It sends WM_HSCROLL/WM_VSCROLL messages (depending on its orientation) to its parent window, where the LOWORD(wParam) is set to TB_ENDTRACK. You can subclass the parent window to handle these notifications.
It can also send a TRBN_THUMBPOSCHANGING notification to the parent window, where the lParam holds a pointer to a NMTRBTHUMBPOSCHANGING struct, whose nReason field is set to TB_ENDTRACK. You can subclass the TTrackBar itself to catch CN_NOTIFY messages to handle this notification.

How to recognize that a form is in focus through event?

In an application, we have multiple forms. Now we want to refresh a form 'X' when user sees the form 'x' either by restoring it from taskbar or with 'alt+tab'. How to recognize this through event.
The events 'OnActivate','OnShow' get called only once when the form is created. So they are not useful here.
Your claim that the TForm.OnActivate and TForm.OnShow events are fired only once per TForm instance is not true.
The TForm.OnActivate event is fired when a TForm window gains input focus for the first time, and afterwards whenever input focus is transferred to that window from another TForm window, while the app is in the foreground.
Note that there are also TApplication.OnActivate and TApplicationEvents.OnActivate events that are fired when your app comes into the foreground for the first time, and afterwards whenever focus moves to another app and then back to your app.
The TForm.OnShow event is fired when a TForm window becomes visible for the first time, and afterwards whenever that window becomes hidden and then is reshown.

Screen frame render finished / presented callback

I'd like to know the exact moment when screen frame finishes rendering and gets displayed, so I can capture specific window images with quartz api. There is CVDisplayLink / CADisplayLink that allows application to synchronise its drawing to the refresh rate of the display.
As I understand, those methods are very similar to what I need, but invoked when screen frame needs to be rendered, not when it finished and presented, meaning that if I capture the current screen image at that time I'd only see the previous render. Is this correct thinking?
If yes, is there a way to do get notified when the frame has actually finished rendered and was presented? Maybe there's private api for that? Ideally I need an answer for macOS, but if you happen to know how to do this in iOS, please let know.
P.S. I'm aware of CGDisplayStream, it's a great solution for capturing the whole screen, not specific windows – it uses run loops and dispatch queues, meaning that callbacks are not invoked in realtime like with display links.

Syncronizing TMediaPlayer.Position and TTrackBar.Position via LiveBindings

I have a TTrackBar and a TMediaPlayer, I'm looking for a way to change the TTrackBar position according to the TMediaPlayer position using the LiveBindigs feature.
The problem is, there is no event on the TMediaPlayer to watch the changes of the TMediaPlayer.Position property, so my TTrackBar.Position can't synchronize.
Is it possible to watch the changes of a component property without trigger an event?
Not it is not possible to monitor changes of certain property without suitable event.
And you would not want to have any event binded to MediaPlayer.Position property either. Why?
For instance when you are playing a video position is changed for each and every frame which menans that when playing a video with 30 FPS such event would be fired 30 times per second. So depending on the code in that event it could quickly bring your application to a crawl.
So best suggestion that I can give you is for you to place a timer on your form and then check media player position in certain intervals to update your TrackBar. I believe one second interval would is more than enough but you can make it shorter if you will.
Just make sure that if you also use TrackBar for seeking ability to use some control variable to see whether the TrackBar position is being updated by user or by your Timer. Other vise you will end up with weird stuttering (happened to me the first time).
As for achieving all this with LiveBindings alone I don't think it is possible.

Delphi - Simulate Click Animation

This is my first post here on stackoverflow so forgive me for anything I'm doing wrong.
I'm making a kind of guide to the users without any computer knowledge of my application where I show him how to use it by signalizing what he should do, more specifically where to click. I want to that by moving a "fake" cursor to the button and simulate a click, and here is where I got my problem, I have to simulate just the animation of the click, and not the event itself but I couldn't find a way to do that, can anyone help me?
What you're describing is exactly what WH_JOURNALPLAYBACK is for. It populates the message queue with the mouse and keyboard messages you want to occur, and the OS interprets them. In your case, activate the playback hook and perform the mouse events necessary for performing a click.
In preparation, you'll probably want to use WH_JOURNALRECORD to discover what messages you need. Once you have them, you can probably winnow them down to a reasonably sized list prior to shipping your product to customers. (In particualr, you'll probably record many more mouse-move messages than you really need.)
In your button's click handler, check whether playback is active. Only perform the rest of the event handler when playback isn't active. That way, your program will behave just as though the button were clicked (including any animation), but it won't execute the real event code.

Resources