none
ReportViewerで、プログラム上からPDF出力を中止するには? RRS feed

  • 質問

  • VB2008にて、ReportViewerを使用し、PDF出力をするプログラムを
    作成しました(レイアウト画面は表示せず、直接出力しています)。
    ただ、データ量が多い場合、出力に時間がかかるため、途中で出力を
    中止できるようにしたいのですが、可能でしょうか。

    イメージとしては、ReportViewerの「停止」ボタンの機能を、フォ
    ームのボタンに持たせたいと思っております。

    「ReportViewer、PDF、印刷、停止」といった単語で調べてみたの
    ですが、見つかりませんでした。

    方法をご存知の方がいらしたら、教えていただけますでしょうか。
    2010年7月31日 15:02

回答

  • 直接出力しているということはLocalReport.RenderでPDF出力しているのでしょうか?
    その場合、Renderメソッドの部分は別スレッドにしない限りキャンセルは出来ません。

    #しかもスレッドをAbortするしかキャンセルする方法はありません…

    CancelRenderingはReportViewerコントロールの機能でレンダリングするときのみ有効です。
    直接エクスポートしているならCancelRenderingは無意味です。

     

      Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
    
        Dim exp As ExportData = New ExportData()
        exp.LocalReport = ReportViewer1.LocalReport
        exp.Path = "Test.PDF"
        StartExport(exp)
      End Sub
    
      Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        CancelExport()
      End Sub
    
    
    
      Private Sub StartExport(ByVal exp As ExportData)
    
        CancelExport()
        Dim start As New System.Threading.ParameterizedThreadStart(AddressOf ExportPDF)
        renderingThread = New System.Threading.Thread(start)
        renderingThread.IsBackground = True
        renderingThread.Start(exp)
      End Sub
      Private Sub CancelExport()
    
        Dim thread As System.Threading.Thread = Me.renderingThread
        If (thread IsNot Nothing) Then
    
          thread.Abort()
          thread.Join()
        End If
        Me.renderingThread = Nothing
      End Sub
    
      Private renderingThread As System.Threading.Thread = Nothing
    
      Friend Class ExportData
        Public LocalReport As Microsoft.Reporting.WinForms.LocalReport
        Public Path As String
      End Class
    
      Private Sub ExportPDF(ByVal data As Object)
        Dim exp As ExportData = DirectCast(data, ExportData)
        Try
          Dim warnings As Microsoft.Reporting.WinForms.Warning() = Nothing
          Dim bs() As Byte = exp.LocalReport.Render("PDF", String.Empty, String.Empty, String.Empty, ".pdf", Nothing, warnings)
          Using fs As New System.IO.FileStream(exp.Path, System.IO.FileMode.Create, System.IO.FileAccess.Write)
            fs.Write(bs, 0, bs.Length)
          End Using
        Catch exLP As Microsoft.Reporting.WinForms.LocalProcessingException
        Catch exTA As System.Threading.ThreadAbortException
        Finally
          If (System.IO.File.Exists(exp.Path)) Then
            System.IO.File.Delete(exp.Path)
          End If
          Me.renderingThread = Nothing
        End Try
      End Sub
    

     

     


    • 回答としてマーク ZARAZARA 2010年8月4日 1:47
    2010年8月3日 9:26

