In the last post I’ve shown how to use the new WebView2 control in a WPF app and said that it could be used in any Windows version and in any platform. As a matter of fact, I can create a 64 bit VCL native application with Delphi, that uses the Win32 API with the same control, offering the same functionality that the previous app did. To do that, you must have Delphi 10.4 Sydney installed in your machine. It offers the TEdgeBrowser component, that can be used to browse the web using the Chromium component.
In Delphi, create a new VCL application. Add the 64 bit platform to it and set it default. Add a TPanel, docked at the top. Then add a TEdgeBrowser and doc it to fill the window. In the panel, add a TLabel and change the Caption property to Search Text, add a TEdit and clear its Text property. Then, add two buttons and set their property Caption to Find and Copy. Add two more buttons at the right of the panel and set their sizes to 23×23, and their font Height to 10, Name to Segoe MDL2 Assets and their caption to the Back and Forward icons in the Character Map:
You should have something like this:
Now you can set the OnCreate event of the main form to
EdgeBrowser1.Navigate('https://docs.microsoft.com');
and run the application, but you won’t see nothing in the window. You can check the initialization of the WebViewer in the OnCreatedWebViewCompleted event and check the AResult parameter.
procedure TForm1.EdgeBrowser1CreateWebViewCompleted(Sender: TCustomEdgeBrowser; AResult: HRESULT); begin if AResult <> 0 then ShowMessage('Error initializing WebView: $'+IntToHex(AResult)); end;
You will see that there is an error code (in my machine it’s $80004005). This is due to the fact that the WebView dll is missing. The dll is in the Redist folder and you have to add a PostBuild command to the project. Go to Project/Options and select Build options and, in the command “Value from All Configurations – Windows 64 bits”, add the following command:
copy /Y "C:\Program Files (x86)\Embarcadero\Studio\21.0\Redist\win64\WebView2Loader.dll" $(OUTPUTDIR)
This will copy the dll to the output directory and will initialize the WebView correctly(you will see that there is no message in the OnCreatedWebViewCompleted event and the page is loaded. Now, we can add the event handlers for the buttons. The Find button will set up the address and call the Navigate method (You must add the NetEncoding unit to the Uses clause):
procedure TForm1.FindClick(Sender: TObject); begin if Edit1.Text <> '' then begin var address := 'https://docs.microsoft.com/en-us/search/?terms=' + TNetEncoding.URL.Encode(Edit1.Text); EdgeBrowser1.Navigate(address); end; end;
The Back and Forward buttons events are very simple to implement:
procedure TForm1.BackClick(Sender: TObject); begin if EdgeBrowser1.CanGoBack then EdgeBrowser1.GoBack; end; procedure TForm1.ForwardClick(Sender: TObject); begin if EdgeBrowser1.CanGoForward then EdgeBrowser1.GoForward; end;
To remove the parts of the page that we don’t want, we use the OnNavigationCompleted event to inject the JavaScript code:
procedure TForm1.EdgeBrowser1NavigationCompleted(Sender: TCustomEdgeBrowser; IsSuccess: Boolean; WebErrorStatus: TOleEnum); begin if IsSuccess then EdgeBrowser1.ExecuteScript( ' var rss = document.querySelector(''[data-bi-name="search-rss-link"]'');'+ #13#10 + ' console.log(rss);'+ #13#10 + ' if (rss)'+ #13#10 + ' rss.style.display = "none";'+ #13#10 + ' var form = document.getElementById("facet-search-form");'+ #13#10 + ' console.log(form);'+ #13#10 + ' if (form)'+ #13#10 + ' form.style.display = "none";'+ #13#10 + ' var container = document.getElementById("left-container");'+ #13#10 + ' console.log(container);'+ #13#10 + ' if (container)'+ #13#10 + ' container.style.display = "none";'+ #13#10 + ' var hiddenClasses = ["header-holder", "footerContainer"];'+ #13#10 + ' var divs = document.getElementsByTagName("div");'+ #13#10 + ' for( var i = 0; i < divs.length; i++) {'+ #13#10 + ' if (hiddenClasses.some(r=> divs[i].classList.contains(r))){'+ #13#10 + ' divs[i].style.display = "none";'+ #13#10 + ' }'+ #13#10 + ' }'); end;
You can see here the same JavaScript code we used in the previous post, it also logs to the console the value of the variables. You can also open the Developer Tools of the browser component by pressing F12.
Now, there is only the Copy button to get the results and copy them to the clipboard. We will inject the code that gets all the results and send them to the application:
procedure TForm1.CopyClick(Sender: TObject); begin EdgeBrowser1.ExecuteScript( 'var results = [...document.querySelectorAll(''[data-bi-name="result"]'')]'+ '.map(a => {'+ #13#10 + ' let aElement = a.querySelector("a");'+ #13#10 + ' return {'+ #13#10 + ' title: aElement.innerText,'+ #13#10 + ' link: aElement.getAttribute("href")'+ #13#10 + ' };'+ #13#10 + '});'+ #13#10 + 'console.log(results);'+ #13#10 + 'if (results.length >= 1){'+ #13#10 + ' window.chrome.webview.postMessage(results);'+ #13#10 + '}'+ #13#10 + 'else {'+ #13#10 + ' alert("There are no results in the page");'+ #13#10 + '}'); end;
This is the same code that is injected in the WPF app, with a slight difference: in the WPF program we’ve sent a message and added a listener in the JavaScript code. Here, we are running the code when the user clicks the button. This will send a message to the app, that will be processed in the OnMessageReceived event:
procedure TForm1.EdgeBrowser1WebMessageReceived(Sender: TCustomEdgeBrowser; Args: TWebMessageReceivedEventArgs); var json : PWideChar; begin var msg := Args as ICoreWebView2WebMessageReceivedEventArgs; msg.Get_webMessageAsJson(json); Clipboard.AsText := json; ShowMessage('Results sent to clipboard'); end
Now, when you run the program, you will have the same results as in the WPF program:
As you can see, with the new WebView2 component you have a lot of flexibility, you can use it in .NET or Win32 programs with almost no change. You can use this component to browse the Web and get data from the browsing, or you can use it to complement your current app: let’s say you have parts of your app that are already written for the web and you don’t want to rewrite them, but use them to interact with your app, you can add these parts and include them in your desktop app.
The full source code for this project is at https://github.com/bsonnino/WebViewDelphi
Hello
This is not an example of how to use WEBVIEW2
This is an example of how to use TEdgeBrowser on top of WebView2
Remember that there are many people that do not have the Latest Delphi version
So we are interested to use native WEBVIEW2 interfaces
Cheers
Hi, is there advice anywhere on how to use WebView2 in older compilers that don’t have the TEdgeBrowser component?
In this case, you will have to use the COM Interfaces for IWebView2. You can base on this article https://docs.microsoft.com/en-us/microsoft-edge/webview2/get-started/win32 (yes, it’s for C++, you will have to convert it to Delphi).
CreateCoreWebView2Environment function is in WebView2Loader.dll, so you’ll have to load the library and use GetProcAddress to get the method. Then it will be similar to what’s pointed in the article
Hi Bruno
Many thanks for this nice example using TEdgeBrowser component!
I tried to create your example in C++Builder but I don’t know how to convert the code in the WebMessageReceived event handler especially:
var msg := Args as ICoreWebView2WebMessageReceivedEventArgs;
I think the rest of the code works fine.
Kind regards, Rolf
Did you try
msg = (ICoreWebView2WebMessageReceivedEventArgs)Args;
Hi Bruno
Many thanks for this nice example using TEdgeBrowser component!
I implemented it in C++Builder. Hardest part was the event handler for OnWebMessageReceived. I wanted to share the code here but it deleted my messages.
Best regards, Rolf
Send me the code (sonnino at revolution dot com dot br), I’ll post it here
There’s an open source project at GitHub called WebView4Delphi that can be used with older Delphi versions and Lazarus.
WebView4Delphi is updated regularly, it supports the latest WebView2 features and it includes several demos.