簡體   English   中英

如何拖動DataPoint並在Chart控件中移動它

[英]How to drag a DataPoint and move it in a Chart control

我希望能夠抓取圖表中繪制的數據點並移動它並通過將其拖動到圖表控件上來更改其位置。

我怎樣才能 ..

  1. ..抓住具體的系列點(系列名稱=“我的系列”)
  2. 釋放時,系列點應改變其位置/值

這就像使用拖動事件使系列點可移動一樣。

這里的顏色點(點)應該能夠移動:

在此輸入圖像描述

有一些圖表,如devExpress圖表執行此任務,但我想在正常的MS圖表中進行。

移動DataPoint不是Chart控件的內置功能。 我們需要編碼..

用鼠標圖表交互的問題是,有不是一個而是三個坐標的工作系統的Chart

  • 圖表元素(如LegendAnnotation以相應容器的百分比度量。 這些數據構成一個ElementPosition ,通常為0-100%

  • 鼠標坐標和在三個Paint事件之一中繪制的所有圖形都以像素為單位; 它們來自0-Chart.ClientSize.Width/Height

  • DataPoints具有x值和一個(或多個)y值。 這些是雙打的,他們可以去往你設置的任何地方。

對於我們的任務,我們需要在鼠標像素數據值之間進行轉換。

請看下面的更新!

在此輸入圖像描述 在此輸入圖像描述

有幾種方法可以做到這一點,但我認為這是最干凈的:

首先,我們創建一些包含目標引用的類級變量:

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

當我們設置圖表時,我們填寫其中一些:

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

接下來我們需要兩個輔助函數。 他們在像素和數據值之間進行第一次轉換:

    // 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));
    }

請注意,我選擇使用每個DataPointsTag來保存一個RectangleF ,它具有DataPoint標記的clientRectangle。

每當圖表調整大小或布局中的其他更改(如圖例等的大小調整等)發生時,這些矩形都會發生變化 ,因此我們需要每次重新同步它們! 當然,每當添加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事件中調用它,這很好。 國旗將有助於保持效率。

現在實際移動一個點 :像往常一樣,我們需要編寫三個鼠標事件的代碼:

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];

請注意,我已設置MinimaMaxima以及兩個AxesIntervals 這會使Chart自動顯示LabelsGridLinesTickMarks等,從而阻止Chart運行。

另請注意,這適用於X和Y值的任何DataType 只需要調整Tooltip格式..

最后注意事項:為了防止用戶從ChartArea移出DataPoint ,您可以將此檢查添加到MouseMove事件的if-clause中:

  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;
        }
}

下載Microsoft圖表控件的示例環境

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

檢查一下:

圖表功能 - >交互式圖表 - >選擇 - >通過拖動更改值

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM