C# 打印大型WPF用户控件

C# 打印大型WPF用户控件,c#,wpf,xaml,C#,Wpf,Xaml,我有一个巨大的数据,我想打印使用WPF。我发现WPF提供了一个PrintDialog.PrintVisual方法来打印从Visual类派生的任何WPF控件 PrintVisual将只打印一页,因此我需要缩放控件以适应页面。不幸的是,这对我来说不起作用,因为报告有时足够长,当按比例缩放到适合页面时无法轻松阅读 WPF提供的另一个打印选项是在流程文档中创建单独的视图。这可能是打印文档的最佳方式,但这比我希望投入的工作量要多,更不用说我希望打印的每个控件都必须维护的额外视图了 我得到了另一个解决方案,

我有一个巨大的数据,我想打印使用WPF。我发现WPF提供了一个
PrintDialog.PrintVisual
方法来打印从
Visual
类派生的任何WPF控件

PrintVisual
将只打印一页,因此我需要缩放控件以适应页面。不幸的是,这对我来说不起作用,因为报告有时足够长,当按比例缩放到适合页面时无法轻松阅读

WPF提供的另一个打印选项是在
流程文档
中创建单独的视图。这可能是打印文档的最佳方式,但这比我希望投入的工作量要多,更不用说我希望打印的每个控件都必须维护的额外视图了

我得到了另一个解决方案,但对我来说太复杂了


有没有更好、更简单的解决方案?感谢您的帮助

我假设您的报告显示在
数据网格
或其他可滚动的文件中

