【问题标题】:How can I prevent the Print Progress dialog appearing when performing a print preview执行打印预览时如何防止出现“打印进度”对话框
【发布时间】:2010-11-24 08:14:50
【问题描述】:

在我的 C# 应用程序中,我试图在屏幕上不显示进度对话框的情况下生成打印预览。

我相信您可以在实际打印时使用PrintDocument.PrintController 来防止这种情况(即不是打印预览),但是在执行打印预览时它似乎不起作用。

我的代码如下:

public FrmDeliveryNotePrintPreview(DeliveryNote deliveryNote)
{
    InitializeComponent();

    this.Text = "Delivery Note #" + deliveryNote.Id.ToString();


    // The print preview window should occupy about 90% of the
    // total screen height

    int height = (int) (Screen.PrimaryScreen.Bounds.Height * 0.9);


    // Making an assumption that we are printing to A4 landscape,
    // then adjust the width to give the correct height:width ratio
    // for A4 landscape.

    int width = (int) (height / 1.415);


    // Set the bounds of this form. The PrintPreviewControl is
    // docked, so it should just do the right thing

    this.SetBounds(0, 0, width, height);

    PrinterSettings printerSettings = new PrinterSettings();
    PrintDeliveryNotes pdn = new PrintDeliveryNotes(
        new DeliveryNote[] { deliveryNote },
        printerSettings);
    PrintDocument printDocument = pdn.PrintDocument;
    printDocument.PrintController = new PreviewPrintController();
    ppcDeliveryNote.Document = printDocument;
}

打印预览完全按照我的意愿工作,除了显示打印预览进度对话框。

请给点建议?

【问题讨论】:

  • PrintDeliveryNotes 是一个打印交货单数组的类。但在这种情况下,我没有调用 Print() 方法,而是简单地检索 PrintDocument 属性并将其提供给自定义表单上的 PrintPreviewControl。

标签: c# .net printing system.drawing


【解决方案1】:

这对我有用:

将文档的打印控制器设置为StandardPrintController

static class Program
    {

        static void Main()
        {
            PrintDocument doc = new PrintDocument();
            doc.PrintController = new StandardPrintController();
            doc.PrintPage += new PrintPageEventHandler(doc_PrintPage);

            doc.Print();
        }

        static void doc_PrintPage(object sender, PrintPageEventArgs e)
        {
            e.Graphics.DrawString("xxx", Control.DefaultFont, Brushes.Black, new PointF(e.PageBounds.Width / 2, e.PageBounds.Height / 2));
        }
    }

【讨论】:

  • 这对我有用,虽然我没有做预览。有关可用的不同 PrintController,请参阅此处的备注:msdn.microsoft.com/en-us/library/…。 PrintController 默认是 PrintControllerWithStatusDialog。
【解决方案2】:

只是为了确认 Pooven 的回答。我有同样的问题并试图解决,Stefan 的解决方案对我也不起作用。然后我终于查看了源代码,发现它是硬编码的,因此无法更改。如果您需要隐藏状态对话框,请寻找除 PrintPreviewControl 之外的其他解决方案。这是PrintPreviewControl的源代码。

 private void ComputePreview() {
        int oldStart = StartPage;

        if (document == null)
            pageInfo = new PreviewPageInfo[0];
        else {
            IntSecurity.SafePrinting.Demand();

            PrintController oldController = document.PrintController;

// --> HERE they have hardcoded it! Why they do this!

            PreviewPrintController previewController = new PreviewPrintController();
            previewController.UseAntiAlias = UseAntiAlias;
            document.PrintController = new PrintControllerWithStatusDialog(previewController,
                                                                           SR.GetString(SR.PrintControllerWithStatusDialog_DialogTitlePreview));

            // Want to make sure we've reverted any security asserts before we call Print -- that calls into user code
            document.Print();
            pageInfo = previewController.GetPreviewPageInfo();
            Debug.Assert(pageInfo != null, "ReviewPrintController did not give us preview info");

// --> and then swap the old one
            document.PrintController = oldController;
        }

        if (oldStart != StartPage) {
            OnStartPageChanged(EventArgs.Empty);
        }
    }

来源 http://reflector.webtropy.com/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/fx/src/WinForms/Managed/System/WinForms/Printing/PrintPreviewControl@cs/1305376/PrintPreviewControl@cs

