如何在Windows Store应用程序中打印WebView内容?

18

我有一个 Metro App ,并尝试打印 WebView 控件的内容。 我使用 MSDN 打印示例 作为参考来源。 我只需更改 printableArea 中的 XAML 如下:

    <RichTextBlock>
        <Paragraph>
            <InlineUIContainer>
                <WebView Width="800" Height="2000" Source="http://www.stackoverflow.com"/>
            </InlineUIContainer>
        </Paragraph>
    </RichTextBlock>

这部分功能可以正常运行。问题在于指定尺寸中的Visible区域被打印,即可滚动的区域不会被打印,并且在打印预览中也不显示为多个页面

我已经接近成功,但需要一点帮助才能使其按预期工作。

我在任何地方都没有找到解决这个具体问题的示例。

我甚至尝试了这里的解决方案:http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/5edcb239-7a5b-49f7-87e3-e5a253b809c4

我并不是第一个遇到相同或类似问题的人: http://social.msdn.microsoft.com/Search/en-US/?Refinement=112&query=print%20webview#refinementChanges=180&pageNumber=1&showMore=false

愿意给能解决这个问题的人100点赏金。希望提供详细说明、示例代码或模拟项目作为解决方案。


富文本块的高度是多少?它是否被设置为剪切?我想知道它是否打印了RTB/段落的高度而不是WebView的高度。 - Nate Diamond
2个回答

30
当然,这就是您需要的翻译。首先,您可以将WebView调整为实际内容大小,然后将其缩放回原始大小。这需要一个脚本调用和一个ScaleTransform。真的很简单。像这样:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <WebView x:Name="MyWebView" Source="http://www.stackoverflow.com" />
</Grid>

void MyWebView_LoadCompleted(object sender, NavigationEventArgs e)
{
    var _Original = MyWebView.RenderSize;

    // ask the content its width
    var _WidthString = MyWebView.InvokeScript("eval",
        new[] { "document.body.scrollWidth.toString()" });
    int _Width;
    if (!int.TryParse(_WidthString, out _Width))
        throw new Exception(string.Format("failure/width:{0}", _WidthString));

    // ask the content its height
    var _HeightString = MyWebView.InvokeScript("eval",
        new[] { "document.body.scrollHeight.toString()" });
    int _Height;
    if (!int.TryParse(_HeightString, out _Height))
        throw new Exception(string.Format("failure/height:{0}", _HeightString));

    // resize the webview to the content
    MyWebView.Width = _Width;
    MyWebView.Height = _Height;

    // scale the webview back to original height (can't do both height & width)
    var _Transform = (MyWebView.RenderTransform as ScaleTransform)
        ?? (MyWebView.RenderTransform = new ScaleTransform()) as ScaleTransform;
    var _Scale = _Original.Height / _Height;
    _Transform.ScaleX = _Transform.ScaleY = _Scale;
}

这将导致一个非常高而窄的WebView,如下所示:

enter image description here

但这不是你想要的。

即使你可以调整结果矩形的大小,使其不那么奇怪,但Windows 8中的打印合同要求您提供单个页面。它不会为您分页。因此,您真正需要的是逐页检索单个网站。

第一种方法是如何实现这一点的基础。但是,您需要根据Windows 8的打印任务传递给您的页面大小来修复矩形的大小。这将基于用户的打印机选择。例如,Letter与A4(在英国)。然后,使用刷子的Stretch属性,您可以确保它裁剪自己。然后,使用画刷的Transform属性,您可以在矩形内上下滑动,直到它显示您要打印的页面。

具体操作如下:

<Grid Background="Blue">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="995" />
        <ColumnDefinition Width="300" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <WebView Grid.Column="0" x:Name="MyWebView" Source="http://www.stackoverflow.com" HorizontalAlignment="Right" />
    <Rectangle Grid.Column="1" x:Name="MyWebViewRectangle" Fill="Red" />
    <ScrollViewer Grid.Column="2" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <ItemsControl x:Name="MyPrintPages" VerticalAlignment="Top" HorizontalAlignment="Left">
            <Rectangle Height="150" Width="100" Fill="White" Margin="5" />
            <Rectangle Height="150" Width="100" Fill="White" Margin="5" />
            <Rectangle Height="150" Width="100" Fill="White" Margin="5" />
            <Rectangle Height="150" Width="100" Fill="White" Margin="5" />
            <Rectangle Height="150" Width="100" Fill="White" Margin="5" />
        </ItemsControl>
    </ScrollViewer>
</Grid>

public MainPage()
{
    this.InitializeComponent();
    MyWebView.LoadCompleted += MyWebView_LoadCompleted;
}

void MyWebView_LoadCompleted(object sender, NavigationEventArgs e)
{
    MyWebViewRectangle.Fill = GetWebViewBrush(MyWebView);
    MyPrintPages.ItemsSource = GetWebPages(MyWebView, new Windows.Foundation.Size(100d, 150d));
    MyWebView.Visibility = Windows.UI.Xaml.Visibility.Visible;
}

