c# - 从Windows Store应用程序中的WebView打印静态内容

标签 c# printing webview windows-store-apps brush

更新3

现在有一个悬赏这个问题。示例项目和问题摘要在页面底部的TL; DR;下。因此,请保存自己的阅读内容并跳到最后!

...

我一直在尝试通过JerryNixon通过NavigateToString使用静态加载的页面从JerryNixon获得这个非常详细,高评价的答案。我不明白为什么这行不通,但是坦白地说,代码的某些部分绝对让我感到困惑。

最初的答案是here,但出于完整性考虑,我将在此处复制代码。 Jerry为想要从WebView打印的人提供以下示例/解决方案

<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;
}


我唯一的更改是从Webview中删除Source属性,并将此行添加到MainPage()方法中

var html = await Windows.Storage.PathIO.ReadTextAsync("ms-appx:///Assets/sherlock.html");
MyWebView.NavigateToString(html);


Sherlock.html是亚瑟·柯南道尔(Arthur Conan Doyle)的故事“红发联盟”的节选,该HTML页面的完整代码为here-请注意,末尾的一段重复了几次。这是在测试中完成的,我将在下面解释

最初,此样本对我有用,但文件很小。 (Sherlock.html,无填充章节)

但是,当我超过某个文件大小(通常在4000个单词左右)时,样本就会发生以下行为。

WebView缩小到非常狭窄的视图

(请参阅更新说明,以了解为何会被删除)

然后屏幕变灰。 (发布灰色屏幕的屏幕截图有点愚蠢,但我想在此进行详细介绍)



该应用程序不会崩溃。没有调试消息出现,没有异常发生。就像Grid只是被删除一样。 。这不是启动屏幕(我将其设置为蓝色),也不是应用程序背景(较暗的灰色)。好像在页面顶部加载了其他内容。

我尝试调整代码的每个部分,但似乎无法找出导致问题的原因。此外,这段代码的一部分只是让我感到困惑。例如,在Load_Completed方法中

MyWebViewRectangle.Fill = GetWebViewBrush(MyWebView);
MyPrintPages.ItemsSource = GetWebPages(MyWebView, new Windows.Foundation.Size(100d, 150d));
MyWebView.Visibility = Windows.UI.Xaml.Visibility.Visible;


第1行:MyWebViewRectangle充满了WebView的快照。为什么?永远不会再引用此控件。我想可能是为了让用户获得页面的更清晰视图,但它似乎没有任何编程目的

第3行:MyWebView.Visibility设置为Visible稍后在GetWebViewBrush中再次发生。

var _OriginalVisibilty = webView.Visibility;
webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
// and a few lines later... 
webView.Visibility = _OriginalVisibilty;


据我所知,webview从来都不是Collapsed,而是设置为Visible的三倍。

如果有人可以向我解释这一切(也许甚至是JerryNixon本人),我也将不胜感激。我花了几个月的时间来开发应用程序,这是最后的难题。如果没有打印功能,我将无法启动!

更新

今天早上做了一些发现。我一直在XAML中设置WebView的宽度。没有这个,即使最小的文件也会失败。基于此发现,我将html文件本身的宽度设置为800px,并成功了。万岁,问题解决了吧?很不幸的是,不行。我再次尝试使用更长的文件(《红发联盟》的完整版本,here),并且相同的问题再次发生。现在变得很奇怪。

我在GetWebPages方法的webView.Height = _ContentHeight;处插入了一个断点。这表明_ContentHeight恰好是“ 13241”。

但是,插入断点会使代码正常工作。一旦我突破并继续,一切似乎都加载了。再次奇怪的是,在我将鼠标悬停在滚动条上之前,WebView是不可见的。

如果删除断点,则会再次显示灰屏。

如果我手动将_ContentHeight设置为13230,则会加载页面。这似乎是神奇的数字。高于此值的页面将失败。但是,如果我将html的宽度更改为小于800px,它也会失败。因此,从逻辑上讲,存在某种神奇的比率,即800:13230像素(或1:16.5375)。如果我超过这个比例,我将引发死亡的灰屏。我可以禁用红色矩形并将GetWebPages限制为3页,并且灰色屏幕仍然存在。这告诉我问题出在WebView本身

如果设置断点,该问题将消失。

还要注意在工作时MyWebViewRectangle中的多余空格和MyPrintPages中的多余空白页:



