DOBON.NET DOBON.NETプログラミング掲示板過去ログ

Chartコントロールでマウス位置から複数のSeriesの値を取得したい

環境/言語:[VB.Net2012/Windows7]
分類:[.NET]

Chartコントロールに関して、知恵を貸して頂きたく思い、
投稿させて頂きました。


【実現したいこと】
4つのSeriesをもつchartがあります。
X軸は日付です。

マウスの現在位置にあるX軸の値(日付)と、
対応する4つのSereiesの値を取得して、
Chart外のラベルに表示したいです。

(マウス位置がseriesの上にない場合も表示したいので、
GetToolTipTextイベントではダメです)


【自分で試したこと】
マウス位置から、簡単にデータポイントを取得する方法が見つからず、
強引に後述のようなコードでやりたいことは実現できました。

(概要)
1.マウス座標のY値を変化させて、PointIndexが取得できるまでHitTestを行う
2.上記で取得したPointIndexに対する各Seriesの値をラベルに表示する



【課題・解決したいこと】
自分で作成したコードで課題に感じていることは、2点。

●CPU負荷が高いことと
●スマートさに欠けること

上記2点を解決するため、
もっと良い方法がないかと検討しています。


ご教示のほど、よろしくお願い致します。



Private Sub Chart1_MouseMove(sender As Object, e As MouseEventArgs) Handles Chart1.MouseMove

    Try
        'マウスに合わせて、X軸に垂直な縦線を表示
        Dim mousePoint As Point = New Point(e.X, e.Y)
        Chart1.ChartAreas(0).CursorX.SetCursorPixelPosition(mousePoint, True)


        'マウス座標でHitTestを行う
        Dim htResult As HitTestResult = Chart1.HitTest(mousePoint.X, mousePoint.Y)

        If htResult.PointIndex <= 0 Then
            'PointIndexが取得できるまでY座標を変化させて、HitTestを行う
            For i = 0 To Chart1.Height
                htResult = Chart1.HitTest(mousePoint.X, i)

                If htResult.PointIndex > 0 Then
                    'PointIndexが取得できたら、ループを抜ける
                    Exit For
                End If
            Next
        End If


    'PointIndexに対する各Seriesの値をラベルに表示
        If htResult.PointIndex > 0 Then
            'X軸の値(日付)
            lblDate.Text = Date.FromOADate(Chart1.Series("seriesReward").Points(htResult.PointIndex).XValue)

            '各Seriesの値
            lblReward.Text = Chart1.Series("seriesReward").Points(htResult.PointIndex).YValues(0).ToString("#,##0")
            lblCost.Text = Chart1.Series("seriesCost").Points(htResult.PointIndex).YValues(0).ToString("#,##0")
            lblBenefit.Text = Chart1.Series("seriesBenefit").Points(htResult.PointIndex).YValues(0).ToString("#,##0")
            lblRanking.Text = Chart1.Series("seriesRanking").Points(htResult.PointIndex).YValues(0).ToString("#,##0")

        Else
            lblDate.Text = ""
            lblReward.Text = ""
            lblCost.Text = ""
            lblBenefit.Text = ""
            lblRanking.Text = ""
        End If


    Catch ex As Exception
        MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)

    End Try
End Sub
マウスが日付と日付の間に位置しているときにどうするかという問題はありますが…。

とりあえず思いついた方法として。
ChartArea.AxisXからAxis::PixelPositionToValueで日付(時刻含む)がDouble型で取得できます。
あとはSeries.DataPointsから検索するとか、日付に直して元データを検索するとか。
HitTestと比べてどっちが重いかは分かりません。
Hongliangさん、ありがとうございます。

頂いたヒントを元に解決することが出来ました!。


「Series.DataPointsから検索する」方法と
「日付に直して元データを検索する」方法の
両方を試してみました。

CPU負荷はどちらの方法でも
HitTest使用時の半分くらいになりました。

今回は、コードがよりシンプルな
「Series.DataPointsから検索する」方法を
採用させて頂きました。


また、以下の課題も同時に解決しました。
>マウスが日付と日付の間に位置しているときにどうするかという問題はありますが…。




以下に解決コードを記載します。



Private Sub Chart1_MouseMove(sender As Object, e As MouseEventArgs) Handles Chart1.MouseMove


    Try
        'マウスに合わせて、X軸に垂直な縦線を表示
        Dim mousePoint As Point = New Point(e.X, e.Y)
        Chart1.ChartAreas(0).CursorX.SetCursorPixelPosition(mousePoint, True)


        '垂直線のX座標(日付)をDouble型で取得
        '垂直線は日付と日付の間には描画されない
        'この値を取得することで、日付と日付の間にマウスがある場合の問題を解決
        Dim xDateDouble As Double
        xDateDouble = Chart1.ChartAreas(0).CursorX.Position


        '値を表示
        '日付
        lblDate.Text = Date.FromOADate(Chart1.Series("seriesReward").Points.FindByValue(xDateDouble, "X").XValue)
        
        '各シリーズの値
        lblReward.Text = Chart1.Series("seriesReward").Points.FindByValue(xDateDouble, "X").YValues(0).ToString("#,##0")
        lblCost.Text = Chart1.Series("seriesCost").Points.FindByValue(xDateDouble, "X").YValues(0).ToString("#,##0")
        lblBenefit.Text = Chart1.Series("seriesBenefit").Points.FindByValue(xDateDouble, "X").YValues(0).ToString("#,##0")
        lblRanking.Text = Chart1.Series("seriesRanking").Points.FindByValue(xDateDouble, "X").YValues(0).ToString("#,##0")


    Catch ex As Exception
        MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)

    End Try

End Sub
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板