none
Graphicsでの線の描き順 RRS feed

  • 質問

  • 初めて、Graphicsオブジェクトを使用します.

    次のコードでFormに配置したPictureBoxにx軸とy軸を描きたいのですが、一度描かれた軸が消えてしまいます。

    消えない方法をお願いいたします。

     

    Dim gr as Graphics
    Dim myMatrix As New Matrix(1, 0, 0, -1, 0, 0)

    Dim Wd, hg, Khg as Single

    Dim jkn as String="平均+合計"

     

    Private Sub PictureBx_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBx.Paint

    Dim br As Brush = Brushes.DeepPink
            gr = PictureBx.CreateGraphics()
            Wd = PictureBx.Width : hg = PictureBx.Height
            Khg = hg / 5 * 3
            GrafUsInit()
             ' GrafUsStart()

    End Sub

     

    Private Sub GrafUsInit()
            Dim br As Brush
            If InStr(jkn, "平均") Then br = Brushes.LightBlue Else br = Brushes.LightGreen
            gr.FillRegion(br, gr.Clip)
            gr.Transform = myMatrix
            gr.TranslateTransform(Wd * 0.95, Khg, MatrixOrder.Append)
            gr.DrawLine(Pens.White, 0, -hg, 0, hg)
            gr.DrawLine(Pens.Blue, -Wd, 0, Wd, 0)
    End Sub

     よろしくお願いします。

     

    2008年3月30日 14:07

回答

  • CreateGraphics するのではなく、e.Graphics を使用してください。

    またフィールドに Graphics を置くのはお勧めしません。描画処理を GrafUsInit メソッドに独立させたいなら、Graphics を引数に渡せるようにすべきです。そのほかのものもわざわざフィールドに配置する必要はなさそうに見えます。

    2008年3月30日 14:20
  • Hongliangさん、ご回答有難うございます。

    Paintメソッドにe.graphicsを使用した場合、マウスのクリックイベントPictureBx_MouseUpでgrがハンドルされないので

    PictureBx_MouseUpイベントで再度e.graphicsを宣言してグラフ領域を再定義しなければなりません。

    その為、PictureBoxのCreateGraphicsを使用しました。

     この場合、どのようにすると良いでしょうか?

            Dim inx As Short
        Private Sub PictureBx_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBx.MouseUp
             ・・・・・・・・・・・

    ・・・・・・・・・・・

    '    折れ線グラフ

            dispgraf(i)
        End Sub

     

    Private Sub dispgraf(ByRef i As Short)
        ・・・・・・・・・・・・・

    ・・・・・・・・・・・・・

            For jx = 1 To P(i).ds : jh = J(i, jx).nn
                Dy = CDate(Mid(jh, 1, 4) & "/" & Mid(jh, 6, 2) & "/" & J(i, jx).hi)
                x = Int(DateDiff(Microsoft.VisualBasic.DateInterval.Day, now1, Dy) * Xritu) '  X は日付間隔です
                Y = (J(i, jx).h - F.thk) * Yritu  '  Y 軸の値です
                If J(i, jx).LV >= 0 Then
                    br = Brushes.Black
                    pn = Pens.Black
                Else
                    br = Brushes.Red
                    pn = Pens.Red
                End If

    '  折れ線グラフを描く

    '   ここで変数gr がモジュールレベルで宣言しているのにハンドルされないエラーが出現します
                If jx = 1 Then
                    gr.FillEllipse(br, x - 5, y - 5, 10, 10)  ' ここでエラーが出現します
                Else
                    gr.DrawLine(Pn, x1, y1, x, Y)
                    gr.FillEllipse(br, x - 5, y - 5, 10, 10)
                End If
                x1 = x : y1 = Y
            Next
            indx = i
        End Sub

     

    よろしくお願いします。

     

    2008年3月30日 15:08
  • 外池と申します。

     

    画面の表示に対してマウス操作をしながらグラフィカルな表示を行う場合には、ガラリと発想を変えないと難しいかもしれません。

     

    基本的に、Graphicsを使って描画する操作は、「全て」、FormのPaintイベントなり、PictureBoxのPaintイベントなりを使って行うように徹底してください。それ以外の場所でGraphicsを創って(Createして)描画することはしないほうが良いです。

     

    本当にそんなことが可能なのか? ということになりますが、次のような考え方でいかがでしょうか?

     

    1) 画面に表示するものばかりを集めた変数なり、コレクションなりを用意します。今回の場合は、線なのであれば、線の開始点と終了点の座標ばかりを集めた配列でも構いません。

    2) マウス操作で新たに表示する線が発生した時には、マウスイベントで、座標点の設定だけしてやります。その上で、FormやPictureBoxのInvalidateなり、Refreshなりをしてやります。(これで自動的に次のPaintイベントが発生します。)

    3) Paintイベントで、設定された座標点を使って全ての線を描きます。ミソは、Paintイベントが発生する度に、全てを描き直すようなプログラムにしておくことです。

     

    最後の3)の「ミソ」の部分は、無駄なことではないか? と思われるかもしれませんが、グラフが出ている画面は、別のアプリのウィンドウで覆われてしまうこともあるわけで、その別のアプリが終了してグラフが再び前面に現れたときは、全てを描き直さないといけません。

     

    で、2)のマウス操作では、「全て」に含まれるもの(線など)を新たに加えたり、取り除いたりするだけのプログラムにしておき、あとはPaintイベントに任せるようにするわけです。

     

    参考になれば、幸いです。

     

     

    2008年3月30日 23:53
  • 外池様、大変ご丁寧な説明有難うございます。

    Net初心者の私には大変参考になりました。

    今後ともよろしくお願いいたします。

     

    今回、ご指摘のようにMouseのクリックにて折れ線グラフを次から次へとサンプルデータを変えて表示したかったので

    1・Graphicsオブジェクトに座標の設定及びX軸Y軸の描画(Form_Loadイベントから立ち上げ)

    2・PictureBoxのクリックにてデータの順次表示(Mouse_Upイベントにて)

    と言う構想だったんですが、これは駄目と言うことですね。判りました。

     

    各イベントから作成したGraphicsオブジェクトのモジュール内で有効性が保障されないと言うこと(ガベージ?)。

    また、Graphicsオブジェクトを発生するイベントの種類が多く、それぞれ独立している事が判りました。

     

     ご教授の2)マウスクリックイベントから全てをPaintイベントに渡してそこで全てを処理するように変更してやって見ます。

    作成に少し時間が掛かるので、しばらくのご猶予をお願いします。

     

    2008年3月31日 11:35

