Как распечатать содержимое WebView в приложении Windows Store?

У меня есть приложение Metro > , и я пытаюсь напечатать содержимое элемента управления WebView. Использование Пример печати MSDN в качестве исходной ссылки. Я просто изменяю XAML в printableArea следующим образом:

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

Это работает частично. Проблема в том, что область Visible в указанных размерах печатается, т.е. Область, которую можно прокрутить, не печатает, а также не отображается как несколько страниц в PrintPreview.

Я почти там, буду благодарен за помощь, чтобы это работало, как ожидалось.

Я нигде не нашел образцов, которые решают эту проблему.

Я даже попробовал решения здесь: 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-процентную награду. Почитал бы пошаговое руководство, образец кода или макет проекта в качестве решения.

Ответы

Ответ 1

Конечно, здесь вы идете.

Во-первых, вы можете изменить размер WebView на фактический контент. Затем вы масштабируете WebView обратно до исходного размера. Для этого потребуется script invoke и a 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 to 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;
}

Таким образом, пользовательский интерфейс будет чем-то вроде этого, где левым столбцом является WebView, второй столбец (средний) - это все-в-одном, как наше первое решение, а третий - ретранслятор, показывающий отдельные страницы, готовые печатать.

enter image description here

Конечно, волшебство действительно в методе GetWebPages()! Я не против сказать, что это простое чудо, сделанное довольно легко, так как работают С# и Transforms.

Обратите внимание: не завершен. Да, он разбивает страницу для вас, но я не могу быть уверен, сколько маржи вы хотите на своей странице. Поэтому требуемая настройка крошечная, но я хотел бы упомянуть об этом. Это 98% кода, который вам нужен, чтобы разбить WebView и подготовить, если для задачи печати Windows 8 в ответ на событие paginate. Затем пропустите прямоугольники к нему по одному.

Сказав это, это, вероятно, наиболее полное решение этой проблемы в Интернете.:)

Желаем удачи!!

Ответ 2

Вот основной синтаксис печати. ​​

PrintManager.PrintTaskRequested += printManager_PrintTaskRequested;
void printManager_PrintTaskRequested(
    Windows.Graphics.Printing.PrintManager sender,
    Windows.Graphics.Printing.PrintTaskRequestedEventArgs args)
    {
        ...
    }