Распечатать страницу, содержащую ListBox или GridView

Страница печати содержит RichTextBlock. Этот RichTextBlock раньше содержал текст и может использовать следующий код для печати. Если страница содержит ListBox или GridView, мне нужна помощь в том, как это распечатать. Можно ли использовать RichTextBlock для содержания ListBox и обнаружения переполнения для продолжения печати? Был бы признателен, если бы вы могли предоставить образец, ссылку или учебное пособие по этому поводу. Спасибо

--- Обновление
Этот список будет содержать такие данные, как:

ItemName ItemCode Цена Кол-во ..

------
------

---- Обновление (2)

Как добавить верхний и нижний колонтитулы?

Заголовок

Заказчик
Адрес
Дата:
Номер заказа

Бренд-код Цена Кол-во



PrintDocument document = null;
IPrintDocumentSource source = null;
List pages = null;
FrameworkElement page1;
protected event EventHandler pagesCreated;
protected const double left = 0.075;
protected const double top = 0.03;



protected override void OnNavigatedTo(NavigationEventArgs e)
 {

    //--- Register to print 

    document = new PrintDocument();
    source = document.DocumentSource;

    //--- handle Document's Events 
    document.Paginate += printDocument_Paginate;
    document.GetPreviewPage += printDocument_GetPreviewPage;
    document.AddPages += printDocument_AddPages;


    PrintManager manager = PrintManager.GetForCurrentView();
    manager.PrintTaskRequested += manager_PrintTaskRequested;


    pages = new List();

    PrepareContent();


 }


person MilkBottle    schedule 04.09.2013    source источник
comment
Можете ли вы показать, какой контент находится в списке? Каким будет ваш ожидаемый результат печати?   -  person Farhan Ghumra    schedule 04.09.2013
comment
Этот ListBox просто содержит некоторые детали транзакции. Мне нужно распечатать его, как указано выше. Спасибо   -  person MilkBottle    schedule 04.09.2013
comment
Не могли бы вы показать мне снимок экрана со списком? Каким будет ваш ожидаемый результат печати с форматированием или без него?   -  person Farhan Ghumra    schedule 04.09.2013
comment
Я не могу распечатать это, потому что не знаю, как реализовать логику. Это просто страница XAML, содержащая ListBox, который будет отображать построчно для этих элементов в ListBox DataTemplate: ItemName, ItemCode, Price, Qty. Могу ли я печатать как с форматированием, так и без него? Большое спасибо за помощь :)   -  person MilkBottle    schedule 04.09.2013


Ответы (1)


Мой ответ основан на моем вспомогательном классе для печати текста текстового поля. Я настроил этот образец для поддержки списка. Ознакомьтесь с приведенным ниже кодом.

XAML

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <Canvas x:Name="printCanvas" Opacity="0" />
    <StackPanel Margin="100">
        <ListBox Height="500" Width="400" x:Name="MyLbx">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock FontSize="20">
                        <Run Text="Item name : "/> <Run Text="{Binding ItemName}" />
                    </TextBlock>
                    <TextBlock FontSize="20">
                        <Run Text="Item code : "/> <Run Text="{Binding ItemCode}" />
                    </TextBlock>
                    <TextBlock FontSize="20">
                        <Run Text="Price : "/> <Run Text="{Binding Price}" />
                    </TextBlock>
                    <TextBlock FontSize="20">
                        <Run Text="Quantity : "/> <Run Text="{Binding Quantity}" />
                    </TextBlock>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
        <Button Content="Print Listbox" Click="btnPrint_Click" HorizontalAlignment="Center" Margin="0,20,0,0"/>
    </StackPanel>
</Grid>

