13

WinForms アプリケーションから印刷しようとすると、2 つの問題が発生します。最初のものは、私が何をしようとしても非常に悪い品質です。2 つ目は、左上隅に大きなページ マージンがあり、winform が切断されていることです。何か案は?これは私のコードです:

Bitmap MemoryImage;
    public void GetPrintArea(Panel pnl)
    {
        MemoryImage = new Bitmap(pnl.Width, pnl.Height);
        Rectangle rect = new Rectangle(0, 0, pnl.Width, pnl.Height);
        pnl.DrawToBitmap(MemoryImage, new Rectangle(0, 0, pnl.Width, pnl.Height));
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        if (MemoryImage != null)
        {
            e.Graphics.DrawImage(MemoryImage, 0, 0);
            base.OnPaint(e);
        }
    }
    void printdoc1_PrintPage(object sender, PrintPageEventArgs e)
    {
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
        e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
        e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        Rectangle pagearea = e.PageBounds;
        e.Graphics.DrawImage(MemoryImage, (pagearea.Width / 2) - (this.panel1.Width / 2), this.panel1.Location.Y);

    }
    public void Print(Panel pnl)
    {
        panel1 = pnl;
        GetPrintArea(pnl);
        printPreviewDialog1.Document = printdoc1;
        printPreviewDialog1.ShowDialog();
    }
    private void button2_Click(object sender, EventArgs e)
    {
        Print(this.panel1);
    }
4

4 に答える 4

18

これが何度も出てきます。魔法のような解決策はありませんが、最終的には問題が解消される可能性があります。「レティーナ」ディスプレイの出現は極めて重要です。

中心的な問題は、モニターの解像度がプリンターより大幅に悪いことです。一般的なプリンターの解像度は、1 インチあたり 600 ドットです。これにより、1 枚の紙に 6600 x 5100 の個々のピクセルを印刷できます。フル HD モニターは 1920 x 1080 ピクセルで表示できますギブ・オア・テイクの約 5 倍悪化します。

これは、モニターに表示されているものを紙に印刷し、同じサイズに保とうとするとうまくいきません。必然的に、モニターにはピクセルが不足しているため、モニターからの各 1 ピクセルが 5x5 のブロブとして紙に印刷されます。ピクセル マッピングを 1 対 1 に維持しようとする、非常にシャープなコピーが紙に印刷されます。でも切手になってしまった。

必然的に、これらのピクセルの塊が原因で、印刷物は非常に粗く見えます。特に見栄えが悪いのはテキストです。オペレーティング システムは、解像度の低いモニターでテキストを適切に表示するために多くのトリックを使用します。アンチエイリアシングは標準であり、ClearType のようなトリックは、知覚される解像度を高めるのに役立つモニターの物理特性を利用するように設計されています。テキストが印刷されると、これは機能しなくなります。これらのアンチエイリアシング ピクセルはブロブに変わり、非常に目立つようになり、効果が完全に損なわれます。カラー プリンタの ClearType テキストでは特に問題で、赤と青のカラー フリンジがはっきりと見えるようになりました。

適切な唯一のアプローチは、モニターの解像度ではなく、実際の解像度を使用してプリンターにレンダリングすることです。.NET で PrintDocument クラスを使用する場合と同様です。レポート ジェネレーターを使用すると、レポート ジェネレーターのコードを記述する必要がなくなります。

于 2013-06-02T17:12:38.850 に答える
4

PrintDocument が印刷されるときに取得する Graphics オブジェクトに自分自身を描画する必要があります。これにより、必要なすべての制御が可能になります。それでも、Hans Passant が言ったことはすべてここにも当てはまります...これは、達成できることを単にデモする最も単純な実装であることに注意してください。これが最も簡単/最良/最も生産的な方法であるとは主張していません...私のコード複数のページ、コンテナに含まれるコントロール、Label および PictureBox のタイプではないコントロールは使用しません。

System.Drawing.Graphicsの Draw... メソッドを使用しました

これを機能させるために、上記のコードをわずかに変更します。

public void GetPrintArea(Panel pnl, Graphics gr)
{
    // scale to fit on width of page...
    if (pnl.Width > 0)
    {
      gr.PageScale = gr.VisibleClipBounds.Width/pnl.Width;
    }
    // this should recurse...
    // just for demo so kept it simple
    foreach (var ctl in pnl.Controls)
    {
        // for every control type
        // come up with a way to Draw its
        // contents
        if (ctl is Label)
        {
            var lbl = (Label)ctl;
            gr.DrawString(
                lbl.Text,
                lbl.Font,
                new SolidBrush(lbl.ForeColor),
                lbl.Location.X,  // simple based on the position in the panel
                lbl.Location.Y);
        }
        if (ctl is PictureBox)
        {
            var pic = (PictureBox)ctl;
            gr.DrawImageUnscaledAndClipped(
                pic.Image,
                new Rectangle(
                    pic.Location.X,
                    pic.Location.Y,
                    pic.Width,
                    pic.Height));
        }
    }
}

void printdoc1_PrintPage(object sender, PrintPageEventArgs e)
{
    e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality;
    e.Graphics.InterpolationMode =Drawing2D.InterpolationMode.HighQualityBilinear;
    e.Graphics.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality;
    GetPrintArea(panel1, e.Graphics);
}
于 2013-06-02T18:19:34.323 に答える
3

ビットマップ レベルではなく「ベクター」レベルでスケーリングを適用することで、よりシャープなコントロールを実際に印刷できます。

このスナップショットは、次の手法の結果を示しています (私の Win2000 っぽい UI を気にしないでください :-) ):

複合

私たちがしていることは、 reneが彼の答えで示してControlCollectionいるのと非常によく似た方法でコントロールを反復することです.