すべての返信

  • 「ReportViewer、PDF、印刷、停止」といった単語で調べてみたの
    ですが、見つかりませんでした。

    CancelRendering で止まりませんでしたか?

    ReportViewer.CancelRendering メソッド
    http://msdn.microsoft.com/ja-jp/library/microsoft.reporting.winforms.reportviewer.cancelrendering(v=VS.80).aspx

    2010年7月31日 18:55
  • Chukiさん
    返事が遅くなってすみません。

    教えていただいたメゾットを元に、

    1.Form1に、開始、中止ボタン(CancelRendering)
    を設置
    2.Form2に、ReportViewerを設置
    3.Form1の開始ボタンクリックで、MDB接続、データを
    抽出する。
    4.ReportViewerを開き、抽出データをPDFファイルに
    出力。
    5.出力中、Form1の中止ボタンクリックで、PDF出力を
    中止。

    という流れのプログラムを作成しましたが、これだと
    PDF出力が終わってから中止ボタンクリックイベントが
    始まるため、意味がありません。

    For ~ Next等の構文なら、途中に「Application.DoEvents()」
    を入れてイベントを割り込ませることができるため、問題ないの
    ですが、ReportViewerではどのように割り込ませれば良い
    のでしょうか。

    苦し紛れにForm2にタイマーを設置し、1秒ごとに「Application.
    DoEvents()」を読むようにしてみましたが無反応でした(フォームが
    違うから?)。

    良い方法がございましたら、教えていただけますでしょうか。

    2010年8月2日 6:10
  • 実装方法に問題があるか、マルチスレッドを使うべきパターンかのいずれかだと思われますが、どういったコードを書いているか具体的に見えないため、判断できかねます。

    For ~ Next等の構文なら、途中に「Application.DoEvents()」
    を入れてイベントを割り込ませることができるため、問題ないの
    ですが

    Application.DoEvents はあくまで回避です。
    たいていの場合、別のやり方で解消できるため、「問題ない」と断言しないようにしてください。

    苦し紛れにForm2にタイマーを設置し、1秒ごとに「Application.
    DoEvents()」を読むようにしてみましたが無反応でした(フォームが
    違うから?)。

    理由が違います。
    おそらく、その印刷処理から抜けてこず、Application.DoEvents のようにメッセージを途中で処理する仕組みがないことから、それらの処理が終わるまで Tick イベントが呼び出されないのでしょう。

    Tick イベントや Click イベントは、メインスレッドと呼ばれるスレッドがほかの処理をしていないとき、またはメッセージを処理するための仕組みを動かしているときのみ、順番が回ってきます。(概念です。正確な表現ではありません)
    現状は、処理がずっと続いているため、順番が回ってこないと考えてください。
    なお、Tick イベントがくると言うことは、Application.DoEvents を改めて呼ぶ必要はありません。そのときは Click イベントもくる状態ですので。

    このあたりのメカニズムは、スレッドやメッセージループについて調べて学んでください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年8月2日 14:18
    モデレータ
  • 直接出力しているということはLocalReport.RenderでPDF出力しているのでしょうか?
    その場合、Renderメソッドの部分は別スレッドにしない限りキャンセルは出来ません。

    #しかもスレッドをAbortするしかキャンセルする方法はありません…

    CancelRenderingはReportViewerコントロールの機能でレンダリングするときのみ有効です。
    直接エクスポートしているならCancelRenderingは無意味です。

     

      Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
    
        Dim exp As ExportData = New ExportData()
        exp.LocalReport = ReportViewer1.LocalReport
        exp.Path = "Test.PDF"
        StartExport(exp)
      End Sub
    
      Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        CancelExport()
      End Sub
    
    
    
      Private Sub StartExport(ByVal exp As ExportData)
    
        CancelExport()
        Dim start As New System.Threading.ParameterizedThreadStart(AddressOf ExportPDF)
        renderingThread = New System.Threading.Thread(start)
        renderingThread.IsBackground = True
        renderingThread.Start(exp)
      End Sub
      Private Sub CancelExport()
    
        Dim thread As System.Threading.Thread = Me.renderingThread
        If (thread IsNot Nothing) Then
    
          thread.Abort()
          thread.Join()
        End If
        Me.renderingThread = Nothing
      End Sub
    
      Private renderingThread As System.Threading.Thread = Nothing
    
      Friend Class ExportData
        Public LocalReport As Microsoft.Reporting.WinForms.LocalReport
        Public Path As String
      End Class
    
      Private Sub ExportPDF(ByVal data As Object)
        Dim exp As ExportData = DirectCast(data, ExportData)
        Try
          Dim warnings As Microsoft.Reporting.WinForms.Warning() = Nothing
          Dim bs() As Byte = exp.LocalReport.Render("PDF", String.Empty, String.Empty, String.Empty, ".pdf", Nothing, warnings)
          Using fs As New System.IO.FileStream(exp.Path, System.IO.FileMode.Create, System.IO.FileAccess.Write)
            fs.Write(bs, 0, bs.Length)
          End Using
        Catch exLP As Microsoft.Reporting.WinForms.LocalProcessingException
        Catch exTA As System.Threading.ThreadAbortException
        Finally
          If (System.IO.File.Exists(exp.Path)) Then
            System.IO.File.Delete(exp.Path)
          End If
          Me.renderingThread = Nothing
        End Try
      End Sub
    

     

     


    • 回答としてマーク ZARAZARA 2010年8月4日 1:47
    2010年8月3日 9:26
  • 直接出力しているということはLocalReport.RenderでPDF出力しているのでしょうか?
    その場合、Renderメソッドの部分は別スレッドにしない限りキャンセルは出来ません。

    #しかもスレッドをAbortするしかキャンセルする方法はありません…

    CancelRenderingはReportViewerコントロールの機能でレンダリングするときのみ有効です。
    直接エクスポートしているならCancelRenderingは無意味です。


    おっしゃるとおりで、Renderコマンドの場合ダメでした。ZARAZARAさんむだな検証時間を使わせてしまい申し訳ございませんでした。
    gekkaさん、フォローありがとうございました

    2010年8月3日 13:39
  • gekkaさん

    サンプルプログラム、ありがとうございます。
    こちらとしては、単純にReportViewerのレイアウト画面に
    ある「停止」ボタンの機能をプログラムに付け加えるだけ
    だと思っておりましたが、そんな簡単にはいかないのですね。
    サンプルプログラムを参考に、別スレッドの勉強をするように
    致します。


    Chukiさん

    こちらの知識不足で、うまくそちらに状況が伝わらなかった
    だけですので、こちらこそすみませんでした。
    おっしゃるとおり、別スレッドの概念をきちんと理解できるよう、
    勉強いたします。


    お二人とも、ご回答ありがとうございました。
    2010年8月4日 1:47