C#

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    MyLbx.ItemsSource = new List<ItemModel> 
    {
        new ItemModel("Item name 8", "Item code 10", 180.76, 13),
        new ItemModel("Item name 19", "Item code 16", 153.68, 14),
        new ItemModel("Item name 8", "Item code 18", 195.71, 10),
        new ItemModel("Item name 11", "Item code 16", 112.71, 14),
        new ItemModel("Item name 5", "Item code 11", 156.09, 18),
        new ItemModel("Item name 7", "Item code 8", 124.38, 18),
        new ItemModel("Item name 16", "Item code 16", 129.31, 15),
        new ItemModel("Item name 13", "Item code 5", 187.55, 19),
        new ItemModel("Item name 6", "Item code 6", 146.10, 14),
        new ItemModel("Item name 19", "Item code 5", 154.84, 18),
        new ItemModel("Item name 15", "Item code 5", 149.69, 18),
        new ItemModel("Item name 17", "Item code 5", 155.39, 6),
        new ItemModel("Item name 6", "Item code 14", 119.19, 12),
        new ItemModel("Item name 14", "Item code 19", 186.40, 19),
        new ItemModel("Item name 7", "Item code 18", 178.30, 8),
        new ItemModel("Item name 12", "Item code 17", 105.60, 8),
        new ItemModel("Item name 5", "Item code 19", 120.94, 9),
        new ItemModel("Item name 9", "Item code 12", 164.13, 19),
        new ItemModel("Item name 18", "Item code 15", 119.24, 7),
        new ItemModel("Item name 16", "Item code 10", 106.30, 16)
    };
}

private async void btnPrint_Click(object sender, RoutedEventArgs e)
{
    string PrintText = "";
    foreach (ItemModel item in MyLbx.Items)
    {
        PrintText += "Item name : " + item.ItemName + "\n";
        PrintText += "Item code : " + item.ItemCode + "\n";
        PrintText += "Price : " + item.Price + "\n";
        PrintText += "Quantity : " + item.Quantity + "\n\n";
    }

    await PrintHelper.ShowPrintUIAsync(printCanvas, PrintText, "PrintedListBox.pdf");
}

PrintHelper.cs

public class PrintHelper
{
    private static Canvas PrintingRoot { get; set; }

    private static string TextToBePrint { get; set; }

    private static string PrintingFileName { get; set; }

    internal static int currentPreviewPage;

    private static PrintDocument printDocument = null;

    private static IPrintDocumentSource printDocumentSource = null;

    internal static List<PageLoadState> printPreviewPages = new List<PageLoadState>();

    private const double ApplicationContentMarginLeft = 0.075;

    private const double ApplicationContentMarginTop = 0.03;

    private static bool ShowText
    {
        get { return ((int)imageText & (int)DisplayContent.Text) == (int)DisplayContent.Text; }
    }

    internal static DisplayContent imageText = DisplayContent.TextAndImages;