しかし、さらに、コントロール自体の位置、サイズ、およびフォントにスケールを適用してから、ビットマップを事前設定されたサイズのビットマップに描画します。ほとんどのプリンタ)。

この理由は、印刷時にコントロールの細い線を鮮明に保つためです。または、ビットマップ自体をスケーリングするだけでは、解像度に関して何のメリットもありません。フォントをスケーリングすることで、アンチエイリアス効果を減らし、より良い印刷品質を提供できます。

これを行うには、最初にボタンのクリック イベントを次のように設定します。

//this will produce 5x "sharper" print
MemoryImage = new Bitmap((Panel1.Width * 5), (Panel1.Height * 5));

Using Graphics g = Graphics.FromImage(MemoryImage) {
    ScaleControls(Panel1, g, 5);
};

PrintPreviewDialog1.Document = printdoc1;
PrintPreviewDialog1.ShowDialog();

再帰ScaleControls的な関数では、ビットマップに描画する前に、各コントロール自体をより高い解像度にするために、位置、サイズ、およびフォントをスケーリングします。

private void ScaleControls(Control c, ref Graphics g, double s)
{
    //To detach controls for panels, groupboxes etc.
    List<Control> hold = null;

    foreach (Control ctrl in c.Controls) {
        if (ctrl is GroupBox || ctrl is Panel) {
            //backup reference to controls
            hold = new List<Control>();
            foreach (Control gctrl in ctrl.Controls) {
                hold.Add(gctrl);
            }
            ctrl.Controls.Clear();
        }

        //backup old location, size and font (see explanation)
        Point oldLoc = ctrl.Location;
        Size oldSize = ctrl.Size;
        Font oldFont = ctrl.Font;

        //calc scaled location, size and font
        ctrl.Location = new Point(ctrl.Location.X * s, ctrl.Location.Y * s);
        ctrl.Size = new Size(ctrl.Size.Width * s, ctrl.Height * s);
        ctrl.Font = new Font(ctrl.Font.FontFamily, ctrl.Font.Size * 5,
                             ctrl.Font.Style, ctrl.Font.Unit);

        //draw this scaled control to hi-res bitmap
        using (Bitmap bmp = new Bitmap(ctrl.Size.Width, ctrl.Size.Height)) {
            ctrl.DrawToBitmap(bmp, ctrl.ClientRectangle);
            g.DrawImage(bmp, ctrl.Location);
        }

        //restore control's geo
        ctrl.Location = oldLoc;
        ctrl.Size = oldSize;
        ctrl.Font = oldFont;

        //recursive for panel, groupbox and other controls
        if (ctrl is GroupBox || ctrl is Panel) {
            foreach (Control gctrl in hold) {
                ctrl.Controls.Add(gctrl);
            }

            ScaleControls(ctrl, g, s);
        }
    }
}

最後に、印刷用のイベント ハンドラーで:

double scale = MemoryImage.Width / e.PageBounds.Width;

e.Graphics.DrawImage(MemoryImage, 0, 0,
          Convert.ToInt32(MemoryImage.Width / scale),
          Convert.ToInt32(MemoryImage.Height / scale));

さて、この例では、コントロールをその場でスケーリングします。もちろん、印刷プレビューを行っている間、彼らは自分の人生を生きているように見えるので、これは理想的ではありません.

理想的には、繰り返しながら各コントロールを複製し、ビットマップに描画した後に破棄します。これにより、ジオメトリをバックアップする必要もなくなります。しかし、原則の例として、そのまま残しました。クローンなどはお任せします。

コントロールをデタッチする理由は、そうしないと (コードが現在のように - これは別の反復方法 (つまり、複製されたコントロールの事前スケーリング) を提供することで確実に変更できます) で、f.ex. GroupBoxコントロールが最初に印刷され、次にスケーリングされたものが反復時にそれらの上に表示されます。これは、そのコントロールをスケーリングする前に行うDrawToBitmapためです。GroupBoxこれはあなたに任せます。

私たちが取り組んでいるビットマップは、ユーザーが印刷ダイアログを設定して最終的に得られる印刷の解像度に必ずしも適合しませんが、より高い解像度で作業することで、貧弱な画面ビットマップよりも優れた結果が得られます。私たちが最初に持っている解決策。

もちろん、他のコントロール、画像コントロールなどを保持できるPanel以外のコントロールの特殊なケースのサポートを追加する必要があります。GroupBox

于 2013-06-08T07:36:33.510 に答える
0

パネルとその内容を高品質で印刷する方法を探すのに何日も費やしました。これは機能せず、他の人のコードを試してみましたが、これが見つかるまで、すべて間違っているか、品質が悪いだけでした:

http://rkinfopedia.blogspot.com/2008/07/printing-contents-of-panel-control.html

次のように、印刷ボタンのクリック イベント ハンドラー内にイベント ハンドラーを配置し、それに印刷メソッドも含めます。

private void button3_Click(object sender, EventArgs e)
{
    printdoc1.PrintPage += new PrintPageEventHandler(printdoc1_PrintPage);
    Print(panel1);
}

次のように、オーバーライド OnPaint メソッド内に if ステートメントを配置します。

protected override void OnPaint(PaintEventArgs e)
{
    if (MemoryImage != null)
    {
        e.Graphics.DrawImage(MemoryImage, 0, 0);
        base.OnPaint(e);
    }
}

残りはそのままで、最終的にほぼ完璧な印刷品質が得られます

この宝石を共有したかっただけです。インターネットの見知らぬ人は大歓迎です!

Rakeshさん、ありがとうございます!

于 2013-10-06T20:11:30.350 に答える