ChartのプロットエリアのSizeを取得するには?

解決


 2012-10-13 12:41:01  No: 147881  IP: [192.*.*.*]

VB2010で、棒グラフなどが描画される部分
(Chart1.ChartAreas(0).BackColor = Color.Blue  で青色になる部分)
のSize(高さと幅)を取得する方法を教えてください。

Chart1.ClientSize.Width
Chart1.ClientSize.Height
では Chart1全体のSizeになってしまいます。

よろしくお願いします。

編集 削除
魔界の仮面弁士  2012-10-13 22:09:45  No: 147882  IP: [192.*.*.*]

Chart を触ったのは今回が初めてなので自信がありませんが、
これでどうでしょうか。
もっとスマートな方法があるのかも知れませんが…。


Option Strict On
Imports System.Drawing
Imports System.Reflection
Imports System.Windows.Forms.DataVisualization.Charting


Partial Public Class Form1
  Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Dim sz As SizeF = GetBackgroundPosition(Me.Chart1.ChartAreas(0), True).Size
    Debug.Print(sz.ToString() & " / " & Chart1.ClientSize.ToString())
    With Chart1.ClientSize
      sz.Height *= .Height / 100.0F
      sz.Width *= .Width / 100.0F
    End With

    TextBox1.Text = sz.ToString()
  End Sub

  Public Function GetBackgroundPosition(area As ChartArea, withScrollBars As Boolean) As RectangleF
    Dim f As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
    Dim t() As Type = {GetType(Boolean)}
    Dim m = GetType(ChartArea).GetMethod("GetBackgroundPosition", f, Nothing, t, Nothing)
    Return DirectCast(m.Invoke(area, New Object() {withScrollBars}), RectangleF)
  End Function
End Class

編集 削除
魔界の仮面弁士  2012-10-13 23:18:16  No: 147883  IP: [192.*.*.*]

> もっとスマートな方法があるのかも知れませんが…。
これだとグラフの内容によって、微妙にズレてしまうようですね。

下記だとどうでしょう。

Option Strict On
Imports System.Windows.Forms.DataVisualization.Charting

Partial Public Class Form1
    Private chartAreaSize As SizeF = SizeF.Empty

    Private Sub Chart1_PrePaint(sender As Object, e As ChartPaintEventArgs) Handles Chart1.PrePaint
        If e.ChartElement Is Chart1.ChartAreas(0) Then
            chartAreaSize = e.ChartGraphics.GetAbsoluteSize(Chart1.ChartAreas(0).Position.Size)
            TextBox1.Text = chartAreaSize.ToString()
        End If
    End Sub
End Class

編集 削除
 2012-10-14 14:01:22  No: 147884  IP: [192.*.*.*]

魔界の仮面弁士さん、ありがとうございます。

このチャートはY軸に表示される数値の桁数によってもX軸の左側の位置が変わってきますし、数値によってもY軸上部の位置が微妙に調整されます。

Private Sub Chart1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Chart1.Click
  従って、実際にチャートを表示してから、例えばこのような中でSize(高さと幅)を取得したいのです。
End Sub

よろしくお願いします。

編集 削除
魔界の仮面弁士  2012-10-14 14:25:56  No: 147885  IP: [192.*.*.*]

> 数値の桁数によってもX軸の左側の位置が変わってきますし
> 数値によってもY軸上部の位置が微妙に調整されます。

経験不足から、具体的にどういう状態なのか想像がつかないのですが、
Click する段階では、描画は完了しているものと予想しています。

Click イベントの時点で、座標の計測が完了しておらず、
以前の位置が得られてしまう事があるという意味でしょうか?

それとも、PrePaint/PostPaint で取得しておいた座標値と、
Click 時の座標にズレが生じているということでしょうか?


もしも前者であれば、Click 段階で Invalidate & Update を実行させ、
強制的に再計測を実施させてから、後続処理を遅延実行させては如何でしょう。

編集 削除
 2012-10-14 17:46:23  No: 147886  IP: [192.*.*.*]

魔界の仮面弁士さん、
始めたばかりの超初心者の質問で申し訳ないです。

Private Sub Chart1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Chart1.Click
   Dim X As Integer
   Dim Y As Integer
   Dim Pos As Point = Chart1.PointToClient(Windows.Forms.Cursor.Position)
   X = Pos.X
   Y = Pos.Y
  MessageBox.Show(X & " " & Y)
End Sub

何故かは分かりませんが、chartAreaSize.ToString() > このX、Yの値から算出した数値となります。

>Click する段階では、描画は完了しているものと予想しています。
単なる推測ですがこの段階では、まだ完了してないのかもしれません。

>後続処理を遅延実行させては如何でしょう。
Clickをする度に取得出来れば!? と思ったりもしますが何とも…??

これ以上は超初心者には遥かに能力オーバーですので一旦諦めることにします。
ありがとうございました m(_ _)m

編集 削除
魔界の仮面弁士  2012-10-14 19:27:40  No: 147887  IP: [192.*.*.*]

> Dim Pos As Point = Chart1.PointToClient(Windows.Forms.Cursor.Position)
ここで得た Point と、目標としている Size 取得の関係が分かりません。
このメッセージボックス表示は、何を得ようとしているコードなのでしょうか。