    private static void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e)
    {
        PrintTask printTask = e.Request.CreatePrintTask(PrintingFileName, sourceRequested => sourceRequested.SetSource(printDocumentSource));
    }

    private static void RegisterForPrinting()
    {
        // Create the PrintDocument.
        printDocument = new PrintDocument();

        // Save the DocumentSource.
        printDocumentSource = printDocument.DocumentSource;

        // Add an event handler which creates preview pages.
        printDocument.Paginate += CreatePrintPreviewPages;

        // Add an event handler which provides a specified preview page.
        printDocument.GetPreviewPage += GetPrintPreviewPage;

        // Add an event handler which provides all final print pages.
        printDocument.AddPages += AddPrintPages;


        // Create a PrintManager and add a handler for printing initialization.
        PrintManager printMan = PrintManager.GetForCurrentView();
        printMan.PrintTaskRequested += PrintTaskRequested;
    }

    private static void UnregisterForPrinting()
    {
        // Set the instance of the PrintDocument to null.
        printDocument = null;

        // Remove the handler for printing initialization.
        PrintManager printMan = PrintManager.GetForCurrentView();
        printMan.PrintTaskRequested -= PrintTaskRequested;
    }

    private static event EventHandler pagesCreated;

    private static void CreatePrintPreviewPages(object sender, PaginateEventArgs e)
    {
        // Clear the cache of preview pages 
        printPreviewPages.Clear();

        // Clear the printing root of preview pages
        PrintingRoot.Children.Clear();

        // This variable keeps track of the last RichTextBlockOverflow element that was added to a page which will be printed
        RichTextBlockOverflow lastRTBOOnPage;

        // Get the PrintTaskOptions
        PrintTaskOptions printingOptions = ((PrintTaskOptions)e.PrintTaskOptions);

        // Get the page description to deterimine how big the page is
        PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);

        // We know there is at least one page to be printed. passing null as the first parameter to
        // AddOnePrintPreviewPage tells the function to add the first page.
        lastRTBOOnPage = AddOnePrintPreviewPage(null, pageDescription);

        // We know there are more pages to be added as long as the last RichTextBoxOverflow added to a print preview
        // page has extra content
        while (lastRTBOOnPage.HasOverflowContent)
        {
            lastRTBOOnPage = AddOnePrintPreviewPage(lastRTBOOnPage, pageDescription);
        }

        if (pagesCreated != null)
            pagesCreated.Invoke(printPreviewPages, null);

        // Report the number of preview pages created
        printDocument.SetPreviewPageCount(printPreviewPages.Count, PreviewPageCountType.Intermediate);
    }

    private static void GetPrintPreviewPage(object sender, GetPreviewPageEventArgs e)
    {
        Interlocked.Exchange(ref currentPreviewPage, e.PageNumber - 1);

        PageLoadState pageLoadState = printPreviewPages[e.PageNumber - 1];

        if (!pageLoadState.Ready)
        {
            // Notify the user that some content is not available yet
            // Apps may also opt to don't show preview untill everything is complete and just use await IsReadyAsync
            //rootPage.NotifyUser("Image loading not complete, previewing only text", NotifyType.ErrorMessage);
        }

        // Set the preview even if images failed to load properly
        printDocument.SetPreviewPage(e.PageNumber, pageLoadState.Page);
    }

    private static void AddPrintPages(object sender, AddPagesEventArgs e)
    {
        // Loop over all of the preview pages and add each one to  add each page to be printied
        for (int i = 0; i < printPreviewPages.Count; i++)
        {
            // We should have all pages ready at this point...
            printDocument.AddPage(printPreviewPages[i].Page);
        }

        // Indicate that all of the print pages have been provided
        printDocument.AddPagesComplete();
    }

    private static RichTextBlockOverflow AddOnePrintPreviewPage(RichTextBlockOverflow lastRTBOAdded, PrintPageDescription printPageDescription)
    {
        // Create a cavase which represents the page 
        Canvas page = new Canvas();
        page.Width = printPageDescription.PageSize.Width;
        page.Height = printPageDescription.PageSize.Height;

        PageLoadState pageState = new PageLoadState(page, printPreviewPages.Count);
        pageState.ReadyAction = async (pageNumber, currentPage) =>
        {
            // Ignore if this is not the current page
            if (Interlocked.CompareExchange(ref currentPreviewPage, currentPreviewPage, pageNumber) == pageNumber)
            {
                await Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    //await new Windows.UI.Popups.MessageDialog("Content loaded").ShowAsync();
                    printDocument.SetPreviewPage(pageNumber + 1, currentPage);
                });
            }
        };

        // Create a grid which contains the actual content to be printed
        Grid content = new Grid();

        // Get the margins size
        // If the ImageableRect is smaller than the app provided margins use the ImageableRect
        double marginWidth = Math.Max(printPageDescription.PageSize.Width - printPageDescription.ImageableRect.Width,
                                    printPageDescription.PageSize.Width * ApplicationContentMarginLeft * 2);

        double marginHeight = Math.Max(printPageDescription.PageSize.Height - printPageDescription.ImageableRect.Height,
                                        printPageDescription.PageSize.Height * ApplicationContentMarginTop * 2);

        // Set content size based on the given margins
        content.Width = printPageDescription.PageSize.Width - marginWidth;
        content.Height = printPageDescription.PageSize.Height - marginHeight;

        // Set content margins
        content.SetValue(Canvas.LeftProperty, marginWidth / 2);
        content.SetValue(Canvas.TopProperty, marginHeight / 2);

        // Add the RowDefinitions to the Grid which is a content to be printed
        RowDefinition rowDef = new RowDefinition();

        rowDef.Height = new GridLength(2.5, GridUnitType.Star);
        content.RowDefinitions.Add(rowDef);
        rowDef = new RowDefinition();
        rowDef.Height = new GridLength(3.5, GridUnitType.Star);
        content.RowDefinitions.Add(rowDef);
        rowDef = new RowDefinition();
        rowDef.Height = new GridLength(1.5, GridUnitType.Star);
        content.RowDefinitions.Add(rowDef);

        // If lastRTBOAdded is null then we know we are creating the first page. 
        bool isFirstPage = lastRTBOAdded == null;

        FrameworkElement previousLTCOnPage = null;
        RichTextBlockOverflow rtbo = new RichTextBlockOverflow();
        // Create the linked containers and and add them to the content grid
        if (isFirstPage)
        {
            // The first linked container in a chain of linked containers is is always a RichTextBlock
            RichTextBlock rtbl = new RichTextBlock();
            rtbl.SetValue(Grid.RowProperty, 0);
            rtbl = AddContentToRTBl(rtbl);
            int a = rtbl.Blocks.Count();
            rtbl.Foreground = new SolidColorBrush(Windows.UI.Colors.Black);
            content.Children.Add(rtbl);

            // Save the RichTextBlock as the last linked container added to this page
            previousLTCOnPage = rtbl;
        }
        else
        {
            // This is not the first page so the first element on this page has to be a
            // RichTextBoxOverflow that links to the last RichTextBlockOverflow added to
            // the previous page.
            rtbo = new RichTextBlockOverflow();
            rtbo.SetValue(Grid.RowProperty, 0);
            content.Children.Add(rtbo);

            // Keep text flowing from the previous page to this page by setting the linked text container just
            // created (rtbo) as the OverflowContentTarget for the last linked text container from the previous page 
            lastRTBOAdded.OverflowContentTarget = rtbo;

            // Save the RichTextBlockOverflow as the last linked container added to this page
            previousLTCOnPage = rtbo;
        }

        if (ShowText)
        {
            // Create the next linked text container for on this page.
            rtbo = new RichTextBlockOverflow();
            rtbo.SetValue(Grid.RowProperty, 1);

            // Add the RichTextBlockOverflow to the content to be printed.
            content.Children.Add(rtbo);

            // Add the new RichTextBlockOverflow to the chain of linked text containers. To do this we much check
            // to see if the previous container is a RichTextBlock or RichTextBlockOverflow.
            if (previousLTCOnPage is RichTextBlock)
                ((RichTextBlock)previousLTCOnPage).OverflowContentTarget = rtbo;
            else
                ((RichTextBlockOverflow)previousLTCOnPage).OverflowContentTarget = rtbo;

            // Save the last linked text container added to the chain
            previousLTCOnPage = rtbo;

            // Create the next linked text container for on this page.
            rtbo = new RichTextBlockOverflow();
            rtbo.SetValue(Grid.RowProperty, 2);
            content.Children.Add(rtbo);

            // Add the new RichTextBlockOverflow to the chain of linked text containers. We don't have to check
            // the type of the previous linked container this time because we know it's a RichTextBlockOverflow element
            ((RichTextBlockOverflow)previousLTCOnPage).OverflowContentTarget = rtbo;
        }
        // We are done creating the content for this page. Add it to the Canvas which represents the page
        page.Children.Add(content);

        // Add the newley created page to the printing root which is part of the visual tree and force it to go
        // through layout so that the linked containers correctly distribute the content inside them.
        PrintingRoot.Children.Add(page);
        PrintingRoot.InvalidateMeasure();
        PrintingRoot.UpdateLayout();

        // Add the newley created page to the list of pages
        printPreviewPages.Add(pageState);

        // Return the last linked container added to the page
        return rtbo;
    }

    private static RichTextBlock AddContentToRTBl(RichTextBlock rtbl)
    {
        // Create a Run and give it content
        Run run = new Run();
        run.Text = TextToBePrint;

        // Create a paragraph, set it's property according to text box.
        Paragraph para = new Paragraph();
        para.FontSize = 20;

        para.Inlines.Add(run);
        // Add the paragraph to the blocks collection of the RichTextBlock
        rtbl.Blocks.Add(para);
        return rtbl;
    }

    public static async Task ShowPrintUIAsync(Canvas PrintingCanvas, string textToBePrint, string FileName)
    {
        if (PrintingCanvas == null)
            throw new NullReferenceException("Canvas must be present in XAML. It is for showing print preview.");

        if (string.IsNullOrWhiteSpace(FileName))
            throw new ArgumentException("The file name for the document which is going to be print must not be empty or only white space.");

        PrintingRoot = PrintingCanvas;
        TextToBePrint = textToBePrint;
        PrintingFileName = FileName;
        UnregisterForPrinting();
        RegisterForPrinting();
        await PrintManager.ShowPrintUIAsync();
    }
}

