2010-09-30 128 views
3

我有一些筆直的水平線,我希望用戶能夠垂直拖動。這將如何成爲可能?我認爲線選擇的最佳參數是線附近的固定數量的像素。所以如果鼠標是+/- 2像素,我應該改變鼠標光標,並使行可拖拽..我看到CurveItem類具有屬性IsSelectable和IsSelected。這些在解決這個問題上有什麼功能?我真的不能明白他們是什麼從閱讀類的文檔..ZedGraph - 如何製作水平線拖拽?


編輯:

看來,FindNearestPoint(和FindNearestObject)只搜索實際點。我如何讓選擇沿着整條直線工作?我想我需要做出自己定製的「Find」例程,它循環遍歷我想要檢查的所有行,並且對於每個計算它都是基於鼠標X位置的虛擬Y點()我也是爲此目的考慮傾斜線,對於水平/垂直線,它會稍微簡單一些。至少看起來這是curveItem所需要的,但我認爲必須完成相同的選擇(在中間部分)LineObj?

我其實不知道LineObj存在。看起來LineObj不可能改變座標,因爲它們是只讀。那麼是否可以拖動LineObj的X2/Y2點?


編輯2:

這似乎是與FindNearestPoint上JapaneseCandleStick圖的問題;當我單擊圖形窗格時,它不會而不是返回最近的條形圖的索引,但我相信它會選擇具有最接近的Y值的索引,而不管它在x軸上有多遠。有時候,它是鼠標右側的欄,有時候位於鼠標左側。這是它的工作方式嗎?

我自己製作了這個自定義函數,所以我猜這沒關係。仍然很好理解爲什麼FindNearestPoint這樣做。

這是鼠標按下代碼:

' Find nearest curve point: 
    Dim ciNearestCurve As CurveItem 
    Dim iNearestCurve As Integer 
    Dim b As Boolean = zgc.GraphPane.FindNearestPoint(mousePt, zgc.GraphPane.CurveList, ciNearestCurve, iNearestCurve) 
    If b Then 
     With ciNearestCurve(iNearestCurve) 
      Debug.Print(Date.FromOADate(.X) & " " & .Y & " " & .Z) 
     End With 

回答

1

就拿這個教程dragging the points with mouse看看。

如果您使用LineObj而不是曲線,請參閱FindNearestObject方法。

此外,如果您想爲點擊製作一些「敏感區域」,this method應該可以幫助您將鼠標座標以像素爲單位轉換爲窗格比例座標。

的主要思想是:
- 認購MouseDownMouseUpMouseMove事件
- 在處理程序MouseDown事件檢查,如果點擊的一點是要移動
曲線/圖形物體附近 - 做以類似的方式改變它例子所示,從第一個鏈接

編輯
關於您的編輯:
讓我們假設你有一個包含兩點的水平曲線myCurve。使用FindNearestPoint可以找到最接近的點擊點包含此點的曲線。

所以,你必須:

// mousePt is where you clicked 
CurveItem nearestCurve = null; 
int nearestID = -1; 

myPane.FindNearestPoint(mousePt, out nearestCurve, out nearestID); 
if(nearestCurve!=null) 
    // remember the curve somewhere. 

下一頁處理MouseMoveMouseUp事件,找出你需要多少移動你的曲線。您只需要知道Y(或Y2)方向的變化,因爲曲線是水平的,您可能不想沿X軸移動它。

當你會發現你是多麼需要移動你的曲線(dy),只是做:

for(int i=0; i<nearestCurve.Points.Count; i++) 
    nearestCurve.Points[i].Y += dy; 

關於你提到的第二個問題,在the documentation for LineObj.Location.Y2您有:

注意Y2位置在內部存儲爲 ,作爲與Y的高度偏移量。

Width/Height屬性可以很容易地設置,所以你可以這樣做。

+0

謝謝!一般來說,這看起來很管用但我不確定一些事情;請看我上面的編輯..(這裏沒有足夠的空間) – bretddog 2010-10-02 09:51:28

+0

看到我的編輯,希望它會有幫助 – Gacek 2010-10-02 10:25:19

+1

乾杯!我可以看到這個邏輯。但是,這種選擇只會觸發LineItem或LineObj上的實際數據點嗎?我期待着擴大這個範圍,使得可以在「顯示」行的任何圖表點上選擇一行,而不僅僅是接近組成行數據的實際(兩個)端點。這是否需要我制定一個自定義Find方法,該方法根據每條線的端點檢查一個虛構插值點? 啊,似乎我沒有詳細閱讀LineObj..Y2。現在有道理:) – bretddog 2010-10-02 11:13:06

1

首先要回答bretddog:

這似乎是與FindNearestPoint上JapaneseCandleStick圖的問題;當我單擊圖形窗格時,它不會返回最近條形圖的索引,但我相信它會選擇具有最接近Y值的索引,而不管它在x軸上距離多遠。有時候,它是鼠標右側的欄,有時候位於鼠標左側。這是它的工作方式嗎?

我做了這個自定義函數自己,所以我想這是確定..不過這將是很好理解爲什麼FindNearestPoint這種方式操作

我不JapaneseCandleStick但行工作,但我認爲這是同樣的問題。 ZedGraph使用座標進行處理,所以使用點而不使用函數,因此要確定最接近的「曲線」,它應該插值,看起來很難做到這一點。

儘管如此,對於線條圖形我開發了一個函數來獲得最近的曲線。所以我在每條曲線的每個連續點之間做了一條直線插值,並且我使用了數學距離來確定最近的曲線。該代碼是:

