3

チャートに描画されたデータポイントを取得し、それをチャート コントロール上にドラッグして移動および位置を変更できるようにしたいと考えています。

どうやって ..

  1. ..特定のシリーズ ポイントを取得する (シリーズ名 ="My Series")
  2. リリースされると、シリーズポイントはその位置/値を変更する必要があります

ドラッグイベントでシリーズポイントを可動にするようなものです。

ここでは、カラー ドット (ポイント) が移動できる必要があります。

ここに画像の説明を入力

このタスクを実行する devExpress チャートのようなチャートがいくつかありますが、通常の MS チャートで実行したいと考えています。

4

2 に答える 2

8

a の移動は、コントロールDataPointの組み込み機能ではありません。Chartコーディングする必要があります..

マウスでチャートを操作する際の問題は、で機能する座標系が 1 つではなく 3 つChartあることです。

  • Legendaや anなどのグラフ要素Annotationは、それぞれのコンテナーのパーセンテージで測定されます。これらのデータは を構成しElementPosition、通常は から移動し0-100%ます。

  • 3 つのPaintイベントのいずれかで描画されるマウス座標とすべてのグラフィックスは、すべてピクセル単位で機能します。彼らはから行き0-Chart.ClientSize.Width/Heightます。

  • DataPointsx 値と 1 つ (または複数) の y 値があります。これらは double であり、設定した場所からどこへでも移動できます。

このタスクでは、マウス ピクセルデータ値の間で変換する必要があります。

以下のアップデートをご覧ください。

ここに画像の説明を入力ここに画像の説明を入力

これを行うにはいくつかの方法がありますが、これが最もクリーンだと思います。

最初に、ターゲットへの参照を保持するいくつかのクラス レベル変数を作成します。

// variables holding moveable parts:
ChartArea ca_ = null;
Series s_ = null;
DataPoint dp_ = null;
bool synched = false;

チャートを設定するとき、それらのいくつかを埋めます:

ca_ = chart1.ChartAreas[0];
s_ = chart1.Series[0];

次に、2 つのヘルパー関数が必要です。ピクセルとデータ値の間で最初の変換を行います。

    // two helper functions:
    void SyncAllPoints(ChartArea ca, Series s)
    {
        foreach (DataPoint dp in s.Points) SyncAPoint(ca, s, dp);
        synched = true;
    }

    void SyncAPoint(ChartArea ca, Series s, DataPoint dp)
    {
        float mh = dp.MarkerSize / 2f;
        float px = (float)ca.AxisX.ValueToPixelPosition(dp.XValue);
        float py = (float)ca.AxisY.ValueToPixelPosition(dp.YValues[0]);
        dp.Tag = (new RectangleF(px - mh, py - mh, dp.MarkerSize, dp.MarkerSize));
    }

のマーカーの clientRectangle を持つを保持するためにTag、 eachの を使用することを選択したことに注意してください。DataPointsRectangleFDataPoint

これらの四角形は、チャートのサイズが変更されるか、凡例のサイズ変更などのレイアウトでのその他の変更が発生するたびに変更されるため、毎回再同期する必要があります。もちろん、 !を追加するたびに最初に設定する必要があります。DataPoint

Resizeイベントは次のとおりです。

private void chart1_Resize(object sender, EventArgs e)
{
    synched = false;
}

長方形の実際の更新は、PrePaintイベントからトリガーされています。

private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
{
    if ( !synched) SyncAllPoints(ca_, s_);
}

の呼び出しValueToPixelPosition常に有効であるとは限らないことに注意してください。間違ったタイミングで呼び出すと、null が返されます。PrePaintイベントから呼び出していますが、これで問題ありません。フラグは物事を効率的に保つのに役立ちます。

ポイントの実際の移動: いつものように、3 つのマウス イベントをコーディングする必要があります。

では、マウスの位置を含むを持つコレクションが見つかるまでMouseDown、コレクションをループします。次に、それを保存して色を変更します..:PointsTag

private void chart1_MouseDown(object sender, MouseEventArgs e)
{
    foreach (DataPoint dp in s_.Points)
        if (((RectangleF)dp.Tag).Contains(e.Location))
        {
            dp.Color = Color.Orange;
            dp_ = dp;
            break;
        }
}

