5

I have a form that has a TWebBrower component that loads an HTML document. The data on the HTML document is updated every few seconds, sometimes multiple times every second and I update the value in Delphi using:

DOMDocument.getElementById(elementID).innerHTML := someValue;

The problem is I want to lock the window/webbrowser to prevent it from painting/updating until all my updates are complete. Is there any way to do this? Would a call to

SendMessage(WebBrowser.Handle,WM_SETREDRAW,0,0);

I would like some help in optimizing this code so that my total CPU usage isn't continuously high.

Warren P
  • 65,725
  • 40
  • 181
  • 316
Wilbert Chua
  • 101
  • 1
  • 5
  • `IViewObject` interface has the `Freeze` and `UnFreeze` functions that might be useful for this. I'll try to prepare an example. – TLama Apr 18 '12 at 10:32
  • I think we need to see real code here (including the HTML). `WM_SETREDRAW` actually works (in a very limited test I made). but since `innerHTML` works asynchronously, I think it would be a problem to synchronize it... – kobik Apr 18 '12 at 10:35
  • @kobik, but if you say the `innerHTML` is asynchronous then I'm afraid even `IViewObject` doesn't help. It would have to be some check for ready state. – TLama Apr 18 '12 at 10:55
  • @TLama, thanks, I'll wait for the sample that you'll prepare. – Wilbert Chua Apr 19 '12 at 00:34
  • @kobik, i'm think it'll be long if i post the code here. basically, the html displays futures data (like prices for oil, gas) and the data is updated realtime, not just one field but may fields. the call to innerHTML is done in Delphi code, not javascript. – Wilbert Chua Apr 19 '12 at 00:38
  • btw, the main reason for this optimization is not because it's flickering, it's because during updates, it takes up a lot of cpu power, but when i commented the innerHTML line and just do the update routine, cpu processing power is "normal" for the application. – Wilbert Chua Apr 19 '12 at 01:02
  • I already did (and deleted), because it didn't locked the canvas as I expected. Anyway, why don't you just check the frequency of your changes and update the `innerHTML` only after some time if there will be many of them in a short time period ? – TLama Apr 19 '12 at 12:37
  • 1
    What do you mean by "it'll be long if i post the code here"? post it somewhere else then. If you want help, we need to be able to reproduce the problem. or provide another smaller code that illustrates what you say. your question is vague as is. – kobik Apr 19 '12 at 20:38
  • @kobik, maybe I'm wrong with the idea of bypassed updating. Maybe OP wants to update the `innerHTML` of several elements, but still the logic would be similar, only the would have to be extended for checking of what element has been updated and when. But the question remains vague. – TLama Apr 19 '12 at 20:51
  • @TLama, he did mentioned `DOMDocument.getElementById(elementID).innerHTML`. so I'm assuming he is updating different elements. maybe ready-state should be tested also. a lot of maybes... :-P – kobik Apr 19 '12 at 20:56

2 Answers2

4

Since your main problem is update frequency, then you need to decrease it. For this you can simply store the last time you've updated the HTML document and at next data change check if a certain period elapsed since that time.

Here is the code that shows how to do this. The FUpdatePeriod in the following example is the update period in milliseconds. Then if you call the UpdateChanges periodically, the innerHTML (pseudo-code here) will be updated only when at least 1000 ms elapsed since its last change.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, MSHTML, OleCtrls, SHDocVw;

type
  TForm1 = class(TForm)
    WebBrowser1: TWebBrowser;
    procedure FormCreate(Sender: TObject);
  private
    FLastUpdate: Cardinal;
    FUpdatePeriod: Cardinal;
    procedure UpdateChanges(const AData: WideString);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FUpdatePeriod := 1000;
end;

procedure TForm1.UpdateChanges(const AData: WideString);
begin
  if (GetTickCount - FLastUpdate > FUpdatePeriod) then
  begin
    (WebBrowser1.Document as IHTMLDocument2).body.innerHTML := AData;
    FLastUpdate := GetTickCount;
  end;
end;

// now remains to call the UpdateChanges periodically

end.
TLama
  • 75,147
  • 17
  • 214
  • 392
1

This is a sideways answer; But when we had flicker issues with TWebBrowser we fixed them by switching to using Google Chrome (DCEF) embedded into our application.

While I too originally felt that I could "optimize" TWebBrowser, and reduce it's flicker, I found out that the problem is just native to Internet Explorer. Aside from installing a new version of Internet Explorer, or rewriting all your JavaScript so your web page never flickers, because it has no active elements in the HTML layout (100% javascript free = no flicker) the only fix is to stop using Internet Explorer, and thus stop using TWebbrowser.

Secondly, flicker can also be caused if your TWebBrowser web page is accessing Delphi native methods as callbacks from the javascript. These callbacks, if they take a lot of time, will also always cause flicker in the embedded TWebBrowser, even if the only javascript that is being executed is the invocation of the delphi callback. The technique I am talking about (callbacks) is documented here.

Warren P
  • 65,725
  • 40
  • 181
  • 316
  • P, my question might be a bit too vague. I don't have flicker issues, my biggest concern is that the webbrowser sucks up a lot of cpu power when i'm calling innerHTML and would want to finish all call to innerHTML before painting it. thanks – Wilbert Chua Apr 19 '12 at 02:39
  • i'll consider using google chrome and see if there's going to be an improvement. thanks – Wilbert Chua Apr 20 '12 at 06:47
  • 1
    I am going to edit your question then. You shouldn't ask a question about X when you really mean Y. :-) – Warren P Apr 20 '12 at 11:07