WebViewBrush GetWebViewBrush(WebView webView)
{
    // resize width to content
    var _OriginalWidth = webView.Width;
    var _WidthString = webView.InvokeScript("eval",
        new[] { "document.body.scrollWidth.toString()" });
    int _ContentWidth;
    if (!int.TryParse(_WidthString, out _ContentWidth))
        throw new Exception(string.Format("failure/width:{0}", _WidthString));
    webView.Width = _ContentWidth;

    // resize height to content
    var _OriginalHeight = webView.Height;
    var _HeightString = webView.InvokeScript("eval",
        new[] { "document.body.scrollHeight.toString()" });
    int _ContentHeight;
    if (!int.TryParse(_HeightString, out _ContentHeight))
        throw new Exception(string.Format("failure/height:{0}", _HeightString));
    webView.Height = _ContentHeight;

    // create brush
    var _OriginalVisibilty = webView.Visibility;
    webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
    var _Brush = new WebViewBrush
    {
        SourceName = webView.Name,
        Stretch = Stretch.Uniform
    };
    _Brush.Redraw();

    // reset, return
    webView.Width = _OriginalWidth;
    webView.Height = _OriginalHeight;
    webView.Visibility = _OriginalVisibilty;
    return _Brush;
}

IEnumerable<FrameworkElement> GetWebPages(WebView webView, Windows.Foundation.Size page)
{
    // ask the content its width
    var _WidthString = webView.InvokeScript("eval",
        new[] { "document.body.scrollWidth.toString()" });
    int _ContentWidth;
    if (!int.TryParse(_WidthString, out _ContentWidth))
        throw new Exception(string.Format("failure/width:{0}", _WidthString));
    webView.Width = _ContentWidth;

    // ask the content its height
    var _HeightString = webView.InvokeScript("eval",
        new[] { "document.body.scrollHeight.toString()" });
    int _ContentHeight;
    if (!int.TryParse(_HeightString, out _ContentHeight))
        throw new Exception(string.Format("failure/height:{0}", _HeightString));
    webView.Height = _ContentHeight;

    // how many pages will there be?
    var _Scale = page.Width / _ContentWidth;
    var _ScaledHeight = (_ContentHeight * _Scale);
    var _PageCount = (double)_ScaledHeight / page.Height;
    _PageCount = _PageCount + ((_PageCount > (int)_PageCount) ? 1 : 0);

    // create the pages
    var _Pages = new List<Windows.UI.Xaml.Shapes.Rectangle>();
    for (int i = 0; i < (int)_PageCount; i++)
    {
        var _TranslateY = -page.Height * i;
        var _Page = new Windows.UI.Xaml.Shapes.Rectangle
        {
            Height = page.Height,
            Width = page.Width,
            Margin = new Thickness(5),
            Tag = new TranslateTransform { Y = _TranslateY },
        };
        _Page.Loaded += (s, e) =>
        {
            var _Rectangle = s as Windows.UI.Xaml.Shapes.Rectangle;
            var _Brush = GetWebViewBrush(webView);
            _Brush.Stretch = Stretch.UniformToFill;
            _Brush.AlignmentY = AlignmentY.Top;
            _Brush.Transform = _Rectangle.Tag as TranslateTransform;
            _Rectangle.Fill = _Brush;
        };
        _Pages.Add(_Page);
    }
    return _Pages;
}

所以,UI 将会像这样,左侧列是 WebView,第二列(中间)是类似于我们第一个解决方案的 all-in-one,第三列是一个重复器,显示准备打印的单个页面。

enter image description here

当然,神奇的真正在于 GetWebPages() 方法!我不介意说,它是一个通过 C# 和 Transform 的工作方式非常容易而简单的奇迹。

请注意,这不是完整的代码。是的,它为您分割了页面,但我无法确定您希望页面有多少边距。所需的微调很小,但我想提一下。这是您需要将 WebView 分割并准备好响应 paginate 事件的 Windows 8 打印任务的 98% 代码。然后逐个矩形传递给它。

话虽如此,这可能是互联网上这个问题的最全面的解决方案。 :)

祝你好运!!


1
谢谢您,Nixon先生!!! 您的建议恰到好处。我更新了您的答案,并包含了边距的代码。万分感谢。您真是我的救星。我的应用现在已经准备好进入应用商店了 :) - c0D3l0g1c
1
非常流畅 - 网络上最好的 :) - Adam Tuliper
我在使用NavigateToString静态加载内容时遇到了困难。这段代码可以处理大约4000个单词的文件,但如果稍微增加一些内容,它就无法正常工作。WebView会缩小,ItemsControl为空,在大约4秒后屏幕变灰。没有任何异常或错误。有什么想法吗? - roryok
@Jerry Nixon - MSFT,这行代码 webView.InvokeScript("eval", new[] {"document.body.scrollWidth.toString()" }); 的现代等效方法是什么?我尝试将其转换为 InvokeScriptAsync 但是由于 无法将 'Windows.Foundation.IAsyncOperation<string>' 转换为 'string',它会破坏 _WidthString。 - b85411
嗨,@Jerry Nixon - MSFT,打印质量不好。我认为问题在于每英寸像素。我正在尝试打印发票。我已经尝试了许多参数,但没有运气。你有什么建议吗? - Code OverFlow
1
这在2018年对于Xamarin.Forms UWP相关吗?我正在开发一个应用程序,并且遇到了从WebView打印多个页面的类似挑战。但我不确定这段代码是否适用于Xamarin.Forms UWP。此外,我使用C#代码而非XAML。 - jbyrd

-1
这是基本的打印语法。
PrintManager.PrintTaskRequested += printManager_PrintTaskRequested;
void printManager_PrintTaskRequested(
    Windows.Graphics.Printing.PrintManager sender,
    Windows.Graphics.Printing.PrintTaskRequestedEventArgs args)
    {
        ...
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接