注意:我还添加了NateDiamond对事件LongRunningScriptDetectedUnsafeContentWarningDisplayingUnviewableContentIdentified的建议

这是我的示例项目的完整资源:
https://onedrive.live.com/redir?resid=EE99EF9560A6740E!110509&authkey=!ACrZgm6uq5k5yck&ithint=file%2czip

更新2

取得一些成功。

我在示例文件中放入了一些标题(通过反复试验发现每个计算页面的“底部”)并扩大了页面大小,以便可以看到标记。尽管有9页,但仅呈现5页和6页的一小部分。 9页对象由GetWebPages创建,但是只有5ish呈现了HTML内容。其余都是空的



(请注意,我更改了背景颜色,因为蓝色的红色给了我偏头痛)

我遍历了代码并进行了一些修改,现在我完全不再显示灰屏了。但是,每运行3次,大约有2次,仅呈现第一页,并对其进行裁剪。这似乎是完全随机的。如所述,当呈现9页时,并非全部都有内容。

这是我的更改:

添加了新方法Resize_WebView()

public void ResizeWebView()
{
    OriginalHeight = MyWebView.Height;

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

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


MyWebView_LoadCompleted中的注释掉了矩形填充,添加了对ResizeWebView()的调用:

void MyWebView_LoadCompleted(object sender, NavigationEventArgs e)
{
    ResizeWebView();

    //MyWebViewRectangle.Fill = GetWebViewBrush(MyWebView);
    MyPrintPages.ItemsSource = GetWebPages(MyWebView, new Windows.Foundation.Size(300d, 450d));
}


GetMyWebPages中更改了画笔的加载位置:

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;

    // Manually set _ContentHeight: comment out the above block and uncomment this to test. 
    // int _ContentHeight = 13230;
    // webView.Height = _ContentHeight;*/

    // how many pages will there be?
    //var _Scale = page.Width / _ContentWidth;
    var _Scale = page.Width / webView.Width; // now sourced directly from webview
    //var _ScaledHeight = (_ContentHeight * _Scale); 
    var _ScaledHeight = (webView.Height * _Scale); // now sourced directly from webview
    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 },
        };
        var _Brush = GetWebViewBrush(webView);
        _Brush.Stretch = Stretch.UniformToFill;
        _Brush.AlignmentY = AlignmentY.Top;
        _Brush.Transform = _Page.Tag as TranslateTransform;
        _Page.Fill = _Brush;
        /*_Page.Loaded += async (s, e) =>
        {
            var _Rectangle = s as Windows.UI.Xaml.Shapes.Rectangle;
            var _Brush = await GetMyWebViewBrush(webView);
            _Brush.Stretch = Stretch.UniformToFill;
            _Brush.AlignmentY = AlignmentY.Top;
            _Brush.Transform = _Rectangle.Tag as TranslateTransform;
            _Rectangle.Fill = _Brush;
        };*/
        _Pages.Add(_Page);
    }
    return _Pages;
}


现在已从GetWebView中删除​​了不必要的调整大小代码

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;
}


这正在成为一个严重的史诗问题。

TL; DR;

这是更新的项目。
https://onedrive.live.com/redir?resid=EE99EF9560A6740E!110545&authkey=!AMcnB_0xKRhYHIc&ithint=file%2czip

加载并观察。文档中有9页。


GetWebPages偶尔仅加载一页,裁剪
其余时间,GetWebPages加载1-5页,6和3空白页的一部分


这几乎是您回答该问题所需的全部知识。

我悬赏这个问题。我正在寻找此问题的解决方案,并在可能的情况下寻找一种向每个页面添加填充以使文本不会出现在边缘的方法。

最佳答案

[更新]

它与这个新代码在完全相同的位置中断的事实非常奇怪。这表明WebViewBrush中不存在数据。如果将整个笔刷渲染到控件,它会渲染所有内容吗?如果我的理论正确,那么您应该最多只能看到第6页的截止。这开始闻起来像Surface Pro上的视频驱动程序错误。 Surface Pro使用Intel视频芯片组并使用共享的即系统内存。您应该有足够的能力来渲染单个画笔实例(共九页)。请记住,即使在更好的硬件上,这种方法仍可能会导致大量页面资源问题。

如果有机会,您可以尝试使用另一家制造商的视频卡将其侧面加载到另一台计算机上,以确认它确实可以工作吗?我将尝试挖掘Surface Pro 1,看看是否可以重现您的发现。

