如何禁用Webview滚动并设置其高度以适合整个内容

时间:2016-10-07 06:47:03

标签: javascript webview uwp

我正在使用webview来显示和编辑我的UWP应用程序中的内容。我想禁用Webview控件中的滚动并使其适应高度以适应它的整个内容。然后它应该尊重它的父控件的scrollviewer的滚动。我在OneDrive上传了一个示例应用程序:https://1drv.ms/u/s!AhChIerZubKRjQKBH5nA0KGTtPYP

在示例应用程序中,您可以看到,一旦到达滚动查看器的末尾,webview将开始在其自身内滚动。相反,我想做的是,继续滚动(父滚动浏览器)到webview的末尾。

编辑:这是代码:

MainPage.xaml中:

<ScrollViewer VerticalScrollBarVisibility="Auto" VerticalScrollMode="Auto" Height="200" Width="400">
        <StackPanel>
            <Button Content="Navigate web page 1"
                    Margin="0,20,0,0"
                    HorizontalAlignment="Center"
                    Click="Button_Click"/>

            <Button Content="Navigate web page 2"
                    Margin="0,20,0,0"
                    HorizontalAlignment="Center"
                    Click="Button_Click_1"/>

            <Button Content="Navigate web page 3"
                    Margin="0,20,0,0"
                    HorizontalAlignment="Center"
                    Click="Button_Click_2"/>

            <Button Content="Navigate web page 4"
                    Margin="0,20,0,0"
                    HorizontalAlignment="Center"
                    Click="Button_Click_3"/>
            <WebView Name="WebView" Width="400" MinHeight="200"></WebView>
        </StackPanel>
</ScrollViewer>

MainPage.xaml.cs中:

public sealed partial class MainPage : Page
    {
        private const string EditableParameter = "~editable~";
        private const string SetBodyEditableScript = @"
                try
                {  
                        document.body.contentEditable = '" + EditableParameter + @"';
                }
                catch(e)
                {

                }";

        private const string SetTextFromClipBoardFunctionName = "setTextFromClipBoard";
        private const string SetHtmlTextFromClipboardFunction =
                "function " + SetTextFromClipBoardFunctionName + @"(htmlFromClipboard) 
                {
                   if (window.getSelection) 
                    {       
                        var range;
                        var sel  = window.getSelection();
                        if (sel.rangeCount) {
                            range = sel.getRangeAt(0);
                            range.deleteContents();

                            var el = document.createElement('div');
                            el.innerHTML = htmlFromClipboard + '<span></span>';
                            var frag = document.createDocumentFragment(), node, lastNode;
                            while ( (node = el.firstChild) ) {
                                lastNode = frag.appendChild(node);
                            }
                            range.insertNode(frag);
                            range.collapse(false); 
                            window.external.notify('RefreshAndReportHtml');
                      }
                        else if (typeof document.selection != 'undefined' && document.selection.type != 'Control') {
                                var html = (node.nodeType == 1) ? node.outerHTML : node.data;
                                html += '<span></span>';
                                var textRange = document.selection.createRange();
                                textRange.pasteHTML(html);
                                textRange.collapse(false);
                                window.external.notify('RefreshAndReportHtml');
                      }
                    }     
                };";

        private const string GetHtmlFunctionName = "getHtml";
        private const string GetHtmlFunction =
                "function " + GetHtmlFunctionName + @"(skipParam) 
                {
                    return document.documentElement.innerHTML;
                };";

        public MainPage()
        {
            this.InitializeComponent();
            MakeWebviewEditable();
            this.NavigationCacheMode = NavigationCacheMode.Required;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //(Window.Current.Content as Frame).Navigate(typeof(WebviewPage), "http://tsn.ua");
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            //(Window.Current.Content as Frame).Navigate(typeof(WebviewPage), "http://buzzfeed.com");
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            //(Window.Current.Content as Frame).Navigate(typeof(WebviewPage), "http://microsoft.com");
        }

        private void Button_Click_3(object sender, RoutedEventArgs e)
        {
            //(Window.Current.Content as Frame).Navigate(typeof(WebviewPage), "http://cnn.com");
        }

        private const string EventNotificationFormat = @"window.external.notify('{0}');";
        private async void MakeWebviewEditable()
        {
            WebView.NavigateToString("I do not know how that about righting wrongs can be, said the bachelor, for from 707" +
                                    "straight you have made me crooked, leaving me with a broken leg that will never see itself straight again all the days of its life and the injury you have redressed in my case" +
                                    "has been to leave me injured in such a way that I shall remain injured for ever and the height of misadventure it was to fall in with you who go in search of adventures." +
                                    "I do not know how that about righting wrongs can be, said the bachelor, for from 707" +
                                    "straight you have made me crooked, leaving me with a broken leg that will never see itself straight again all the days of its life and the injury you have redressed in my case" +
                                    "has been to leave me injured in such a way that I shall remain injured for ever and the height of misadventure it was to fall in with you who go in search of adventures.");
            //await InjectJavaScriptAsync(SetBodyEditableScript.Replace(EditableParameter, "true"));
            await InjectJavaScriptAsync("document.designMode='on'");
            await InjectJavaScriptAsync(GetHtmlFunction);
        }

        private async Task InjectJavaScriptAsync(string jscript)
        {
            await WebView.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
            {
                try
                {
                    // Only execute JS if a document is fully loaded. This should eliminate JS exception related to UNKNOWN name errors.
                    //if (IsHtmlLoaded)
                    string result = await WebView.InvokeScriptAsync("eval", new string[] { jscript });
                }
                catch (Exception ex)
                {

                }
            });
        }
    }

