none
Microsoft Report Viewer:印刷時/印刷レイアウト表示時、用紙右側に出現する余計な余白の消し方 RRS feed

  • 質問

  • Report Viewer初心者です。いろいろと調べたのですが解決方法が分からないのでご質問させてください。

    OS:Windows 10 Enterprise 64bit
    IDE:Microsoft Visual Studio Professional 2015
    言語:C#
    .NET Framework:4.5.2
    Microsoft SQL Server Data Tools:14.0.60519.0
    形式:Windows フォーム アプリケーション
    レポートの用紙設定:A4 横(29.7cm×21cm)
    レポートのプロパティ:
     ・InteractiveSize:29.7cm, 21cm
     ・Margins:0cm, 0cm, 0cm, 0cm
     ・PageSize:29.7cm, 21cm
    本文のプロパティ:
     ・Size:11.65361in, 1.20244in (※cm表記だとそれぞれ29.6001694cm、3.0541976cm)

    Microsoft Report Viewerを利用して帳票を作成しようとしています。
    使用しているコントロールはテーブルのみで、
    上記に記載した本文サイズ幅(約29.6cm)いっぱいにテーブル1つ(カラム9個)を並べただけのシンプルなものです。
    データはクラスを作成してオブジェクトとして流し込んでいます。

    デバッグ実行して印刷を行うと、用紙の約8/10のところまでテーブルが縮小されて印刷されてしまいます。
    カラムは9個すべて印刷されていますが、テーブルの幅終点が用紙の約8/10になってしまっています。
    印刷レイアウトボタンを押下して印刷レイアウトを確認すると、やはり用紙の約8/10のところまでテーブルが縮小されてしまっています。

    本文の横サイズが上記に記載した通り約29.6cmなので、用紙の幅(29.7cm)いっぱいに印刷されるのかなと考えたのですが、
    用紙の右2/10程度(約6cm)があいてしまいます。
    pdf出力でもXPS出力でもプリンタ出力でも用紙の右2/10程度があきます。
    一方、用紙の左側は指定した通り余白0で印刷されます。
    用紙の左側に余白を設定した場合、その分テーブルが右側に動きますが、
    右側の謎の余白は変化せずそこに存在しているので、1ページに出力できるカラム数が少なくなります。

    余白設定は上下左右すべて0なので、右側に余計な余白がつくことなく印刷されてほしいのですが。。。
    また、テーブルが勝手に縮小されるのも不可解です。プリンタ側での倍率変更は行っていません。
    なお、余白設定が上下左右すべて0であることは、デバッグ実行時の「ページ設定」ボタンからも確認しています。

    また、用紙設定を縦にして印刷しても、右側約5cmに余白が出来ます。
    縦の場合は全カラムが用紙に収まりきらず、6カラム目までが1ページ目に印刷されます。
    右側の余白さえなければあと2カラムは1ページ目に収まりそうなのですが。。。

    こういう仕様なのでしょうか、それとも何か設定に誤りがあるのでしょうか。
    この右側の余計な余白を消す方法をご存知でしたら、ご教授いただけますと幸いです。

    なお、フォーム上ではレポートをFillにしていますので、フォーム側に余白があるわけでもないです。
    ほかに足りない情報がありましたらご指摘ください。












    2017年10月26日 7:54