public class PageLoadState
{
    private CountdownEvent loadingElements;

    public Action<int, UIElement> ReadyAction { get; set; }

    private int pageNumber;

    private UIElement page;

    public UIElement Page
    {
        get { return page; }
    }

    public PageLoadState(UIElement page, int pageNumber)
    {
        this.page = page;
        this.pageNumber = pageNumber;
        loadingElements = new CountdownEvent(0);
    }

    private void SetElementComplete()
    {
        loadingElements.Signal();
    }

    public void ListenForCompletion(BitmapImage bitmap)
    {
        if (loadingElements.CurrentCount == 0)
        {
            // Event is already signaled. Manually set the count to 1 and "arm" the event.
            loadingElements.Reset(1);
        }
        else
        {
            // AddCount will throw if event is already in signaled state.
            loadingElements.AddCount();
        }
        bitmap.ImageOpened += (s, e) => SetElementComplete();
    }

    public bool Ready
    {
        get
        {
            var ready = loadingElements.CurrentCount == 0;
            if (!ready)
            {
                // A request was made and the content is not ready, serve it once it's complete
                Task.Run(async () =>
                {
                    await IsReadyAsync();
                    ReadyAction(pageNumber, page);
                });
            }

            return ready;
        }
    }

    public async Task IsReadyAsync()
    {
        await Task.Run(() => { loadingElements.Wait(); });
    }
}