【讨论】:

  • 由于 Winforms 现已开源,因此可以提取 PrintPreviewControl 及其依赖项的代码来更改其打印控制器。
【解决方案3】:

我想我做到了。使用此类而不是 PrintPreviewControl:

public class PrintPreviewControlSilent : PrintPreviewControl
{
    public new PrintDocument Document
    {
        get { return base.Document; }
        set
        {
            base.Document = value;

            PreviewPrintController ppc = new PreviewPrintController();

            Document.PrintController = ppc;
            Document.Print();

            FieldInfo fi = typeof(PrintPreviewControl).GetField("pageInfo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            fi.SetValue(this, ppc.GetPreviewPageInfo());
        }
    }
}

【讨论】:

    【解决方案4】:

    我不想回答我自己的问题,但解决方案却让我眼前一亮。

    由于我已经编写了打印交货单的功能,我的下一步是提供屏幕副本(即不打算打印硬拷贝)。打印预览对话框似乎是一个简单的方法。

    最后,我只是创建了一个自定义表单并直接在上面绘制,看不到打印预览控件。

    不幸的是,我过于专注于试图让打印预览对话按照我的意愿行事,而不是着眼于更大的问题。

    【讨论】:

      【解决方案5】:

      PreviewPrintController 而不是 StandardPrintController,您可能会很幸运。

      【讨论】:

      • 是的,但是我已经尝试过了,它仍然会产生进度对话框。
      • 我已经更新了代码示例以包含当前的整个类。
      【解决方案6】:

      一个适合我的解决方案是使用Harmony (v1.2) 并修补上面提到的PrintPreviewControlComputePreview 函数:

      补丁类如下所示

      [Harmony.HarmonyPatch(typeof(System.Windows.Forms.PrintPreviewControl))]
      [Harmony.HarmonyPatch("ComputePreview")]
      class PrintPreviewControlPatch
      {
          static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
          {
              var cis = new List<CodeInstruction>(instructions);
              // the codes 26 to 28 deal with creating the
              // progress reporting preview generator that
              // we don't want. We replace them with No-Operation
              // code instructions. 
              cis[26] = new CodeInstruction(OpCodes.Nop);
              cis[27] = new CodeInstruction(OpCodes.Nop);
              cis[28] = new CodeInstruction(OpCodes.Nop);
              return cis;
          }
      }
      

      要应用补丁,您需要在应用程序的启动中包含以下 2 行:

      var harmony = Harmony.HarmonyInstance.Create("Application.Namespace.Reversed");
      harmony.PatchAll(Assembly.GetExecutingAssembly());
      

      【讨论】:

        【解决方案7】:

        一种解决方法是使用 EnumChildWindows API 来查找窗口的句柄,如果找到,使用带有 SW_HIDE 标志的 ShowWindow API 来隐藏窗口。

        如果您知道窗口的标题,以下是使用 FindWindow 的示例:

        #region Constants 
        
        private const int SW_HIDE = 0;
        
        private const int SW_SHOWNORMAL = 1;
        
        private const int SW_SHOW = 5;
        
        #endregion Constants
        
        #region APIs
        
        [System.Runtime.InteropServices.DllImport("user32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)] 
        
        private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 
        
        [System.Runtime.InteropServices.DllImport("user32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)] 
        
        private static extern bool ShowWindow(IntPtr hwnd, int nCmdShow); 
        
        [System.Runtime.InteropServices.DllImport("user32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)] 
        
        private static extern bool EnableWindow(IntPtr hwnd, bool enabled);
        
        #endregion APIs
        
        public static void ShowProgress()
        
        {
        
        IntPtr h = FindWindow(null, "titleofprogresswindow");
        
        ShowWindow(h, SW_SHOW); 
        
        EnableWindow(h, true); 
        
        }
        
        public static void HideProgress()
        
        {
        
        IntPtr h = FindWindow(null, "titleofprogresswindow");
        
        ShowWindow(h, SW_HIDE); 
        
        EnableWindow(h, false); 
        
        }
        

        【讨论】:

          【解决方案8】:

          似乎PrintPreviewDialog 使用的PrintPreviewControl 将替换PrintDocumentPrintController,以便在预览渲染过程中使用PrintControllerWithStatusDialog。一旦Print 操作完成,PrintController 就会恢复到之前的值。似乎无法自定义 PrintPreviewControl 以使用任何其他 PrintController

          【讨论】: