WPF FixedPage с тем же экземпляром UserControl

Во-первых, я не уверен, правильно ли я сформулировал название. Есть UserControls которые добавляются через ViewModel и я нахожу их, ища VisualTree и добавить их в ObservableCollection<Grid>, Я хотел бы напечатать каждый экземпляр UserControl что я извлекаю из VisualTree в FixedDocument но с каждым UserControl находясь на одной странице, пока она не заполнит эту страницу и не перейдет на следующую страницу.

Вот код моего текущего события нажатия кнопки "Печать":

private async void btnPrint_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            if (tabMain.Items.Count > 0)
            {
                tab = new TabLayout();

                tab.Payslip = new ObservableCollection<Grid>();


                foreach (var grid in FindVisualChildren<Grid>(this))
                {
                    if (grid.Name == "pdfFile")
                    {
                        tab.Payslip.Add(grid);
                    }
                }

                FrameworkElement toPrint = new FrameworkElement();
                PrintDialog printDialog = new PrintDialog();

                PrintCapabilities capabilities = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket);
                Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
                Size visibleSize = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
                FixedDocument fixedDoc = new FixedDocument();


                StackPanel panel = new StackPanel(); //was trying to stack them in a stackpanel first but it threw an exception about same instance of usercontrol blah blah...


                foreach (var doc in tab.Payslip.ToList())
                {

                    double yOffset = 0;

                    doc.Measure((new Size(double.PositiveInfinity, double.PositiveInfinity)));
                    doc.Arrange(new Rect(new Point(0, 0), doc.DesiredSize));

                    Size size = doc.DesiredSize;


                    while (yOffset < size.Height)
                    {
                        VisualBrush vb = new VisualBrush(doc);
                        vb.Stretch = Stretch.None;
                        vb.AlignmentX = AlignmentX.Left;
                        vb.AlignmentY = AlignmentY.Top;
                        vb.ViewboxUnits = BrushMappingMode.Absolute;
                        vb.TileMode = TileMode.None;
                        vb.Viewbox = new Rect(0, yOffset, visibleSize.Width, visibleSize.Height);

                        FixedPage page = new FixedPage();
                        PageContent pageContent = new PageContent();
                        ((IAddChild)pageContent).AddChild(page);
                        fixedDoc.Pages.Add(pageContent);
                        page.Width = fixedDoc.DocumentPaginator.PageSize.Width;
                        page.Height = fixedDoc.DocumentPaginator.PageSize.Height;

                        Canvas canvas = new Canvas();
                        FixedPage.SetLeft(canvas, capabilities.PageImageableArea.OriginWidth);
                        FixedPage.SetTop(canvas, capabilities.PageImageableArea.OriginHeight);
                        canvas.Width = visibleSize.Width;
                        canvas.Height = visibleSize.Height;
                        canvas.Background = vb;
                        page.Children.Add(canvas);
                        yOffset += visibleSize.Height;
                    }
                }
                //printDialog.PrintDocument(fixedDoc.DocumentPaginator, "");
                ShowPrintPreview(fixedDoc);
            }
        }
        catch(Exception ex)
        {
            var exceptionDialog = new MessageDialog
            {
                Message = { Text = ex.ToString() }
            };

            await DialogHost.Show(exceptionDialog, "RootDialog");
        }
    }

Вот как это выглядит, когда я пытаюсь распечатать три вкладки (UserControls размещены на вкладках): как вы можете видеть здесь, он печатает три отдельные страницы

Я хочу, чтобы все три на одной странице, и если я напечатал 10 вкладок, то он должен заполнить первую страницу и перейти на следующую страницу.

В последний раз, когда я задавал подобный вопрос, меня спрашивали, написал ли я код. Куски и кусочки этого кода взяты из похожих FixedDocument вопросы по Stackru но он был отредактирован до такой степени, что он на самом деле работает для меня. Так что да, я знаю, что FixedPage ссылка внутри foreach Это является причиной создания трех отдельных страниц, и я понимаю код.