internal enum DisplayContent : int
{
    Text = 1,

    Images = 2,

    TextAndImages = 3
}
person Farhan Ghumra    schedule 04.09.2013
comment
Ого, кодов действительно много !! Я изучу это и вернусь позже. Мне нужно спросить вас, как распечатать его на принтере. INSTEAD распечатайте его в файл, который использует этот код: await PrintHelper.ShowPrintUIAsync (printCanvas, PrintText, PrintedListBox.pdf); Спасибо :) - person MilkBottle; 05.09.2013
comment
Он также будет печатать на принтере, просто выберите принтер, а не XPS или Acrobat in deice charm. В основном вы можете печатать, а также создавать PDF или XPS с помощью моего кода. - person Farhan Ghumra; 05.09.2013
comment
Все еще работаю над этим. Поймите, вы передаете Printext классу Helpler. Если мне нужно распечатать счет-фактуру или заказ, как передать клиента, адрес, счет-фактуру и другие, чтобы создать заголовок для заказа или счета-фактуры. Спасибо - person MilkBottle; 09.09.2013
comment
Попробуйте это, это поможет вам. - person Farhan Ghumra; 09.09.2013
comment
Спасибо. Ваше решение лучше подходит для моей проблемы, в которой ListBox содержит все данные. Теперь мне просто нужно добавить заголовок над ListBox (см. Выше Update (2)). Все данные передаются этому методу для печати: await PrintHelper.ShowPrintUIAsync (printCanvas, PrintText, PrintedListBox.pdf), который не имеет формата, подобного формату Update (2). Как печатать с форматом, например с заголовком вверху, данными в теле, как в обычном формате заказа или счета-фактуры? Спасибо - person MilkBottle; 09.09.2013
comment
Сначала попробуйте из вышеуказанного решения, а затем сообщите мне, если у вас возникнут какие-либо трудности. - person Farhan Ghumra; 09.09.2013
comment
Для печати ListBox ваш метод работает. Я разместил еще один вопрос, который больше похож на использование отформатированной страницы, содержащей список. Ссылка: stackoverflow.com/questions/18694979/ Мы будем благодарны за вашу помощь. - person MilkBottle; 09.09.2013