1

I'm currently using a WebView2 in my WinUI3 application to display some HTML which is sent from our server.

The HTML itself doesn't contains a body / html tags and is displayed through NavigateToString:

await web.EnsureCoreWebView2Async();
web.NavigationCompleted += async (sender, args) => await sender.ResizeToContent();  // more about this later
web.NavigateToString(someText);

When I display this HTML in my WebView, the WebView's height is always set at 0 by default, and I want my WebView to autosize to its content (I cannot set a fixed sized for its container and stretch the webview to it).

I tried executing scripts found there to evaluate the HTML's size: How to get height of entire document with JavaScript? :

public static async Task ResizeToContent(this WebView2 webView)
        {
            var script = "";
            var heightString = await webView.ExecuteScriptAsync(script);
            int height = 0;
            if (int.TryParse(heightString, out height))
            {
                webView.Height = height;
            }
        }

Here are 2 differents scripts I tried: eval(document.documentElement.scrollHeight.toString());

and

;(function() {
    var pageHeight = 0;

    function findHighestNode(nodesList) {
        for (var i = nodesList.length - 1; i >= 0; i--) {
            if (nodesList[i].scrollHeight && nodesList[i].clientHeight) {
                var elHeight = Math.max(nodesList[i].scrollHeight, nodesList[i].clientHeight);
                pageHeight = Math.max(elHeight, pageHeight);
            }
            if (nodesList[i].childNodes.length) findHighestNode(nodesList[i].childNodes);
        }
    }

    findHighestNode(document.documentElement.childNodes);
    return pageHeight;

})();

But in both cases, no mater the HTML provided, it always returns 1040 even with a simple HTML such as <p>test</p>

When I set a fixed height for my WebView, let's say of 60, this p is displayed correctly without scrollbar (while my script would return a height of 1040) BUT when I do some complex HTML intended to be bigger than those 60px, the webview displays a vertical scrollbar.

So all in one, it seems the WebView somehow knows that 1040 is not the real height (otherwise I'd have a scrollbar all the time).

Note that I've also tried to surround my text with <html><body>{myText}</body></html> with the same result.

How can I get the real actual content's height?

Thanks.

Thomas KiTe Trentin
  • 600
  • 1
  • 5
  • 19
  • What do you get if you just get the `scrollHeight` of the Document (after navigation is completed, i.e., the Document is rendered), e.g., `string docHeight = await webView.ExecuteScriptAsync("document.documentElement.scrollHeight");`? – Jimi Nov 24 '22 at 17:19
  • Always 1040. Any call to `scrollHeight` no matter the target result in 1040. I ended up with a solution I've put in a separate answer, but this really feel hacky. – Thomas KiTe Trentin Nov 24 '22 at 17:21
  • This is the result you get with or without body, or in both cases? I've tested the latest version of WebView2 and I get correct measures (with a body - content generated with JavaScript calls) – Jimi Nov 24 '22 at 17:23
  • I tried with body, and without body too. I'll check if I have the latest version – Thomas KiTe Trentin Nov 24 '22 at 17:25

2 Answers2

1

After trying other solutions, here is what I came up with which seems to work:

In my ViewModel:

Text = $"<div id='container'>{_source.Text}</div>";

And in my extension method to get the height:

        public static async Task ResizeToContent(this WebView2 webView)
        {
            var script = "eval(document.getElementById('container').getBoundingClientRect().height.toString());";
            var heightString = await webView.ExecuteScriptAsync(script);
            if (int.TryParse(heightString, out int height))
            {
                webView.Height = height + 30;
            }
        }

I had to add the +30 because otherwise the scrollbar was displayed and the webview's content slightly truncated.

Is there any cleaner and less hacky way to do this?

Thomas KiTe Trentin
  • 600
  • 1
  • 5
  • 19
  • The html document/root element size is set by the webview2 XAML control size (how can it be different?), so the fact you need to get a specific item's height (or set your own height) to compute the size you need in your case is logical. The +30 is indeed more hackish :-) – Simon Mourier Nov 24 '22 at 17:51
  • `The html document/root element size is set by the webview2 XAML control size (how can it be different?)` one could argue that the WebView should be autosized to its content, since that's the default behavior for most container controls in WinUI / UWP. Having to execute injected Javascript on either the html page or a given injected `div` is really hacky and shouldn't be the expected way to have this working – Thomas KiTe Trentin Nov 25 '22 at 14:32
  • By default, a browser (the user agent) has always the size of its main window, which is ultimately decided by the end-user, not by the size of its HTML content (nor any js inside). That's the same with a webview and as such with the XAML control. – Simon Mourier Nov 25 '22 at 15:36
  • On my machine, I only had to add 5, not 30. I'm not sure where that value is coming from though and would like to find the root cause. Here's a related issue: https://github.com/MicrosoftEdge/WebView2Feedback/issues/123 – Rami A. Apr 04 '23 at 23:33
0

I was able to figure out where the extra padding was coming from.

This worked for me:

CSS:

/* hide scrollbars in WebView2 */
body::-webkit-scrollbar {
  display: none;
}
public async Task ResizeToContentAsync()
{
    var script = "document.body.children[0].getBoundingClientRect().height;";
    var heightString = await webView.ExecuteScriptAsync(script);
    if (int.TryParse(heightString, out int height))
    {
        this.Height = height;
    }
}
Rami A.
  • 10,302
  • 4
  • 44
  • 87