すべての返信

  • CreateGraphics するのではなく、e.Graphics を使用してください。

    またフィールドに Graphics を置くのはお勧めしません。描画処理を GrafUsInit メソッドに独立させたいなら、Graphics を引数に渡せるようにすべきです。そのほかのものもわざわざフィールドに配置する必要はなさそうに見えます。

    2008年3月30日 14:20
  • Hongliangさん、ご回答有難うございます。

    Paintメソッドにe.graphicsを使用した場合、マウスのクリックイベントPictureBx_MouseUpでgrがハンドルされないので

    PictureBx_MouseUpイベントで再度e.graphicsを宣言してグラフ領域を再定義しなければなりません。

    その為、PictureBoxのCreateGraphicsを使用しました。

     この場合、どのようにすると良いでしょうか?

            Dim inx As Short
        Private Sub PictureBx_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBx.MouseUp
             ・・・・・・・・・・・

    ・・・・・・・・・・・

    '    折れ線グラフ

            dispgraf(i)
        End Sub

     

    Private Sub dispgraf(ByRef i As Short)
        ・・・・・・・・・・・・・

    ・・・・・・・・・・・・・

            For jx = 1 To P(i).ds : jh = J(i, jx).nn
                Dy = CDate(Mid(jh, 1, 4) & "/" & Mid(jh, 6, 2) & "/" & J(i, jx).hi)
                x = Int(DateDiff(Microsoft.VisualBasic.DateInterval.Day, now1, Dy) * Xritu) '  X は日付間隔です
                Y = (J(i, jx).h - F.thk) * Yritu  '  Y 軸の値です
                If J(i, jx).LV >= 0 Then
                    br = Brushes.Black
                    pn = Pens.Black
                Else
                    br = Brushes.Red
                    pn = Pens.Red
                End If

    '  折れ線グラフを描く

    '   ここで変数gr がモジュールレベルで宣言しているのにハンドルされないエラーが出現します
                If jx = 1 Then
                    gr.FillEllipse(br, x - 5, y - 5, 10, 10)  ' ここでエラーが出現します
                Else
                    gr.DrawLine(Pn, x1, y1, x, Y)
                    gr.FillEllipse(br, x - 5, y - 5, 10, 10)
                End If
                x1 = x : y1 = Y
            Next
            indx = i
        End Sub

     

    よろしくお願いします。

     

    2008年3月30日 15:08
  • 外池と申します。

     

    画面の表示に対してマウス操作をしながらグラフィカルな表示を行う場合には、ガラリと発想を変えないと難しいかもしれません。

     

    基本的に、Graphicsを使って描画する操作は、「全て」、FormのPaintイベントなり、PictureBoxのPaintイベントなりを使って行うように徹底してください。それ以外の場所でGraphicsを創って(Createして)描画することはしないほうが良いです。

     

    本当にそんなことが可能なのか? ということになりますが、次のような考え方でいかがでしょうか?

     

    1) 画面に表示するものばかりを集めた変数なり、コレクションなりを用意します。今回の場合は、線なのであれば、線の開始点と終了点の座標ばかりを集めた配列でも構いません。

    2) マウス操作で新たに表示する線が発生した時には、マウスイベントで、座標点の設定だけしてやります。その上で、FormやPictureBoxのInvalidateなり、Refreshなりをしてやります。(これで自動的に次のPaintイベントが発生します。)

    3) Paintイベントで、設定された座標点を使って全ての線を描きます。ミソは、Paintイベントが発生する度に、全てを描き直すようなプログラムにしておくことです。

     

    最後の3)の「ミソ」の部分は、無駄なことではないか? と思われるかもしれませんが、グラフが出ている画面は、別のアプリのウィンドウで覆われてしまうこともあるわけで、その別のアプリが終了してグラフが再び前面に現れたときは、全てを描き直さないといけません。

     

    で、2)のマウス操作では、「全て」に含まれるもの(線など)を新たに加えたり、取り除いたりするだけのプログラムにしておき、あとはPaintイベントに任せるようにするわけです。

     

    参考になれば、幸いです。

     

     

    2008年3月30日 23:53
  • 外池様、大変ご丁寧な説明有難うございます。

    Net初心者の私には大変参考になりました。

    今後ともよろしくお願いいたします。

     

    今回、ご指摘のようにMouseのクリックにて折れ線グラフを次から次へとサンプルデータを変えて表示したかったので

    1・Graphicsオブジェクトに座標の設定及びX軸Y軸の描画(Form_Loadイベントから立ち上げ)

    2・PictureBoxのクリックにてデータの順次表示(Mouse_Upイベントにて)

    と言う構想だったんですが、これは駄目と言うことですね。判りました。

     

    各イベントから作成したGraphicsオブジェクトのモジュール内で有効性が保障されないと言うこと(ガベージ?)。

    また、Graphicsオブジェクトを発生するイベントの種類が多く、それぞれ独立している事が判りました。

     

     ご教授の2)マウスクリックイベントから全てをPaintイベントに渡してそこで全てを処理するように変更してやって見ます。

    作成に少し時間が掛かるので、しばらくのご猶予をお願いします。

     

    2008年3月31日 11:35
  • 外池様、Hongliang様、今回は大変お世話になりました。

    ご教授の通りアルゴリズムを変更し、当方の意図するものにすることが出来ました。

    有難うございました。

    ちなみに作成したPaintイベントのコードです。

     

    Dim init as Boolean = True

     

        Private Sub PictureBx_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBx.Paint
            Dim Gr As Graphics = e.Graphics
            Dim i As Integer

            If init = True Then
                i = AJ(inx)
                GrafUmaInit(Gr)  ' 初期化 図形領域設定、座標作成
                GrafumaStart(i, gr) '  inxの最初のグラフを描画
                init = False           '  2度目に入ってこなくする
            Else
                GrafUmaInit(Gr)           ' 初期化 図形領域設定、座標作成
                dispShadow(indx, Gr)   '  前に描いたinxのグラフを薄い色に描き換え
                i = AJ(inx)
                GrafumaStart(i, Gr)       '  次のinxのグラフを描画
            End If
        End Sub

       inxはMouse_Upイベントで変更、その他の変数はLoadイベントで変更しました。

     

    当方、問題山積の為、今後ともお世話に成ると思いますがよろしくご指導お願いいたします。

     

    ---ネット移行者---

    2008年3月31日 14:04