''' <summary> 
''' To obtain the nearest curve and its index on ZedGraph stick 
''' </summary> 
''' <param name="GraphPane">The graphpane on wich you are working</param> 
''' <param name="PointLocation">Mouse location</param> 
''' <param name="NearestCurve">Reference of the nearest curve</param> 
''' <param name="NearestCurveIndex">Index of the nearest curve</param> 
''' <returns>True if a curve is found</returns> 
''' <remarks></remarks> 
Private Function FindNearestCurve(ByVal GraphPane As ZedGraph.GraphPane, ByVal PointLocation As System.Drawing.Point, ByRef NearestCurve As CurveItem, ByRef NearestCurveIndex As Integer) As Boolean 
    Try 
     Dim MinDist As Double = -1 'error if < 0 
     Dim DistTemp As Double 
     Dim a, b As Double 
     Dim Curve As CurveItem 
     Dim ValX, ValY As Double 
     Dim NormX, NormY As Double 

     'ini 
     NearestCurveIndex = -1 
     GraphPane.ReverseTransform(PointLocation, ValX, ValY) 'To use real values 
     NormX = GraphPane.XAxis.Scale.Max - GraphPane.XAxis.Scale.Min 'To normalize value when we haven't orthonormal axis 
     NormY = GraphPane.YAxis.Scale.Max - GraphPane.YAxis.Scale.Min 'To normalize value when we haven't orthonormal axis 

     'We looking for the nearest curve 
     For j = 0 To GraphPane.CurveList.Count - 1 
      Curve = GraphPane.CurveList.Item(j) 
      If Curve.IsVisible = True Then 
       'We generate all coefficient (a and b) of straight line interpolation (equation y=ax+b) 
       For i = 0 To Curve.NPts - 2 '-2 because we work on intervals 
        'we check if interval is close to the point (to prevent case where the complete interpolation curve is the nearest curve but the real segment is far to the point) 
        If (Curve.Points.Item(i + 1).Y >= ValY And Curve.Points.Item(i).Y <= ValY) Or 
          (Curve.Points.Item(i + 1).Y <= ValY And Curve.Points.Item(i).Y >= ValY) Or 
          (Curve.Points.Item(i + 1).X >= ValX And Curve.Points.Item(i).X <= ValX) Or 
          (Curve.Points.Item(i + 1).X <= ValX And Curve.Points.Item(i).X >= ValX) Then 

         'We calculate straight line interpolation coefficient a and b 
         'Vertical line case 
         If (Curve.Points.Item(i + 1).X/NormX - Curve.Points.Item(i).X/NormX) = 0 Then 
          'We calculate directly the distance 
          DistTemp = Math.Abs(Curve.Points.Item(i).X/NormX - ValX/NormX) 
         Else 'All other case 
          'a = (yi+1 - yi)/(xi+1 - xi) 
          a = (Curve.Points.Item(i + 1).Y/NormY - Curve.Points.Item(i).Y/NormY)/(Curve.Points.Item(i + 1).X/NormX - Curve.Points.Item(i).X/NormX) 
          'b = yi - a*xi 
          b = Curve.Points.Item(i).Y/NormY - a * Curve.Points.Item(i).X/NormX 
          'We calculate the minimum distance between the point and all straight line interpolation 
          DistTemp = Math.Abs(a * ValX/NormX - ValY/NormY + b)/Math.Sqrt(1 + a * a) 
         End If 
         'We test if it's the minimum and save corresponding curve 
         If MinDist = -1 Then 
          MinDist = DistTemp 'first time 
          NearestCurveIndex = j 
         ElseIf DistTemp < MinDist Then 
          MinDist = DistTemp 
          NearestCurveIndex = j 
         End If 
        End If 
       Next 
      End If 
     Next 

     'Return the result 
     If NearestCurveIndex >= 0 And NearestCurveIndex < GraphPane.CurveList.Count Then 
      NearestCurve = GraphPane.CurveList.Item(NearestCurveIndex) 
      Return True 
     Else 
      NearestCurve = Nothing 
      NearestCurveIndex = -1 
      Return False 
     End If 

    Catch ex As Exception 
     NearestCurve = Nothing 
     NearestCurveIndex = -1 
     Return False 
    End Try 
End Function 

我測試過這個功能,它似乎運作良好,但我不能在所有情況下保證(的確,如果曲線的第一個/最後一個點是最近點,它不會被檢測到)。關於使用部分言論:

  • 工作只能在可見的曲線,將其刪除,刪除如果Curve.IsVisible = TRUE,則線;
  • 爲了防止與非正交軸的不一致,我在計算之前對X,Y值進行了歸一化處理;
  • 當出現錯誤時,返回和你有NearestCurve =無NearestCurveIndex = -1;
  • 你想拖動的線光標應該是一條曲線(不管是指向點還是更多),而不是LineObj;
  • 測試間隔是否接近是代碼的弱部分,我認爲它應該發生一些錯誤(我已經確定了一個 - 如前所述的罕見情況)。如果你有一條不完美的垂直線(所以係數很大)也會出現問題。

最後,我不確定此代碼是否針對速度進行了優化,但我並沒有對我的方式進行凍結。最好的方法應該是將函數集成到ZedGraph類中,並在調用函數時計算每個係數(a和b),以便每次不計算它們(因此每個鼠標移動)。

所以我希望代碼能夠幫助一些人創建一個可移動的光標,這在ZedGraph中是非常缺失的。