在您走这条路之前,我需要警告您有关XAML打印的已知资源陷阱。 XAML打印子系统要求您在将其呈现之前将所有页面传递给它。然后它将整个文档发送到打印机。这要求将每个页面都缓存为XAML,然后将每个页面呈现为位图。这一切都发生在主存储器中。您可能可以摆脱二十页左右的空间,但是一旦超过了二十页,您将很快耗尽系统资源。五十到一百页可能会由于内存压力而导致运行时关闭。在资源不足的设备上,您获得的页面将更少。

最初,我认为您的目标是打印HTML页面。下面的解决方案是完美的。但是,如果您的最终目标是允许用户从您的应用程序中打印大型文档,则真正的答案(也是Windows Store应用程序的唯一解决方案)是使用Direct2D和DirectWrite。这些技术使您可以将页面“流式传输”到打印机缓存。打印机缓存由磁盘支持,不会引起同样的内存压力问题(尽管理论上您仍然可以用尽磁盘空间)。不用说这是理想的解决方案,但确实需要大量的专业知识。您需要了解一些有关C ++ / CX以及Direct2D的知识,才能真正正确地对页面进行分页。

Direct2Dapp printing sample

我希望这有帮助,

詹姆士



我今天仔细研究了一下,我认为您遇到了资源限制。我无法在工作站类计算机上重现该问题,而我的同事也无法在他的工作站类笔记本电脑上重现该问题。我在您的代码中看到的是,您正在为每个页面创建一个新画笔。即使您要转换画笔,整个WebView仍会转换为每个页面的位图,这是一个非常大的位图。这对XAML丰富的合成器施加了资源限制。由于合成器基于D3D,因此您的视频卡上的纹理内存可能已用完。

我想我有一个解决方案给您。我不会撒谎,但它对我有用,我希望它对您有用。基本上,我是通过画笔将页面部分呈现为XAML中声明的隐藏矩形。然后,我使用RenderTargetBitmap渲染隐藏的矩形。这样的效果是仅呈现所需的WebView部分。然后,我将RenderTargetBitmap设置为Image的源。我将图像添加到页面集合。页面集设置为ItemsControl,鲍勃为您的叔叔。

大部分更改都在GetWebPages中:

async Task<bool> GetWebPages(WebView webView, Windows.Foundation.Size page)
{
    // how many pages will there be?
    var _Scale = page.Width / webView.Width; // now sourced directly from webview
    var _ScaledHeight = (webView.Height * _Scale); // now sourced directly from webview
    var _PageCount = (double)_ScaledHeight / page.Height;
    _PageCount = _PageCount + ((_PageCount > (int)_PageCount) ? 1 : 0);

    var _Brush = GetWebViewBrush(webView);

    for (int i = 0; i < (int)_PageCount; i++)
    {
        var _TranslateY = -page.Height * i;
        // You can probably optimize this bit
        _RenderSource.Height = page.Height;
        _RenderSource.Width = page.Width;
        Margin = new Thickness(5);

        _Brush.Stretch = Stretch.UniformToFill;
        _Brush.AlignmentY = AlignmentY.Top;
        _Brush.Transform = new TranslateTransform { Y = _TranslateY };
        _RenderSource.Fill = _Brush;

        RenderTargetBitmap rtb = new RenderTargetBitmap();

        await rtb.RenderAsync(_RenderSource);

        var _iPge = new Image
        {
            Source = rtb,
            Margin = new Thickness(10)
        };

        _Pages.Add(_iPge);
    }
    return true;
}


整个修改后的项目可以在这里找到:http://1drv.ms/1y91iv7

请尝试一下,让我知道它是否适合您。

附言:我没有尝试解决一些布局问题和分页问题。

我希望这有帮助,

詹姆士

关于c# - 从Windows Store应用程序中的WebView打印静态内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26109792/

相关文章:

c# - 单声道不支持 UDP 协议(protocol)?

c# - 防止对象在拖动开始时跳到中心点

Java无法处理π字符

javascript - 从打印页面禁用浏览器特定的页眉和页脚

android webview相关

iphone - 强制在 Safari 而不是应用内浏览器中打开链接

c# - 自定义 ValidationAttribute ErrorMessage 未按预期工作

c# - 如何使用 VB.net 或 C# 从 Excel 动态删除行并保存

java - 用 java 降序输出 Map.Entry<Sentence, Integer>

android - 我如何使用 Android 使用 webView 下载基于 session /cookie 的文件?