none
再び/DataGridViewの印刷で改ページを設定するとき RRS feed

  • 質問

  • http://social.msdn.microsoft.com/Forums/ja-JP/csharpexpressja/thread/304b370c-4194-49ad-b61c-df28b3c36743 の続きです。

    TH01さん,今までいろいろとご教授をいただきありがとうございました。
    お陰さまで当所の念願のとおり,縦横罫線付きのグループ毎の表を印刷できるようになりました。検討すべきところは残ってはいるのですが改ページもできるようになりました。
    
    <改善すべきこと>
    グループの表が2ページにまたがってしまうとき,
    ア [あかしあ]班           
      -------------------------------------
      のように下線を残して残りを次ページに印刷するために
    イ 次ページの先頭が
      |  |  |  |  |
      -------------------------------
       のように最上部に横線が表示されない。
    
    また,改ページとは関係ないのですが,
    ウ プレビュー画面を閉じた直後に再びプレビュー画面を開くと,前回に見たデータの前に同じデータが追加されて表示されてしまう不具合が生じています。
    
    アとイについては報告のつもりです。ウについての対策は是非ともお聞かせ願いたいと思います。どうぞよろしくお願いします。
    

     

    //印刷データ
    private string[,] hyoji = new string[500, 9];
    private string [,] prtData=new string [3 ,6];
    private int rowCnt = 0;
    private int page = 1;
    //改ページ変数
    int rnum;
    int maxRow = 45;
    //グループの開始行と終了行など
    private int[] sRow = new int[2];  //先頭
    private int[] eRow = new int[2];  //最後
    private int[] hRow = new int[2];  //班名行
    private int[] mRow = new int[2];  //見出し行
    private int[] kRow = new int[2];  //入金予定行
    //描画する行の合計
    private int rowSum = 0;
    
    private void btnPrint_Click(object sender, EventArgs e)
    {
      sRow = new int[hanmei.Length]; //グループの開始行
      eRow = new int[hanmei.Length]; //グループの終了行
      hRow = new int[hanmei.Length]; //グループの班名行
      mRow = new int[hanmei.Length]; //グループの見出し行
      kRow = new int[hanmei.Length]; //グループの入金予定行
      for (int i = 0; i <hanmei.Length; i++)
      {
        dvGroup.RowFilter = "group='" + hanmei[i] + "'";  
        rowCnt = suitoDataGridView.RowCount;//班のレコード数
        sRow[i] = rowSum;
        eRow[i] = rowSum + rowCnt + 3;
    
        toRead(i, sRow[i], eRow[i]);    //データ整形
        rowSum += rowCnt + 4;        //総レコード数
      }
      for (int i = 0; i < hanmei.Length; i++)
      {
        hRow[i] = sRow[i] + 1;
        mRow[i] = sRow[i] + 2;
        kRow[i] = sRow[i] + 3;
      }
      System.Drawing.Printing.PrintDocument pd =
          new System.Drawing.Printing.PrintDocument();
      pd.PrintPage +=
        new System.Drawing.Printing.PrintPageEventHandler(printDocument1_PrintPage);
      PrintDialog pdlg = new PrintDialog();
      pdlg.Document = printDocument1;
      if (pdlg.ShowDialog() == DialogResult.OK)
        printDocument1.Print();
    }
    
    private void leftPad(int i, int j, int s)
    {
      hyoji[i, j] = suitoDataGridView[j + 1, i - s - 4].Value.ToString().PadLeft(2);
    }
    
    private void toRead(int h, int s, int e)
    {
      //印刷用データを整形
      int inYotei = 0;  //納入予定額
      int inSum = 0;   //累積納入額
      int inKin = 0;   //入金額
    
      for (int i = s; i < e + 1; i++)
      {
        if (i == s)
        {
          for (int j = 0; j < 7; j++)
          {
            hyoji[i, j] = "";
          }
        }
        else if (i == s + 1)
        {
          for (int j = 0; j < 7; j++)
          {
            hyoji[i, j] = "";
            hyoji[i, 0] = "[" + hanmei[h] + "]班";
          }
        }
        else if (i == s + 2)
        {
          hyoji[i, 0] = "月";
          hyoji[i, 1] = "日";
          hyoji[i, 2] = " 入金額";
          hyoji[i, 3] = "  摘  要";
          hyoji[i, 4] = " 納入額";
          hyoji[i, 5] = " 未納額";
        }
        else if (i == s + 3)
        {
          for (int j = 0; j < 7; j++)
          {
            hyoji[i, j] = "";
          }
          hyoji[i, 3] = "       納入予定額";
          hyoji[i, 4] = dgvSum[1, h].Value.ToString();
          inYotei = int.Parse(hyoji[i, 4]);
          hyoji[i, 4] = inYotei.ToString("#,###").PadLeft(2);
        }
        else
        {
          int pp = 0;
          for (int j = 0; j < 4; j++)
          {
            if (j > 1) pp = 1; //班名は読み込まない
            switch (j)
            {
              case 0:
              case 1:
                leftPad(i, j, s);
                break;
              default:
                hyoji[i, j] = suitoDataGridView[j + 1 + pp, i - s - 4].Value.ToString();
                break;
            }
          }
        }
      }
      for (int i = s; i < e + 1; i++)
      {
        if (i > s + 3)
        {
          inKin = int.Parse(hyoji[i, 2]);
          inSum += inKin;
          hyoji[i, 2] = inKin.ToString("#,###").PadLeft(10); 
          hyoji[i, 4] = inSum.ToString("#,###").PadLeft(10);
          hyoji[i, 5] = (inYotei - inSum).ToString("#,###").PadLeft(10);
       }
      }
    }
    
    private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
    {
      drawLine(e);  //データの表を描く
    }
    
    private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
    {
      rnum = 0;
      page = 1;
    }
    
    private void drawLine(System.Drawing.Printing.PrintPageEventArgs e)
    {
      Font f = new Font("MS ゴシック", 11);
      int pitch = f.Height + 7;  //行ピッチ
      int x = 110;        //印刷開始位置
      int y = 110;        //印刷開始位置
      int Apage;         //ページ番号
      int cnt = 0;
    
      int leftX = x;
      int topY = y;
      int lineY;
      int lastlineY;
      int[] moveX = new int[7];  //縦罫線の位置
      int[] moveS = new int[7];  //文字列の位置
      moveX[0] = x;
      moveX[1] = moveX[0] + 23;
      moveX[2] = moveX[1] + 23;
      moveX[3] = moveX[2] + 88;
      moveX[4] = moveX[3] + 200;
      moveX[5] = moveX[4] + 88;
      moveX[6] = moveX[5] + 88;
      for (int s = 0; s < 7; s++)
      {
        moveS[s] = moveX[s] + 2;
      }
      int rectWidth = moveX[6] - x;
      int rightX = moveX[6];
    
      //セルの色(見出し&総計)
      HatchBrush hBrushOO = new HatchBrush(HatchStyle.LightDownwardDiagonal, Color.SaddleBrown, Color.White);
      HatchBrush hBrushOs = new HatchBrush(HatchStyle.LightDownwardDiagonal, Color.Coral, Color.White);
      HatchBrush hBrushOg = new HatchBrush(HatchStyle.LightDownwardDiagonal, Color.Moccasin, Color.White);
    
      //標題
      f = new Font("MS ゴシック", 12);
      e.Graphics.DrawString("会 費 収 納 簿", f, Brushes.Black, x + 150, y - 50);
    
      //日付
      f = new Font("MS 明朝", 9);
      System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("ja-JP");
      System.Globalization.DateTimeFormatInfo dtfi = ci.DateTimeFormat;
      dtfi.Calendar = new System.Globalization.JapaneseCalendar();
      System.DateTime dTime = System.DateTime.Now;
      e.Graphics.DrawString(dTime.ToString("ggy年M月d日 現在", dtfi), f, Brushes.Black, rightX - 138, y - 20);
    
      //ページ番号
      f = new Font("MS ゴシック", 10);
      Apage = (rowSum % maxRow == 0) ? rowSum / maxRow : rowSum / maxRow + 1;
      e.Graphics.DrawString(page + "/" + Apage + "page", f, Brushes.Black, rightX - 55, y - 60);
    
      f = new Font("MS 明朝", 11);
      int dataY = y;
    
      do
      {
        if (rnum <= rowSum - 1)
        {
          lineY = dataY + f.Height - 1;
          lastlineY = 0;
          //見出し行塗潰しとフォント指定
          if (Array.IndexOf(mRow, rnum) != -1)
          {
            e.Graphics.FillRectangle(hBrushOO, leftX, lineY - 16, rectWidth, pitch - 2);
            f = new Font("MS ゴシック", 11);
          }
          else
          {
            f = new Font("MS 明朝", 11);
          }
          //下線-空白行以外
          if (Array.IndexOf(sRow, rnum) == -1)
          {
            e.Graphics.DrawLine(Pens.Black, leftX, lineY + 5, rightX, lineY + 5);
            lastlineY = lineY + 5;
          }
          //グループ毎の合計を強調する塗潰し
          if (Array.IndexOf(eRow, rnum) != -1)
          {
            e.Graphics.FillRectangle(hBrushOs, moveX[4], lineY + 7 - pitch, 88 * 2, pitch -3);
          }
          
          //データを描画
          for (int i = 0; i < 6; i++)
          {
            //入金予定行は特別色
            if (Array.IndexOf(kRow, rnum) != -1)
              e.Graphics.DrawString(hyoji[rnum, i], f, Brushes.Coral, moveS[i], dataY);
            else
              e.Graphics.DrawString(hyoji[rnum, i], f, Brushes.Black, moveS[i], dataY);
          }
          //縦線
          Pen thinPen = new Pen(Color.Black, 0.3F);
          if (Array.IndexOf(sRow, rnum) == -1 && Array.IndexOf(hRow, rnum) == -1)
          {
            for (int i = 0; i < 7; i++)
            {
              if (i >= 1 && i <= 5)
                e.Graphics.DrawLine(thinPen, moveX[i], lastlineY - pitch, moveX[i], lastlineY);
              else 
                e.Graphics.DrawLine(Pens.Black, moveX[i], lastlineY - pitch, moveX[i], lastlineY);
            }
          }
        }
        dataY += pitch;
        rnum++;
        cnt++;
        //改ページの確認
        if (cnt >= maxRow)
        {
          e.HasMorePages = true;
          page++;
          break;
        }
    
      } while (rnum <= rowSum - 1);
      //最終ページの罫線
      if (rnum == rowSum - 1)
      {
        e.HasMorePages = false;
      }
    }
    
    

    zen73
    2010年8月30日 8:19