逆の計算MouseMove行い、ポイントの値を設定します。また、新しい位置を同期し、 をトリガーして表示を更新することに注意してください。Chart

private void chart1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button.HasFlag(MouseButtons.Left) && dp_ != null)
    {
        float mh = dp_.MarkerSize / 2f;
        double vx = ca_.AxisX.PixelPositionToValue(e.Location.X);
        double vy = ca_.AxisY.PixelPositionToValue(e.Location.Y);

        dp_.SetValueXY(vx, vy);
        SyncAPoint(ca_, s_, dp_);
        chart1.Invalidate();
    }
   else
   {
       Cursor = Cursors.Default;
       foreach (DataPoint dp in s_.Points)
          if (((RectangleF)dp.Tag).Contains(e.Location))
          {
             Cursor = Cursors.Hand; break;
          }
   }
}

最後に、MouseUpイベントをクリーンアップします。

    private void chart1_MouseUp(object sender, MouseEventArgs e)
    {
        if (dp_ != null)
        {
            dp_.Color = s_.Color;
            dp_ = null;
        }
    }

チャートを設定した方法は次のとおりです。

Series S1 = chart1.Series[0];
ChartArea CA = chart1.ChartAreas[0];
S1.ChartType = SeriesChartType.Point;
S1.MarkerSize = 8;
S1.Points.AddXY(1, 1);
S1.Points.AddXY(2, 7);
S1.Points.AddXY(3, 2);
S1.Points.AddXY(4, 9);
S1.Points.AddXY(5, 19);
S1.Points.AddXY(6, 9);

S1.ToolTip = "(#VALX{0.##} / #VALY{0.##})";

S1.Color = Color.SeaGreen;

CA.AxisX.Minimum = S1.Points.Select(x => x.XValue).Min();
CA.AxisX.Maximum = S1.Points.Select(x => x.XValue).Max() + 1;
CA.AxisY.Minimum = S1.Points.Select(x => x.YValues[0]).Min();
CA.AxisY.Maximum = S1.Points.Select(x => x.YValues[0]).Max() + 1;
CA.AxisX.Interval = 1;
CA.AxisY.Interval = 1;

ca_ = chart1.ChartAreas[0];
s_ = chart1.Series[0];

Minimaと の両方とfor bothMaximaを設定したことに注意してください。これにより、 が、などの自動表示で暴走するのを防ぎます。IntervalsAxesChartLabelsGridLinesTickMarks

また、これはDataTypeX 値と Y 値のいずれでも機能することに注意してください。Tooltipフォーマットのみを調整する必要があります..

DataPoint最後の注意: ユーザーがオフに移動するのを防ぐために、このチェックをイベントのにChartArea追加できます。if-clauseMouseMove

  RectangleF ippRect = InnerPlotPositionClientRectangle(chart1, ca_);
  if (!ippRect.Contains(e.Location) ) return;

InnerPlotPositionClientRectangle機能についてはこちら!

アップデート:

コードを再検討すると、なぜもっと単純な方法を選択しなかったのか疑問に思います。

DataPoint curPoint = null;

private void chart1_MouseUp(object sender, MouseEventArgs e)
{
    curPoint = null;
}

private void chart1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button.HasFlag(MouseButtons.Left))
    {
        ChartArea ca = chart1.ChartAreas[0];
        Axis ax = ca.AxisX;
        Axis ay = ca.AxisY;

        HitTestResult hit = chart1.HitTest(e.X, e.Y);
        if (hit.PointIndex >= 0) curPoint = hit.Series.Points[hit.PointIndex];

        if (curPoint != null)
        {
            Series s = hit.Series;
            double dx = ax.PixelPositionToValue(e.X);
            double dy = ay.PixelPositionToValue(e.Y);

            curPoint.XValue = dx;
            curPoint.YValues[0] = dy;
        }
}
于 2016-04-19T10:09:56.410 に答える
1

ダウンロードMicrosoft Chart Controls のサンプル環境

https://code.msdn.microsoft.com/Samples-Environments-for-b01e9c61

これをチェックして:

チャート機能 -> インタラクティブ チャート -> 選択 -> ドラッグによる値の変更

于 2017-10-20T09:01:01.607 に答える