可以吗?

2 个答案:

答案 0 :(得分:2)

要根据HTML内容自动调整WebView的大小,我们可以尝试使用以下代码:

private async void WebView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
    var webView = sender as WebView;

    int width;
    int height;

    // get the total width and height
    var widthString = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
    var heightString = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });

    if (!int.TryParse(widthString, out width))
    {
        throw new Exception("Unable to get page width");
    }
    if (!int.TryParse(heightString, out height))
    {
        throw new Exception("Unable to get page height");
    }

    // resize the webview to the content
    webView.Width = width;
    webView.Height = height;
}

然后我们可以在XAML中使用此方法,如:

<WebView Name="WebView" Width="400" MinHeight="200" NavigationCompleted="WebView_NavigationCompleted"/>

或代码隐藏:

public MainPage()
{
    this.InitializeComponent();
    MakeWebviewEditable();
    this.NavigationCacheMode = NavigationCacheMode.Required;

    WebView.NavigationCompleted += WebView_NavigationCompleted;
}

这里我使用了WebView.NavigationCompleted事件,因为在我们尝试获取内容的宽度和高度之前,我们需要确保当前内容已加载。

<强>更新

在Windows 10 Mobile上,上述方法可能无法完全正常工作。这是因为Windows 10 Mobile上的 WebView 与父控件不兼容,需要手势从WebView控件传播到父控件,例如FlipViewScrollViewer class以及其他相关控件。

WebView class备注

  

默认情况下, WebView 内容托管在桌面设备系列中的设备上的UI线程上,以及所有其他设备上的UI线程上。您可以使用WebView.DefaultExecutionMode静态属性来查询当前客户端的默认线程行为。如有必要,您可以使用WebView(WebViewExecutionMode)构造函数来覆盖此行为。

     
    

注意在移动设备上的UI线程上托管内容时可能会出现性能问题,因此请务必在更改DefaultExecutionMode时在所有目标设备上进行测试。

  
     

在UI线程中托管内容的 WebView 与需要手势从 WebView 控件传播到父级的父控件不兼容,例如{{ 3}},FlipView和其他相关控件。这些控件将无法接收在线程外 WebView 中启动的手势。

作为一种解决方法,通常我们可以尝试使用

之类的东西
WebView WebView = new WebView(WebViewExecutionMode.SameThread);

在代码隐藏中,而不是在XAML中创建Web浏览器来解决此问题。但是,正如注意所说,在移动设备上的UI线程上托管内容并不是一个好习惯,在我的测试中,这将导致HTML内容无法编辑。因此,对于您的问题,这可能不是一个可接受的解决方案。

我不确定你想要达到什么目标。根据您的要求,您可以尝试像@DecadeMoon所说的将按钮放入HTML页面。或者,您可以将HTML的内容转换为ScrollViewer。或者您可以更改布局,只需将WebView放在ScrollViewer之外。

答案 1 :(得分:1)

所以你说你希望WebView控件的高度等于它显示的网页的高度?我不认为这是可能的。我想WebView会采用某种形式的渲染优化,这样它就不需要渲染在视口中看不到的东西。如果网页的整个高度都是可见的,那么它必须呈现一切对性能而言非常糟糕的内容。

看起来您正试图通过注入javascript直接通过WebView来控制网页。你将很难做到这一点。如果没有办法在XAML中实现你想要的东西,那么也许你可以在WinJS中重写你的应用程序?

实现您想要的最简单方法是将按钮直接放在网页中,并在点击它们时通知主机,以便您采取行动。

<强> XAML

<Grid>
    <WebView x:Name="webView" ScriptNotify="webView_ScriptNotify" Height="300"/>
</Grid>

<强> CS

private void prepareWebViewContent()
{
    var html = @"
        <style>
            button {
                padding: 15px 20px;
                font-size: 40px;
                border: none;
                background-color: #ccc;
                font-family: inherit;
                display: block;
                margin: 10px auto;
            }

            button:active {
                background-color: #aaa;
            }
        </style>

        <button onclick=""window.external.notify('Navigate1')"">Navigate to web page 1</button>
        <button onclick=""window.external.notify('Navigate2')"">Navigate to web page 2</button>

        <p>
            I do not know how that about righting wrongs can be, said the bachelor, for from 707
            straight you have made me crooked, leaving me with a broken leg that will never see itself straight again all the days of its life and the injury you have redressed in my case
            has been to leave me injured in such a way that I shall remain injured for ever and the height of misadventure it was to fall in with you who go in search of adventures.
            I do not know how that about righting wrongs can be, said the bachelor, for from 707
            straight you have made me crooked, leaving me with a broken leg that will never see itself straight again all the days of its life and the injury you have redressed in my case
            has been to leave me injured in such a way that I shall remain injured for ever and the height of misadventure it was to fall in with you who go in search of adventures.
        </p>
    ";

    webView.NavigateToString(html);
}

private void webView_ScriptNotify(object sender, NotifyEventArgs e)
{
    switch (e.Value)
    {
        case "Navigate1":
            // Handle this case
            break;

        case "Navigate 2":
            // Handle this case
            break;
    }
}

Screenshot

这很简陋,但你明白了。