我相信
FlowDocument
绝对是您在这里的最佳选择,如果您想打印一些看起来很专业的东西,因为没有更好的词。但是,如果您想要快速而肮脏的东西,可以使用
RenderTargetBitmap.Render
进行一系列操作。基本过程将是:

  • 创建
    RenderTargetBitmap
  • 滚动视图,以便在一页上显示要打印的区域
  • 调用
    RenderTargetBitmap.Render
    DataGrid
    或包含“大”控件的
    ScrollViewer
  • 打印生成的位图
  • 对下一“页面”重复此操作
  • 同样,不要在“大”控件上调用
    RenderTargetBitmap.Render
    。将大控件包装在
    ScrollViewer
    中(如果尚未包装)。这将基本上是你的paginator


    我不知道你对结果是否满意,但这是我能想到的最简单的方法。每次你都会手动点击
    PrintScreen
    。不确定这是否是您想要的,但如果您想让它看起来更好,我认为您需要使用
    FlowDocument

    我使用PrintDialog和DocumentPaginator进行打印

    我所做的是:

    • 选择打印机(显示打印对话框或使用系统默认设置)
    • 创建页面(纸张大小的wpf控件)
    • 印刷品
    以下是我的测试函数:

    public static void PrintTest1(Viewbox viewboxInWindowForRender)
    {
        FrameworkElement[] testContArr = PrepareTestContents();
    
        //=========================
        PrintManager man = new PrintManager();
    
        // Show print dialog (or select default printer)
        if (!man.SelectPrinter())
            return;
    
        man.SetPageMargins(new Thickness(PrintManager.Size1cm * 2));
    
        //=========================
        List<FrameworkElement> pagesForPrint = new List<FrameworkElement>();
    
        for (int i = 0; i < testContArr.Length; i++)
        {
            // Put the page content into the control of the size of paper
            FrameworkElement whitePage = man.CreatePageWithContentStretched(testContArr[i]);
            // Temporary put the page into window (need for UpdateLayout)
            viewboxInWindowForRender.Child = whitePage;
            // Update and render whitePage.
            // Measure and Arrange will be used properly.
            viewboxInWindowForRender.UpdateLayout();
    
            pagesForPrint.Add(whitePage);
        }
        viewboxInWindowForRender.Child = null;
        //=========================
        // Now you can show print preview to user.
        // pagesForPrint has all pages.
        // ...
        //=========================
    
        MyDocumentPaginator paginator = man.CreatePaginator();
        paginator.AddPages(pagesForPrint);
    
        // Start printing
        man.Print(paginator, "Printing Test");
    }
    
    // For testing
    public static FrameworkElement[] PrepareTestContents()
    {
        StackPanel sp1 = new StackPanel();
        sp1.Width = PrintManager.PageSizeA4.Width - PrintManager.Size1cm * 2;
        sp1.Children.Add(PrepareTestBorder("Alice has a cat."));
        sp1.Children.Add(PrepareTestBorder("Page number one."));
    
        StackPanel sp2 = new StackPanel();
        sp2.Width = sp1.Width / 2;
        sp2.Children.Add(PrepareTestBorder("Farmer has a dog."));
        sp2.Children.Add(PrepareTestBorder("Page number two."));
    
        return new FrameworkElement[] {sp1, sp2 };
    }
    
    // For testing
    public static FrameworkElement PrepareTestBorder(string text)
    {
        Border b = new Border();
        b.BorderBrush = Brushes.Black;
        b.BorderThickness = new Thickness(1);
        b.Margin = new Thickness(0, 0, 0, 5);
    
        TextBlock t = new TextBlock();
        t.Text = text;
    
        b.Child = t;
        return b;
    }
    
    public static void RemoveFromParent(FrameworkElement child)
    {
        DependencyObject parent = child.Parent;
    
        if (parent == null)
            return;
        if (parent is Panel)
            ((Panel)parent).Children.Remove(child);
        else if (parent is Decorator)
            ((Decorator)parent).Child = null;
        else if (parent is ContentControl)
            ((ContentControl)parent).Content = null;
        else if (parent is ContentPresenter)
            ((ContentPresenter)parent).Content = null;
        else
            throw new Exception("RemoveFromParent: Unsupported type " + parent.GetType().ToString());
    }
    
    这是我的MyDocumentPaginator课程:

    public class PrintManager
    {
        public static readonly Size PageSizeA4 = new Size(21 * 96 / 2.54, 29.7 * 96 / 2.54); // (793.700787401575, 1122.51968503937)
        public static readonly double Size1cm = 96 / 2.54; // 37.7952755905512
    
        private PrintDialog _printDialog;
    
        public PrintTicket PrintTicket { get; private set; }
        public PrintCapabilities TicketCapabilities { get; private set; }
    
        // Page size selected in print dialog (may not be exactly as paper size)
        public Size PageSize { get; private set; }
        public Thickness PageMargins { get; private set; }
    
        public Rect PageContentRect {
            get {
                return new Rect(PageMargins.Left, PageMargins.Top,
                    PageSize.Width - PageMargins.Left - PageMargins.Right,
                    PageSize.Height - PageMargins.Top - PageMargins.Bottom);
            }
        }
    
        public PrintManager()
        {
        }
    
        /// <summary>
        /// Show print dialog or try use default printer when useDefaultPrinter param set to true.
        /// <para/>
        /// Return false on error or when user pushed Cancel.
        /// </summary>
        public bool SelectPrinter(bool useDefaultPrinter = false)
        {
            if (_printDialog == null)
                _printDialog = new PrintDialog();
    
            try
            {
                if (useDefaultPrinter)
                    _printDialog.PrintQueue = LocalPrintServer.GetDefaultPrintQueue();
    
                // pDialog.PrintQueue == null when default printer is not selected in system
                if (_printDialog.PrintQueue == null || !useDefaultPrinter)
                {
                    // Show print dialog
                    if (_printDialog.ShowDialog() != true)
                        return false;
                }
    
                if (_printDialog.PrintQueue == null)
                    throw new Exception("Printer error");
    
                // Get default printer settings
                //_printDialog.PrintTicket = _printDialog.PrintQueue.DefaultPrintTicket;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
    
            PrintTicket = _printDialog.PrintTicket;
            TicketCapabilities = _printDialog.PrintQueue.GetPrintCapabilities(PrintTicket);
            PageSize = new Size((double)TicketCapabilities.OrientedPageMediaWidth,
                (double)TicketCapabilities.OrientedPageMediaHeight);
            SetPageMargins(PageMargins); // Update margins if too small
    
            return true;
        }
    
        /// <summary>
        ///  Start printing pages from paginator.
        /// </summary>
        public void Print(MyDocumentPaginator paginator, string printTaskDescription)
        {
            if (_printDialog == null)
                return;
    
            // Start printing document
            _printDialog.PrintDocument(paginator, printTaskDescription);
        }
    
        /// <summary>
        /// Set page margins and return true.
        /// <para/>
        /// If new page margins are too small (unprinted area) then set minimum and return false.
        /// </summary>
        public bool SetPageMargins(Thickness margins)
        {
            PageImageableArea pia = TicketCapabilities.PageImageableArea;
    
            PageMargins = new Thickness(Math.Max(margins.Left, pia.OriginWidth),
                Math.Max(margins.Top, pia.OriginHeight),
                Math.Max(margins.Right, PageSize.Width - pia.OriginWidth - pia.ExtentWidth),
                Math.Max(margins.Bottom, PageSize.Height - pia.OriginHeight - pia.ExtentHeight));
    
            return PageMargins == margins;
        }
    
        /// <summary>
        /// Set pate margins with minimal
        /// </summary>
        public void SetMinimalPageMargins()
        {
            PageImageableArea pia = TicketCapabilities.PageImageableArea;
    
            // Set minimal page margins to bypass the unprinted area.
            PageMargins = new Thickness(pia.OriginWidth, pia.OriginHeight,
                (double)TicketCapabilities.OrientedPageMediaWidth - - pia.OriginWidth - pia.ExtentWidth,
                (double)TicketCapabilities.OrientedPageMediaHeight - pia.OriginHeight - pia.ExtentHeight);
        }
    
        /// <summary>
        /// Create page control witch pageContent ready to print.
        /// Content is stretched to the margins.
        /// </summary>
        public FrameworkElement CreatePageWithContentStretched(FrameworkElement pageContent)
        {
            // Place the content inside the page (without margins)
            Viewbox pageInner = new Viewbox();
            pageInner.VerticalAlignment = VerticalAlignment.Top; // From the upper edge
            pageInner.Child = pageContent;
    
            // Printed control - the page with content
            Border whitePage = new Border();
            whitePage.Width = PageSize.Width;
            whitePage.Height = PageSize.Height;
            whitePage.Padding = PageMargins;
            whitePage.Child = pageInner;
    
            return whitePage;
        }
    
        /// <summary>
        /// Create page control witch pageContent ready to print.
        /// <para/>
        /// Content is aligned to the top-center and must have
        /// a fixed size (max PageSize-PageMargins).
        /// </summary>
        public FrameworkElement CreatePageWithContentSpecSize(FrameworkElement contentSpecSize)
        {
            // Place the content inside the page
            Decorator pageInner = new Decorator();
            pageInner.HorizontalAlignment = HorizontalAlignment.Center;
            pageInner.VerticalAlignment = VerticalAlignment.Top;
            pageInner.Child = contentSpecSize;
    
            // Printed control - the page with content
            Border whitePage = new Border();
            whitePage.Width = PageSize.Width;
            whitePage.Height = PageSize.Height;
    
            // We align to the top-center only, because padding will cut controls
            whitePage.Padding = new Thickness(0, PageMargins.Top, 0, 0);
    
            whitePage.Child = pageInner;
            return whitePage;
        }
    
        /// <summary>
        /// Create paginator for pages created by CreatePageWithContent().
        /// </summary>
        public MyDocumentPaginator CreatePaginator()
        {
            return new MyDocumentPaginator(PageSize);
        }
    }
    
    public class MyDocumentPaginator : DocumentPaginator
    {
        private List<FrameworkElement> _pages = new List<FrameworkElement>();
    
        public override bool IsPageCountValid  { get { return true; } }
        public override int PageCount { get { return _pages.Count; } }
        public override Size PageSize { get; set; } 
        public override IDocumentPaginatorSource Source { get { return null; } }  
    
        public MyDocumentPaginator(Size pageSize)
        {
            PageSize = pageSize;
        }
    
        public override DocumentPage GetPage(int pageNumber)
        {
            // Warning: DocumentPage remember only reference to Visual object.
            // Visual object can not be changed until PrintDialog.PrintDocument() called
            // or e.g. XpsDocumentWriter.Write().
            // That's why I don't create DocumentPage in AddPage method.
            return new DocumentPage(_pages[pageNumber], PageSize, new Rect(PageSize), new Rect(PageSize));
        }
    
        public void AddPage(FrameworkElement page)
        {
            _pages.Add(page);
        }
        public void AddPages(List<FrameworkElement> pages)
        {
            _pages.AddRange(pages);
        }
    }
    
    然后您可以准备页面:

    FrameworkElement whitePage = man.CreatePageWithContentStretched(UserCtrl);
    viewboxInWindowForRender.Child = whitePage;
    viewboxInWindowForRender.UpdateLayout();
    
    MyDocumentPaginator paginator = man.CreatePaginator();
    paginator.AddPages(whitePage);
    
    man.Print(paginator, "Printing UserControl");
    
    // After print you can restore UserCtrl
    RemoveFromParent(UserCtrl);
    ParentBorder.Child = UserCtrl;
    
    下面是从父级移除的函数:

    public static void PrintTest1(Viewbox viewboxInWindowForRender)
    {
        FrameworkElement[] testContArr = PrepareTestContents();
    
        //=========================
        PrintManager man = new PrintManager();
    
        // Show print dialog (or select default printer)
        if (!man.SelectPrinter())
            return;
    
        man.SetPageMargins(new Thickness(PrintManager.Size1cm * 2));
    
        //=========================
        List<FrameworkElement> pagesForPrint = new List<FrameworkElement>();
    
        for (int i = 0; i < testContArr.Length; i++)
        {
            // Put the page content into the control of the size of paper
            FrameworkElement whitePage = man.CreatePageWithContentStretched(testContArr[i]);
            // Temporary put the page into window (need for UpdateLayout)
            viewboxInWindowForRender.Child = whitePage;
            // Update and render whitePage.
            // Measure and Arrange will be used properly.
            viewboxInWindowForRender.UpdateLayout();
    
            pagesForPrint.Add(whitePage);
        }
        viewboxInWindowForRender.Child = null;
        //=========================
        // Now you can show print preview to user.
        // pagesForPrint has all pages.
        // ...
        //=========================
    
        MyDocumentPaginator paginator = man.CreatePaginator();
        paginator.AddPages(pagesForPrint);
    
        // Start printing
        man.Print(paginator, "Printing Test");
    }
    
    // For testing
    public static FrameworkElement[] PrepareTestContents()
    {
        StackPanel sp1 = new StackPanel();
        sp1.Width = PrintManager.PageSizeA4.Width - PrintManager.Size1cm * 2;
        sp1.Children.Add(PrepareTestBorder("Alice has a cat."));
        sp1.Children.Add(PrepareTestBorder("Page number one."));
    
        StackPanel sp2 = new StackPanel();
        sp2.Width = sp1.Width / 2;
        sp2.Children.Add(PrepareTestBorder("Farmer has a dog."));
        sp2.Children.Add(PrepareTestBorder("Page number two."));
    
        return new FrameworkElement[] {sp1, sp2 };
    }
    
    // For testing
    public static FrameworkElement PrepareTestBorder(string text)
    {
        Border b = new Border();
        b.BorderBrush = Brushes.Black;
        b.BorderThickness = new Thickness(1);
        b.Margin = new Thickness(0, 0, 0, 5);
    
        TextBlock t = new TextBlock();
        t.Text = text;
    
        b.Child = t;
        return b;
    }
    
    public static void RemoveFromParent(FrameworkElement child)
    {
        DependencyObject parent = child.Parent;
    
        if (parent == null)
            return;
        if (parent is Panel)
            ((Panel)parent).Children.Remove(child);
        else if (parent is Decorator)
            ((Decorator)parent).Child = null;
        else if (parent is ContentControl)
            ((ContentControl)parent).Content = null;
        else if (parent is ContentPresenter)
            ((ContentPresenter)parent).Content = null;
        else
            throw new Exception("RemoveFromParent: Unsupported type " + parent.GetType().ToString());
    }
    

    为什么我在窗口中使用UpdateLayout和Viewbox,而不是像其他示例中的人那样进行测量和排列

    我试过了,但我有很多问题。我使用我一直拥有的控件,我改变打印的样式,我也。
    测量并安排不为我工作。控件必须停靠在窗口中,以便正确地更新和渲染布局。

    是要打印已有的控件,还是生成要再次打印的控件?@marbel82打印已有的控件
    <Window ...>
        <Grid>
            <Viewbox x:Name="forRender" Visibility="Hidden" Width="100" Height="100"/>
            ...
        </Grid>
    </Window>