回答

  • コードはほとんど見られていませんけど、
    ウの話は rowSum が2回目に初期化されない点があやしいと思いました。

    前のスレッドの方にも、これから返信します。

    追記:
    一応、前のスレッドに続きのURLを書いてきたので、こちらに返信しますね。

    • 回答としてマーク zen73 2010年8月31日 8:03
    2010年8月30日 10:20

すべての返信

  • コードはほとんど見られていませんけど、
    ウの話は rowSum が2回目に初期化されない点があやしいと思いました。

    前のスレッドの方にも、これから返信します。

    追記:
    一応、前のスレッドに続きのURLを書いてきたので、こちらに返信しますね。

    • 回答としてマーク zen73 2010年8月31日 8:03
    2010年8月30日 10:20
  • zen73 さんのコードの話じゃないので、このスレッドに返信しない方がいいかなと迷ったのですが、スレッドの最終日時が逆転するとややこしそうなのでこちらにさせてもらいます。
    それと、せっかくコーディングされているのに、全然別な方法を書いてしまい、すみません。

    前のスレッドからの引用です。
    > なお,私の環境は VS 2008pro です。

    あ、そうなんですか。
    ここは Express Edition のフォーラムですから、てっきり Express だと思ってました。
    pro でしたらお知らせできますね。日が空いちゃいましたけど。

    以下は「Microsoft レポート」を使用した手順です。
    わかりずらい点もあるかもしれませんけど、試してもらえればうれしいです。
    (ざっとみて、簡単そう、と思ってもらうだけでもいいです。返信できただけで私は満足ですから(^^;)
    ※Express Edition ではこの機能は使えません。念のため。

    まず、私が目指したレポートのレイアウトは次のものです。

    [A] group
    入金額 摘 要 納入額 未納額
    8 27 101 ああ 10 1
    8 28 102   10 1
    8 29 103   10 1
        306   30 3

    [B] group
    入金額 摘 要 納入額 未納額
    8 28 201   10 1
    8 29 202   10 1
        403   20 2

    上記を目指して、以下の手順で作成しました。

    ★0. プロジェクトを新規作成
    ★1. レポートで使用するデータの型を定義

    1. [プロジェクト]→[新しい項目の追加] で「データセット」を追加
    2. データセットデザイナで右クリックして [追加]→[DataTable]
    3. DataTable1 で右クリックして [追加]→[列]
    4. 6列分、追加
    5. DataColumn1 ~ DataColumn6 の部分をそれぞれ次の値に変更
      "group"、"日付", "入金額", "摘要", "納入額", "未納額"
      … 年月を分けていませんが、この手順ではこうしています。
    6. それぞれの DataType を次の値に変更(先頭は System.)
      String, DateTime, Decimal, String, Decimal, Decimal
    ★2. レポートのレイアウト
    1. [プロジェクト]→[新しい項目の追加] で「レポート」を追加
    2. [ツールボックス] から「テーブル」をレポート上に配置
    3. テーブルの上部灰色の箇所を右クリックして [右に列を追加]
    4. 6列になるように3列追加
    5. [データソース] ウィンドウの DataTable1 を展開して、「日付」 をテーブルの「詳細」の行の左端にドラッグ&ドロップ。
      2つ目の列にも「日付」をドラッグ&ドロップ。
      残りの列には残りの項目をドラッグ&ドロップ。
    6. 列の幅を適当に調整する。
    7. 「日付」の1つ目のセルを選択し、プロパティの Format を "MM" にする。
      2つ目の「日付」の Format は "dd" にする。
      残りの金額のセルでは、Format を "#,##0" にする。
    8. グルーピングの設定([group] 項目をグルーピング条件にする)
      1. テーブルの左端の灰色の箇所を右クリックして [グループの挿入]
      2. [グループ化の条件] の1行目を選択するとドロップダウンになるので、
        「=Fields!group.Value」を選択して [OK] を押して画面を閉じる。
        すると、グルーピング結果用の行が「詳細」の上下に追加される。
        … 上側のがグループヘッダー用の行、下側のがグループフッター用の行です。
      3. グループヘッダー用の行のセルを全部選択して右クリックして [セルの結合]
      4. 結合したセルに
        =Format(Fields!group.Value, "[{0}] group")
        と入力する。
      5. グループフッター用の行(「フッター」行の上)の「入金額」列のセルに
        =Sum(Fields!入金額.Value, "table1_Group1")
        と入力する。
        … "table1_Group1" は、[グループ化の条件] を指定した画面の上部に表示されてた値です。その画面でわかりやすい名前に変更しておいてもいいです。
      6. 「納入額」と「未納額」も集計値用の値を入力する。
        … Field! の後ろをそれぞれの項目名に変えます。
      7. グループヘッダーとグループフッターの行のセルを全部選択して、
        プロパティの Font.FontStyle を "Italic"、Font.FontWeight を "Bold" に変更
      8. グループヘッダーの行の左端の灰色のところで右クリックして、[下に行を挿入]
      9. 追加された行に左のセルからそれぞれ次の値を入力
        "月", "日", "入金額", "摘 要", "納入額", "未納額"
        Font.FontStyle は "Normal" にしておく。
        … グループフッターの下に列名が印刷されるようにします。
    9. 1行目の左端で右クリックして [行の削除]
      最終行の「フッター」行の空行も同様に削除
    10. 各セルの TextAlign を "Left" とか "Center" とか "Right" にする。
    11. きれいな罫線枠になるように、各セルの BorderStyle の Left とか Right とかを "Solid" などに変更する。
    12. レポートの「本文」の白い部分が「テーブル」にぴったりになるようにサイズをそろえる。
    ★3. データの準備などのコーディング
    1. [ツールボックス] から「レポート」カテゴリにある「MicrosoftReportViewer」をフォームに配置する。
    2. [ReportViewer タスク] にある [レポートの選択] で
      "WindowsFormsApplication1.Report1.rdlc"
      を選択する。
    3. reportViewer1 の Doc プロパティを "Fill" にする。
    4. Form1_Load イベントに以下のコードを貼り付ける。
      var dt = DataSet1.DataTable1;
      dt.Rows.Add("A", DateTime.Now.Date.AddDays(-3), 101, "ああ", 10, 1);
      dt.Rows.Add("A", DateTime.Now.Date.AddDays(-2), 102, "", 10, 1);
      dt.Rows.Add("A", DateTime.Now.Date.AddDays(-1), 103, "", 10, 1);
      dt.Rows.Add("B", DateTime.Now.Date.AddDays(-2), 201, "", 10, 1);
      dt.Rows.Add("B", DateTime.Now.Date.AddDays(-1), 202, "", 10, 1);
      dt.Rows.Add("C", DateTime.Now.Date.AddDays(-5), 301, "", 10, 1);
      dt.Rows.Add("C", DateTime.Now.Date.AddDays(-4), 302, "いいい", 10, 1);
      dt.Rows.Add("C", DateTime.Now.Date.AddDays(-3), 303, "", 10, 1);
      dt.Rows.Add("C", DateTime.Now.Date.AddDays(-2), 304, "", 10, 1);
      dt.Rows.Add("C", DateTime.Now.Date.AddDays(-1), 305, "", 10, 1);
      reportViewer1.RefreshReport();

    プロジェクトを保存してから実行してください。
    きれいなレポートは表示されました?
    罫線の部分とかの詳細手順は省略していますのでそのあたり心配ですが、セルごとに細かく設定できるので、いろいろ試していただければと思います。

    (私のコードをアップするのは、本当に意味がないと思いましたので、自粛します。。)

    2010年8月30日 12:01
  • 以下は「Microsoft レポート」を使用した手順です。わかりずらい点もあるかもしれませんけど、試してもらえればうれしいです。

    詳細に手順をお示しいただきました。今朝本屋で購入した「VisualC#2008逆引き大全 データベース+印刷/帳票編」「VisualBasic2005による実践データベースプログラミング」と併せていろいろとやってみたのですが便利さが実感できました。

    ウの話は rowSum が2回目に初期化されない点があやしいと思いました。。

        private void btnPrint_Click(object sender, EventArgs e)
            {
                rowSum = 0;

        }

    とすることで不具合を解消することができました。TH01さん,本当にありがとうございました。

     

     


    zen73
    2010年8月31日 8:03
  • イ の不具合も解消することができました。

    //2頁以降の先頭に横線
    if(page >1 && Array.IndexOf(sRow, rnum) == -1 && Array.IndexOf(hRow, rnum) == -1)
      e.Graphics.DrawLine(Pens.Black, leftX, y - 3, rightX, y - 3);
    do
          {
    } while (rnum <= rowSum - 1);
    

    zen73
    2010年8月31日 10:07