回答

  • ディスプレイの解像度(DPI)の影響を受けて大きさが変わってしまうみたいです。
    AutoScaleModeを変えてみたり、DpiAwareを設定してやるともしかしたら治るかもしれません。

    あるいは自前で描画と印刷を処理してみるとか。
    追記:以下のコードだとDPIの受けたままになってますがマニフェストファイルでDpiAwareを設定すると正しい大きさになります。

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace ReportsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
    
                string rdlcFilePath = "Report1.rdlc";
                Microsoft.Reporting.WinForms.LocalReport report = new Microsoft.Reporting.WinForms.LocalReport();
                using (System.IO.FileStream fs = System.IO.File.OpenRead(rdlcFilePath))
                {
                    report.LoadReportDefinition(fs);
                }
    
                //report.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource("DataSet1", (System.Data.DataTable)table));
    
                var settings = report.GetDefaultPageSettings();
                SizeF pageCMSize = Tool.GetPageCMSize(rdlcFilePath);
                if (settings.IsLandscape)
                {
                    pageCMSize = new SizeF(pageCMSize.Height, pageCMSize.Width);
                }
                var originalEMFStreams = Tool.GetEMFStreams(report);
    
                this._DisplayMetafiles = new List<System.Drawing.Imaging.Metafile>();
                foreach (System.IO.MemoryStream stream in originalEMFStreams)
                {
                    _DisplayMetafiles.Add(Tool.GetDisplayImage(stream, pageCMSize));
                }
    
                PictureBox pictureBox1 = new PictureBox();
                pictureBox1.BackColor = Color.White;
                pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
                pictureBox1.Image = _DisplayMetafiles[0];
    
                NumericUpDown updown = new NumericUpDown();
                updown.Minimum = 0;
                updown.Maximum = _DisplayMetafiles.Count - 1;
                updown.ValueChanged += (s, e)=>
                {
                    pictureBox1.Image = _DisplayMetafiles[(int)updown.Value];
                };
                this.Controls.Add(updown);
    
    
                Panel panel = new Panel();
                panel.Top = updown.Height;
                panel.Height = this.ClientSize.Height - updown.Height;
                panel.Width = this.ClientSize.Width;
                panel.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
                panel.AutoScroll = true;
                panel.Controls.Add(pictureBox1);
                this.Controls.Add(panel);
    
                this.Shown += Form1_Shown;
            }
    
            private List<System.Drawing.Imaging.Metafile> _DisplayMetafiles;
    
            private void Form1_Shown(object sender, EventArgs e)
            {
                PrintDialog dlg = new PrintDialog();
                dlg.Document = new System.Drawing.Printing.PrintDocument();
    
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    int i = 0;
                    dlg.Document.PrintPage += (s, e2) =>
                    {
    
                        e2.Graphics.DrawImage(_DisplayMetafiles[i], Point.Empty);
                        i++;
                        e2.HasMorePages = i < _DisplayMetafiles.Count;
                    };
                    dlg.Document.Print();
                }
            }
    
            private void Document_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
            {
    
            }
        }
    
        class Tool
        {
            /// <summary>RDCLファイルに定義されている用紙サイズをcm単位で取得</summary>
            public static SizeF GetPageCMSize(string filePath)
            {
                using (System.IO.FileStream fs = System.IO.File.OpenRead(filePath))
                {
                    return GetPageCMSize(fs);
                }
            }
            /// <summary>RDCLファイルに定義されている用紙サイズをcm単位で取得</summary>
            public static SizeF GetPageCMSize(System.IO.Stream reportStream)
            {
                System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                doc.Load(reportStream);
    
                string xmlns = "http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition";
                System.Xml.XmlNamespaceManager manager = new System.Xml.XmlNamespaceManager(doc.NameTable);
                manager.AddNamespace("q", xmlns);
                System.Xml.XmlNode nodePage = doc.SelectSingleNode("q:Report/q:Page", manager);
                System.Xml.XmlNode nodeHeight = nodePage.SelectSingleNode("q:PageHeight", manager);
                System.Xml.XmlNode nodeWidth = nodePage.SelectSingleNode("q:PageWidth", manager);
    
                SizeF sz = new SizeF();
                sz.Width = GetCMLength(nodeHeight.InnerText);
                sz.Height = GetCMLength(nodeWidth.InnerText);
                return sz;
            }
    
            private static float GetCMLength(string text)
            {
                text = text.Trim().ToLower();
                if (text.EndsWith("cm"))
                {
                    return float.Parse(text.Substring(0, text.Length - 2));
                }
                else if (text.EndsWith("mm"))
                {
                    return float.Parse(text.Substring(0, text.Length - 2)) / 10;
                }
                else if (text.EndsWith("in"))
                {
                    return float.Parse(text.Substring(0, text.Length - 2)) / 2.54f;
                }
                else
                {
                    throw new NotSupportedException();
                }
            }
    
            /// <summary>LocalReprotをEMF形式の画像に出力</summary>
            public static List<System.IO.MemoryStream> GetEMFStreams(Microsoft.Reporting.WinForms.LocalReport report)
            {
                string devInfo = "<DeviceInfo><OutputFormat>EMF</OutputFormat></DeviceInfo>";
    
                Microsoft.Reporting.WinForms.Warning[] warnings;
                List<System.IO.MemoryStream> streams = new List<System.IO.MemoryStream>();
                report.Render("Image", devInfo
                    , (name, extension, encoding, mimeTYpe, willSeek) =>
                    {
                        var ms = new System.IO.MemoryStream();
                        streams.Add(ms);
                        return ms;
                    }, out warnings);
    
                foreach (System.IO.MemoryStream ms in streams)
                {
                    ms.Position = 0;
                }
                return streams;
            }
    
            /// <summary>LocalReprotから出力されたEMF形式の画像を画面上で100%になる大きさに直す</summary>
            public static System.Drawing.Imaging.Metafile GetDisplayImage(System.IO.MemoryStream stream, SizeF sizeCM)
            {
                stream.Position = 0;
                var metaOrginal = (System.Drawing.Imaging.Metafile)System.Drawing.Imaging.Metafile.FromStream(stream);
    
                using (Graphics g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
                {
                    var dpiX = g.DpiX; //正しいDPIが取得できないと縮尺がおかしくなります。
                    var dpiY = g.DpiY;
    
                    var hdc = g.GetHdc();
                    //画面のDPIに合わせてEMFを作り直す
                    RectangleF rect = new RectangleF(0, 0, sizeCM.Width / 2.54f * dpiX, sizeCM.Height / 2.54f * dpiY);
                    System.IO.MemoryStream ms = new System.IO.MemoryStream();
                    var metaNew = new System.Drawing.Imaging.Metafile(ms, hdc, rect, System.Drawing.Imaging.MetafileFrameUnit.Pixel, System.Drawing.Imaging.EmfType.EmfPlusDual);
    
                    using (System.Drawing.Graphics g2 = System.Drawing.Graphics.FromImage(metaNew))
                    {
                        g2.DrawImage(metaOrginal, rect);
                    }
                    return metaNew;
                }
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2017年10月29日 23:49 追記
    • 回答としてマーク googoo4200 2017年10月30日 1:24
    2017年10月29日 15:39

すべての返信

  • ディスプレイの解像度(DPI)の影響を受けて大きさが変わってしまうみたいです。
    AutoScaleModeを変えてみたり、DpiAwareを設定してやるともしかしたら治るかもしれません。

    あるいは自前で描画と印刷を処理してみるとか。
    追記:以下のコードだとDPIの受けたままになってますがマニフェストファイルでDpiAwareを設定すると正しい大きさになります。

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace ReportsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
    
                string rdlcFilePath = "Report1.rdlc";
                Microsoft.Reporting.WinForms.LocalReport report = new Microsoft.Reporting.WinForms.LocalReport();
                using (System.IO.FileStream fs = System.IO.File.OpenRead(rdlcFilePath))
                {
                    report.LoadReportDefinition(fs);
                }
    
                //report.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource("DataSet1", (System.Data.DataTable)table));
    
                var settings = report.GetDefaultPageSettings();
                SizeF pageCMSize = Tool.GetPageCMSize(rdlcFilePath);
                if (settings.IsLandscape)
                {
                    pageCMSize = new SizeF(pageCMSize.Height, pageCMSize.Width);
                }
                var originalEMFStreams = Tool.GetEMFStreams(report);
    
                this._DisplayMetafiles = new List<System.Drawing.Imaging.Metafile>();
                foreach (System.IO.MemoryStream stream in originalEMFStreams)
                {
                    _DisplayMetafiles.Add(Tool.GetDisplayImage(stream, pageCMSize));
                }
    
                PictureBox pictureBox1 = new PictureBox();
                pictureBox1.BackColor = Color.White;
                pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
                pictureBox1.Image = _DisplayMetafiles[0];
    
                NumericUpDown updown = new NumericUpDown();
                updown.Minimum = 0;
                updown.Maximum = _DisplayMetafiles.Count - 1;
                updown.ValueChanged += (s, e)=>
                {
                    pictureBox1.Image = _DisplayMetafiles[(int)updown.Value];
                };
                this.Controls.Add(updown);
    
    
                Panel panel = new Panel();
                panel.Top = updown.Height;
                panel.Height = this.ClientSize.Height - updown.Height;
                panel.Width = this.ClientSize.Width;
                panel.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
                panel.AutoScroll = true;
                panel.Controls.Add(pictureBox1);
                this.Controls.Add(panel);
    
                this.Shown += Form1_Shown;
            }
    
            private List<System.Drawing.Imaging.Metafile> _DisplayMetafiles;
    
            private void Form1_Shown(object sender, EventArgs e)
            {
                PrintDialog dlg = new PrintDialog();
                dlg.Document = new System.Drawing.Printing.PrintDocument();
    
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    int i = 0;
                    dlg.Document.PrintPage += (s, e2) =>
                    {
    
                        e2.Graphics.DrawImage(_DisplayMetafiles[i], Point.Empty);
                        i++;
                        e2.HasMorePages = i < _DisplayMetafiles.Count;
                    };
                    dlg.Document.Print();
                }
            }
    
            private void Document_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
            {
    
            }
        }
    
        class Tool
        {
            /// <summary>RDCLファイルに定義されている用紙サイズをcm単位で取得</summary>
            public static SizeF GetPageCMSize(string filePath)
            {
                using (System.IO.FileStream fs = System.IO.File.OpenRead(filePath))
                {
                    return GetPageCMSize(fs);
                }
            }
            /// <summary>RDCLファイルに定義されている用紙サイズをcm単位で取得</summary>
            public static SizeF GetPageCMSize(System.IO.Stream reportStream)
            {
                System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                doc.Load(reportStream);
    
                string xmlns = "http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition";
                System.Xml.XmlNamespaceManager manager = new System.Xml.XmlNamespaceManager(doc.NameTable);
                manager.AddNamespace("q", xmlns);
                System.Xml.XmlNode nodePage = doc.SelectSingleNode("q:Report/q:Page", manager);
                System.Xml.XmlNode nodeHeight = nodePage.SelectSingleNode("q:PageHeight", manager);
                System.Xml.XmlNode nodeWidth = nodePage.SelectSingleNode("q:PageWidth", manager);
    
                SizeF sz = new SizeF();
                sz.Width = GetCMLength(nodeHeight.InnerText);
                sz.Height = GetCMLength(nodeWidth.InnerText);
                return sz;
            }
    
            private static float GetCMLength(string text)
            {
                text = text.Trim().ToLower();
                if (text.EndsWith("cm"))
                {
                    return float.Parse(text.Substring(0, text.Length - 2));
                }
                else if (text.EndsWith("mm"))
                {
                    return float.Parse(text.Substring(0, text.Length - 2)) / 10;
                }
                else if (text.EndsWith("in"))
                {
                    return float.Parse(text.Substring(0, text.Length - 2)) / 2.54f;
                }
                else
                {
                    throw new NotSupportedException();
                }
            }
    
            /// <summary>LocalReprotをEMF形式の画像に出力</summary>
            public static List<System.IO.MemoryStream> GetEMFStreams(Microsoft.Reporting.WinForms.LocalReport report)
            {
                string devInfo = "<DeviceInfo><OutputFormat>EMF</OutputFormat></DeviceInfo>";
    
                Microsoft.Reporting.WinForms.Warning[] warnings;
                List<System.IO.MemoryStream> streams = new List<System.IO.MemoryStream>();
                report.Render("Image", devInfo
                    , (name, extension, encoding, mimeTYpe, willSeek) =>
                    {
                        var ms = new System.IO.MemoryStream();
                        streams.Add(ms);
                        return ms;
                    }, out warnings);
    
                foreach (System.IO.MemoryStream ms in streams)
                {
                    ms.Position = 0;
                }
                return streams;
            }
    
            /// <summary>LocalReprotから出力されたEMF形式の画像を画面上で100%になる大きさに直す</summary>
            public static System.Drawing.Imaging.Metafile GetDisplayImage(System.IO.MemoryStream stream, SizeF sizeCM)
            {
                stream.Position = 0;
                var metaOrginal = (System.Drawing.Imaging.Metafile)System.Drawing.Imaging.Metafile.FromStream(stream);
    
                using (Graphics g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
                {
                    var dpiX = g.DpiX; //正しいDPIが取得できないと縮尺がおかしくなります。
                    var dpiY = g.DpiY;
    
                    var hdc = g.GetHdc();
                    //画面のDPIに合わせてEMFを作り直す
                    RectangleF rect = new RectangleF(0, 0, sizeCM.Width / 2.54f * dpiX, sizeCM.Height / 2.54f * dpiY);
                    System.IO.MemoryStream ms = new System.IO.MemoryStream();
                    var metaNew = new System.Drawing.Imaging.Metafile(ms, hdc, rect, System.Drawing.Imaging.MetafileFrameUnit.Pixel, System.Drawing.Imaging.EmfType.EmfPlusDual);
    
                    using (System.Drawing.Graphics g2 = System.Drawing.Graphics.FromImage(metaNew))
                    {
                        g2.DrawImage(metaOrginal, rect);
                    }
                    return metaNew;
                }
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2017年10月29日 23:49 追記
    • 回答としてマーク googoo4200 2017年10月30日 1:24
    2017年10月29日 15:39
  • gekkaさん、ご回答ありがとうございます!
    また、ソースコードまで付けて解説頂き非常に参考になりました。

    ディスプレイの解像度(DPI)の影響を受けて大きさが変わってしまうみたいです。
    ドンピシャでした……ディスプレイの解像度に影響を受けていたとは盲点でした。
    ディスプレイの解像度が、「125%(推奨)」となっておりました。これを「100%」にすると、正しい大きさで出力されるようになりました。
    解決策がなかなか見つからないところでしたので、非常に助かりました。

    AutoScaleModeを変えてみたり、DpiAwareを設定してやるともしかしたら治るかもしれません。
    ソースコード上でも制御できるのですね。勉強になります。
    お客様先にアプリケーションをご提供する予定になっているので、環境要因で出力結果が変わらないよう、
    上記につきましても調べてソースの修正対応を行う所存です。
    必要に応じて上記自前の描画・印刷処理のソースコードを参考にさせていただきます。

    このたびはご回答いただきありがとうございます。
    今後もフォーラムに質問を投稿させていただくかもしれませんが、その際は何卒よろしくお願いいたします。


    2017年10月30日 1:35