そもそも、先の「Private chartAreaSize As SizeF」などの記載が
見当たらないようですが、具体的にはどのように検証されましたか?


> chartAreaSize.ToString() > このX、Yの値から算出した数値となります。
仰っている意味が理解できなかったのですが、Chart コントロール内の
各要素のX/Y座標は、常に 0.0〜100.0 の範囲の「相対座標」で示されます。

そのため、その相対座標を画面上の絶対座標に変換したいのであれば、
先の2本のサンプルのように、ChartGraphicsクラスのメソッドで変換するか、
もしくは、その値にコントロール領域のサイズを乗じてやる必要がありますね。


> 単なる推測ですがこの段階では、まだ完了してないのかもしれません。
「かもしれない」で済ませず、念のため稍さん自身が検証してみてください。

実際に描いているチャートがどのような物なのか分からない以上、
こちらでは追加検証のしようがありませんので…。


> Clickをする度に取得出来れば!? と思ったりもしますが何とも…??
先の回答に繰り返しになってしまいますが、仮に取得できないようならば

(1)Click時に Invalidate & Update を実行することで「取得依頼」を発行。
(2)即座に描画系イベントが発生するので、ChartGraphics 経由で座標算出。
(3)そこで得た座標値を使って、クリック処理の続きを実施。

のようにしてみた場合はどうなりますか?

編集 削除
 2012-10-15 11:01:09  No: 147888  IP: [192.*.*.*]

魔界の仮面弁士さん、ありがとうございます。

これ程たいそうだとは思いもせず恐縮しています。
るるご指摘やお教えを頂いてますが、概ね理解は出来るもののそれを具現化する方法が分かりません。
まだその程度のレベルなのです。
繰り返し説明頂いてる(1)、(2)、(3)についても同様で、ChartGraphics 経由で座標算出?? と言った有様です。
それではならじと参考書やネットで調べては、あれこれ試行錯誤を繰り返しますがそれとても儘成らずです。
他の一つひとつにも答えるべきでしょうが、再び的外れなことを言い出しそうなので控えさせて頂きます。

下記サンプルはshuさんに手直しを頂いたものを元に作成しましたが間違っているのかもしれません。
このようなこともあり、これ以上は迷惑を掛けたくはありませんので一旦諦めることにしたのです。

Option Strict On
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
    Private chartAreaSize As SizeF = SizeF.Empty
    '
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim tbl As New DataTable
        '列の作成
        With tbl.Columns
            .Add("日付", GetType(String))
            .Add("高値", GetType(Integer))
            .Add("安値", GetType(Integer))
            .Add("始値", GetType(Integer))
            .Add("終値", GetType(Integer))
        End With
        'データの追加
        tbl.Rows.Add("2012/08/1", 700, 500, 600, 550)
        tbl.Rows.Add("2012/08/2", 1100, 800, 850, 800)
        tbl.Rows.Add("2012/08/7", 800, 500, 700, 750)
        tbl.Rows.Add("2012/08/8", 800, 300, 300, 600)
        '初期化    
        Chart1.Series.Clear()
        '
        Chart1.ChartAreas(0).BackColor = Color.AliceBlue
        'グラフの種類,系列,軸の設定
        Dim rosoku = Chart1.Series.Add("ローソク足")
        With rosoku
            .ChartType = DataVisualization.Charting.SeriesChartType.Candlestick
            .XValueMember = "日付"
            .YValueMembers = "高値, 安値, 始値, 終値"
            .IsVisibleInLegend = False
        End With
        Chart1.DataSource = tbl
        Chart1.DataBind()
    End Sub
    '
    Private Sub Chart1_PrePaint(ByVal sender As Object, ByVal e As ChartPaintEventArgs) Handles Chart1.PrePaint
        If e.ChartElement Is Chart1.ChartAreas(0) Then
            chartAreaSize = e.ChartGraphics.GetAbsoluteSize(Chart1.ChartAreas(0).Position.Size)
            TextBox1.Text = chartAreaSize.ToString()
        End If
    End Sub
    '
    Private Sub Chart1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Chart1.Click
        Dim X As Integer
        Dim Y As Integer
        Dim Pos As Point = Chart1.PointToClient(Windows.Forms.Cursor.Position)
        X = Pos.X
        Y = Pos.Y
        MessageBox.Show(X & " " & Y)  '相対座標
    End Sub
End Class

編集 削除
shu  2012-10-15 12:46:19  No: 147889  IP: [192.*.*.*]

Dim axx = Chart1.ChartAreas(0).AxisX
Dim axy = Chart1.ChartAreas(0).AxisY

Dim x1 = axx.ValueToPixelPosition(axx.Minimum)
Dim y1 = axy.ValueToPixelPosition(axy.Maximum)
Dim x2 = axx.ValueToPixelPosition(axx.Maximum)
Dim y2 = axy.ValueToPixelPosition(axy.Minimum)

とすると
(x1,y1) - (x2,y2)
がPixel単位のグラフ描画領域になります。
=>
サイズは(x2-x1, y2-y1)ということになります。

編集 削除
 2012-10-15 18:22:10  No: 147890  IP: [192.*.*.*]

shuさん、ありがとうございました。
希望通りになりました。

魔界の仮面弁士さん
今後ともよろしくお願いします。

編集 削除