Резюме:

То, что я хочу знать, это как получить UserControls с каждого Tabна одну страницу до ее полного заполнения, без получения "Указанный элемент уже является логическим дочерним элементом другого элемента. Сначала отключите его". ошибка.

1 ответ

Решение

Я в конечном итоге с помощью ItemsControl держать мой ObservableCollection из Controls как список и распечатать из ItemsControl используя это XpsDocumentWriter метод.

public void PrintItemsTo(ItemsControl ic, String jobName)
    {
        PrintDialog dlg = new PrintDialog();
        dlg.UserPageRangeEnabled = true;
        if (dlg.ShowDialog().GetValueOrDefault())
        {
            PageRange range = dlg.PageRange;
            //    range check - user selection starts from 1
            if (range.PageTo > ic.Items.Count)
                range.PageTo = ic.Items.Count;

            dlg.PrintQueue.CurrentJobSettings.Description = jobName;

            XpsDocumentWriter xdw = PrintQueue.CreateXpsDocumentWriter(dlg.PrintQueue);
            if (dlg.UserPageRangeEnabled == false || range.PageTo < range.PageFrom)
                WriteAllItems(ic, xdw);
            else
                WriteSelectedItems(ic, xdw, range);
        }
    }

    private void WriteAllItems(ItemsControl ic, XpsDocumentWriter xdw)
    {
        PageRange range = new PageRange(1, ic.Items.Count);
        WriteSelectedItems(ic, xdw, range);
    }

    private void WriteSelectedItems(ItemsControl ic, XpsDocumentWriter xdw, PageRange range)
    {
        IItemContainerGenerator generator = ic.ItemContainerGenerator;

        // write visuals using a batch operation
        VisualsToXpsDocument collator = (VisualsToXpsDocument)xdw.CreateVisualsCollator();

        collator.BeginBatchWrite();
        if (WritePageRange(collator, generator, range))
            collator.EndBatchWrite();
    }

    private bool WritePageRange(VisualsToXpsDocument collator, IItemContainerGenerator generator, PageRange range)
    {
        //    Get the generator position of the first data item
        //    PageRange reflects user's selection starts from 1
        GeneratorPosition startPos = generator.GeneratorPositionFromIndex(range.PageFrom - 1);
        //    If PageFrom > PageTo, print in reverse order
        //    UPDATE: never occurs; PrintDialog always 'normalises' the PageRange
        GeneratorDirection direction = (range.PageFrom <= range.PageTo) ? GeneratorDirection.Forward : GeneratorDirection.Backward;

        using (generator.StartAt(startPos, direction, true))
        {
            for (int numPages = Math.Abs(range.PageTo - range.PageFrom) + 1; numPages > 0; --numPages)
            {
                bool newlyRealized;

                // Get or create the child
                UIElement next = generator.GenerateNext(out newlyRealized) as UIElement;
                if (newlyRealized)
                {
                    generator.PrepareItemContainer(next);
                }

                //    The collator doesn't like ContentPresenters and produces blank pages
                //    for pages 2-n.  Finding the child of the ContentPresenter and writing
                //    that solves seems to solve the problem
                if (next is ContentPresenter)
                {
                    ContentPresenter presenter = (ContentPresenter)next;
                    presenter.UpdateLayout();
                    presenter.ApplyTemplate();    //    not sure if this is necessary
                    DependencyObject child = VisualTreeHelper.GetChild(presenter, 0);
                    if (child is UIElement)
                        next = (UIElement)child;
                }

                try
                {
                    collator.Write(next);
                }
                catch
                {
                    //    if user prints to Microsoft XPS Document Writer
                    //    and cancels the SaveAs dialog, we get an exception.
                    return false;
                }
            }
        }

        return true;
    }

PrintItemsTo (), отображает PrintDialog для печати на любом принтере, в то время как WriteAllItems отображает диалоговое окно для сохранения в формате PDF.

Другие вопросы по тегам