a の移動は、コントロールDataPoint
の組み込み機能ではありません。Chart
コーディングする必要があります..
マウスでチャートを操作する際の問題は、で機能する座標系が 1 つではなく 3 つChart
あることです。
Legend
aや anなどのグラフ要素Annotation
は、それぞれのコンテナーのパーセンテージで測定されます。これらのデータは を構成しElementPosition
、通常は から移動し0-100%
ます。
3 つのPaint
イベントのいずれかで描画されるマウス座標とすべてのグラフィックスは、すべてピクセル単位で機能します。彼らはから行き0-Chart.ClientSize.Width/Height
ます。
DataPoints
x 値と 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の を使用することを選択したことに注意してください。DataPoints
RectangleF
DataPoint
これらの四角形は、チャートのサイズが変更されるか、凡例のサイズ変更などのレイアウトでのその他の変更が発生するたびに変更されるため、毎回再同期する必要があります。もちろん、 !を追加するたびに最初に設定する必要があります。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
、コレクションをループします。次に、それを保存して色を変更します..:Points
Tag
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
を設定したことに注意してください。これにより、 が、などの自動表示で暴走するのを防ぎます。Intervals
Axes
Chart
Labels
GridLines
TickMarks
また、これはDataType
X 値と Y 値のいずれでも機能することに注意してください。Tooltip
フォーマットのみを調整する必要があります..
DataPoint
最後の注意: ユーザーがオフに移動するのを防ぐために、このチェックをイベントのにChartArea
追加できます。if-clause
MouseMove
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;
}
}