This article has been updated for the version of WebView2 that requires Microsoft Edge WebView2 Runtime 88.0.705.50 or newer.
In the previous article, we learned how to create a web view and display web content in a Windows desktop application. In this third article of the series, we will look in detail at navigation and handling events, in general.
Navigation overview
To navigate to a web URL you must use the method Navigate() from the ICoreWebView2 interface. The only argument this method takes is the URL of the web page. However, you must specify the scheme, such as http:// or https:// or file://. URLs of the form www.bing.com or simply bing.com do not work. For this reason, in the code shown in the previous article, you can see this helper method for navigating:
CString CWebBrowser::NormalizeUrl(CString url) { if (url.Find(_T("://")) < 0) { if (url.GetLength() > 1 && url[1] == ':') url = _T("file://") + url; else url = _T("http://") + url; } return url; } void CWebBrowser::NavigateTo(CString url) { m_pImpl->m_webView->Navigate(NormalizeUrl(url)); }
What is happening here, we look if the URL contains ://. If it does not, but it starts with something like C: then we prefix it with file://, otherwise with http://.
During navigation, the web view control generates several events, as follows:
(Source: docs.microsoft.com)
NavigationStarting is the first event, generated when navigation begins. If HTTP redirection occurs, then multiple NavigationStarting events will be fired. When navigation completes, the event NavigationCompleted is fired. In between these, SourceChanged, ContentLoading, and HistoryChanged events may be generated.
You can learn more about navigation events here.
If you want to display some HTML content that you have locally, or generated on the fly, and you do not actually need to go to the web, you can use the method NavigateToString() that will display the HTML content provided as a string.
Handling navigation events
To handle web content events, you must register handlers. You can do this using the ICoreWebView2 interface. For instance, to handle NavigationStarting and NavigationCompleted, call add_NavigationStarting and add_NavigationCompleted. If you no longer wish to handle these events, you can remove the handlers by calling remove_NavigationStarting and remove_NavigationCompleted.
The same approach for registering and unregistering event handlers applies to all events. For an event X there is an add_X() and remove_X() pair of methods to add and remove handlers.
You can only register events after the web view control has been created and you have a valid pointer to the ICoreWebView2 interface. In the sample application, and the code shown in the previous article, the method OnCreateWebViewCompleted() contained a call to RegisterEventHandlers(). In this method, we add the handlers for the two navigation events.
void CWebBrowser::RegisterEventHandlers() { // NavigationCompleted handler CHECK_FAILURE(m_pImpl->m_webView->add_NavigationCompleted( Callback<ICoreWebView2NavigationCompletedEventHandler>( [this]( ICoreWebView2*, ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT { m_isNavigating = false; BOOL success; CHECK_FAILURE(args->get_IsSuccess(&success)); if (!success) { COREWEBVIEW2_WEB_ERROR_STATUS webErrorStatus{}; CHECK_FAILURE(args->get_WebErrorStatus(&webErrorStatus)); if (webErrorStatus == COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED) { // Do something here if you want to handle a specific error case. // In most cases this isn't necessary, because the WebView will // display its own error page automatically. } } wil::unique_cotaskmem_string uri; m_pImpl->m_webView->get_Source(&uri); if (wcscmp(uri.get(), L"about:blank") == 0) { uri = wil::make_cotaskmem_string(L""); } auto callback = m_callbacks[CallbackType::NavigationCompleted]; if (callback != nullptr) RunAsync(callback); return S_OK; }) .Get(), &m_navigationCompletedToken)); // NavigationStarting handler CHECK_FAILURE(m_pImpl->m_webView->add_NavigationStarting( Callback<ICoreWebView2NavigationStartingEventHandler>( [this]( ICoreWebView2*, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { wil::unique_cotaskmem_string uri; CHECK_FAILURE(args->get_Uri(&uri)); m_isNavigating = true; return S_OK; }).Get(), &m_navigationStartingToken)); }
The functions add_NavigationStarting() and add_NavigationCompleted, as well as the other event handler registration methods, take two arguments: the first is a pointer to a callback that is invoked when the event occurs, and the second is a pointer to a EventRegistrationToken object, which represent a reference to a delegate (the callback) that receives change notifications. This token is set by the function and must be passed to the method that removes the event handler. In other words, the token received from add_NavigationStarting() must be passed to remove_NavigationStarting() in order to be able to remove the event handler.
What we do in the event handlers above is the following. At the start of the navigation, we only set a Boolean flag that indicates that the navigation is in progress. We need this for instance if we want to have a button that we can press to stop loading a page if that takes too long. At the end of the navigation, the flag is reset but we also invoke a callback, if any was set by the caller when starting the navigation. In the demo app, we use a callback for the navigation completion from the main frame in order to update the URL in the toolbar with the URL resulted after the navigation, which may not be the original one (because HTTP redirects can occur).
In the previous article, we saw a method called CloseWebView() what closed the web view control. Here is the method updated with removing handlers for the navigation events.
void CWebBrowser::CloseWebView() { if (m_pImpl->m_webView) { m_pImpl->m_webView->remove_NavigationCompleted(m_navigationCompletedToken); m_pImpl->m_webView->remove_NavigationStarting(m_navigationStartingToken); m_pImpl->m_webController->Close(); m_pImpl->m_webController = nullptr; m_pImpl->m_webView = nullptr; m_pImpl->m_webSettings = nullptr; } m_pImpl->m_webViewEnvironment = nullptr; }
Handling other events
Let us look at another example for handling events. For this purpose we will consider the DocumentTitleChanged event that is occurring when the DocumentTitle property of the web view changes. This may occur before or after the NavigationCompleted event. To add/remove a handler for this event, you need a pointer to the ICoreWebView2 interface.
We can handle this event as follows, by adding a handler in the RegisterEventHandlers method we saw above.
CHECK_FAILURE(m_pImpl->m_webView->add_DocumentTitleChanged( Callback<ICoreWebView2DocumentTitleChangedEventHandler>( [this](ICoreWebView2* sender, IUnknown* args) -> HRESULT { wil::unique_cotaskmem_string title; CHECK_FAILURE(sender->get_DocumentTitle(&title)); m_strTitle = title.get(); auto callback = m_callbacks[CallbackType::TitleChanged]; if (callback != nullptr) RunAsync(callback); return S_OK; }) .Get(), &m_documentTitleChangedToken));
What we do here, is retrieving the title of the document and storing it in the class. Then, if a callback was set for this event we invoke it. We can modify the creation of the web view and install a callback for this event so that every time a page is loaded and the title changes we update the title of the main window of the application.
void CMfcEdgeDemoView::OnInitialUpdate() { CView::OnInitialUpdate(); this->ModifyStyleEx(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE, 0, 0); this->ModifyStyle(WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME | WS_BORDER, 0, 0); m_pWebBrowser = std::make_unique<CWebBrowser>(); if (m_pWebBrowser != nullptr) { CRect rectClient; GetClientRect(rectClient); m_pWebBrowser->CreateAsync( WS_VISIBLE | WS_CHILD, rectClient, this, 1, [this]() { m_pWebBrowser->SetParentView(this); m_pWebBrowser->DisablePopups(); m_pWebBrowser->Navigate(L"https://bing.com", nullptr); m_pWebBrowser->RegisterCallback(CWebBrowser::CallbackType::TitleChanged, [this]() { CString title = m_pWebBrowser->GetTitle(); if (GetDocument() != nullptr) { GetDocument()->SetTitle(title); } AfxGetMainWnd()->SetWindowText(title); }); }); } }
List of events
Currently, the following events can be handled.
Event | Description | Add/Remove handlers |
---|---|---|
AcceleratorKeyPressed | Fires when an accelerator key or key combo is pressed or released while the WebView is focused | add_AcceleratorKeyPressed remove_AcceleratorKeyPressed |
ContainsFullScreenElementChanged | An HTML element inside the WebView is entering fullscreen to the size of the WebView or leaving fullscreen | add_ContainsFullScreenElementChanged remove_ContainsFullScreenElementChanged |
ContentLoading | Fires before any content is loaded, including scripts added with AddScriptToExecuteOnDocumentCreated | add_ContentLoading remove_ContentLoading |
DocumentTitleChanged | Fires when the DocumentTitle property of the WebView changes. | add_DocumentTitleChanged remove_DocumentTitleChanged |
FrameNavigationCompleted | Fires when a child frame has completely loaded (body.onload has fired) or loading stopped with error. | add_FrameNavigationCompleted remove_FrameNavigationCompleted |
FrameNavigationStarting | Fires when a child frame in the WebView requesting permission to navigate to a different URI. | add_FrameNavigationStarting remove_FrameNavigationStarting |
GotFocus | Fires when WebView got focus. | add_GotFocus remove_GotFocus |
LostFocus | Fires when WebView lost focus. | add_LostFocus remove_LostFocus |
MoveFocusRequested | Fires when the user tries to tab out of the WebView. | add_MoveFocusRequested remove_MoveFocusRequested |
NavigationCompleted | Fires when the WebView has completely loaded (body.onload has fired) or loading stopped with error. | add_NavigationCompleted remove_NavigationCompleted |
NavigationStarting | Fires when the WebView main frame is requesting permission to navigate to a different URI | add_NavigationStarting remove_NavigationStarting |
NewWindowRequested | Fires when content inside the WebView requested to open a new window, such as through window.open. | add_NewWindowRequested remove_NewWindowRequested |
PermissionRequested | Fires when content in a WebView requests permission to access some privileged resources. | add_PermissionRequested remove_PermissionRequested |
ProcessFailed | Fires when a WebView process terminated unexpectedly or become unresponsive. | add_ProcessFailed remove_ProcessFailed |
HistoryChange | Listen to the change of navigation history for the top level document. | add_HistoryChanged remove_HistoryChanged |
ScriptDialogOpening | Fires when a JavaScript dialog (alert, confirm, or prompt) will show for the webview. | add_ScriptDialogOpening remove_ScriptDialogOpening |
SourceChanged | Fires when the Source property changes. | add_SourceChanged remove_SourceChanged |
WebMessageReceived | Fires when the IsWebMessageEnabled setting is set and the top level document of the webview calls window.chrome.webview.postMessage. | add_WebMessageReceived remove_WebMessageReceived |
WebResourceRequested | Fires when the WebView is performing an HTTP request to a matching URL and resource context filter that was added with AddWebResourceRequestedFilter. | add_WebResourceRequested remove_WebResourceRequested |
WindowCloseRequested | Fires when content inside the WebView requested to close the window, such as after window.close is called. | add_WindowCloseRequested remove_WindowCloseRequested |
ZoomFactorChanged | Fires when the ZoomFactor property of the WebView changes, either because the caller modified the ZoomFactor property, or due to the user manually modifying the zoom. | add_ZoomFactorChanged remove_ZoomFactorChanged |
Try the app
You can download, build, and try the sample app for this series from here: MfcEdgeDemo.zip (24793 downloads ) .
hi mariusbancila, This is good I’ve tested and working fine after made simple change.
replaced code as => is_aggregate : public integral_constant {}; with is_aggregate : public integral_constant {};
Can you please help me to create simple edge webview2 win32 in C++ with windows desktop Dynamic link library.
Need to use this dll in VB6 to show the url.
thank you for your explanation,
i’am inspired from some ideas e.g. discover installed versions.
what i’am stronly missing is:
How To load resources from memory using: ICoreWebView2WebResourceRequestedEventHandler
or in general, How to provide a scheme handler like IInternetProtocolRoot from legacy IE
Are you motivated to show us that in part 4
Hello, I am using the below function to get the document in Internet Explorer, now I wish to convert the same function to find the document in Edge Browser. So kindly help me, thanks in advance.
bool TestID(CString csID, MSHTML::IHTMLElementPtr &htmlElement)
{
IWebBrowser2Ptr pWebBrowser2Ptr;
IDispatchPtr pDisp;
pWebBrowser2Ptr->get_Document(&pDisp);
if(pDisp)
{
MSHTML::IHTMLDocument2Ptr htmlDoc(pDisp);
if(htmlDoc)
{
MSHTML::IHTMLElementCollectionPtr htmlElementsPtr=htmlDoc->Getall();
if(htmlElementsPtr)
{
_variant_t var(csID);
htmlElement=htmlElementsPtr->item(var);
if(htmlElement)
return true;
}
}
}
return false
}
Hi Mariusbancila
Many thanks for the articles on WebView2 they are certainly a lot more helpful than the other documentation I have seen, is it okay to use the EdgeWebBrowser class in our own applications or as a basis for implementations.
I didn’t see any license information in the article or in the download.
Many thanks
Glad to hear you found the article useful. You are free to use the code in any way you want. Just attribute it, as per https://creativecommons.org/licenses/by/4.0/.
Can you please explain how we can correctly support the NewWindowRequested event handler using the CWebBrowser class? I can’t quite understand how to support it compared to the official samples. I am very grateful of an explanation of handling the NewWindowRequested event.