ZedGraph - 如何使水平线可拖动?

时间:2010-09-30 11:46:49

标签: line drag zedgraph

我有一些直线水平线,我希望用户能够垂直拖动。这怎么可能?我认为线选择的最佳参数是线附近的固定像素数。因此,如果鼠标是+/- 2像素,我应该更改鼠标光标并使该行可拖动..我看到CurveItem类具有属性IsSelectable和IsSelected。这些在解决这个问题上有什么作用吗?通过阅读课程文档,我无法真正理解它们的用途。


编辑:

FindNearestPoint (和 FindNearestObject )似乎只搜索实际点。如何选择沿直线的整个部分工作?我想我需要制作我自己的自定义“查找”例程,它循环遍历我要检查的所有行,并且每个都根据鼠标X位置计算它的虚构Y点(我也在考虑为此目的倾斜的线条,对于水平/垂直线条,它会稍微简单一些。至少看起来这对于一个curveItem来说是必需的,但我认为必须做同样的选择(在中间部分)一个LineObj?

我实际上并不知道 LineObj 存在。似乎LineObj无法更改 X2 / Y2 坐标,因为它们是 ReadOnly 。那么是否可以拖动LineObj的X2 / Y2点?


编辑2:

在JapaneseCandleStick图上,FindNearestPoint似乎存在问题;当我在图形窗格中单击时,它返回最近的条形的索引,但我相信它会选择具有最接近的Y值的索引,无论x轴有多远它是。有时它是鼠标右侧的条形图,有时是鼠标左侧的条形图。这是它的工作方式吗?

我自己创建了这个自定义函数,所以我猜它没问题。理解为什么FindNearestPoint这样做会很好。

这是mouseDown上的代码:

   ' 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

3 个答案:

答案 0 :(得分: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属性,因此您可以这样做。

答案 1 :(得分:1)

首先回答bretddog:

  

在JapaneseCandleStick图上,FindNearestPoint似乎存在问题;当我在图形窗格中单击时,它不会返回最近的条形图的索引,但我相信它会选择具有最接近的Y值的索引,无论它在x轴上有多远。有时它是鼠标右侧的条形图,有时是鼠标左侧的条形图。这是它的工作方式吗?

     

我自己创建了这个自定义函数,所以我猜它没关系..还是很高兴理解为什么FindNearestPoint以这种方式行事

我不使用JapaneseCandleStick但使用Line,但我认为这是同一类问题。 ZedGraph使用坐标,所以使用点,而不是函数,所以要确定最近的“曲线”它应该插值,这似乎很难做到。

然而,对于Line图形,我开发了一个函数来获得最近的曲线。所以我在每条曲线的每个连续点之间进行了直线插值,并且我使用数学距离来确定最近的曲线。代码是:

''' <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值进行了标准化,以防止与非正交轴的分歧;
  • 如果出现错误,请返回 False 并且 NearestCurve = Nothing NearestCurveIndex = -1 ;
  • 您想要拖动的线条光标应该是曲线(无论是指点还是更多),而不是LineObj;
  • 如果间隔是否接近的测试是代码的弱部分,我认为它应该发生一些错误(我已经确定了一个 - 罕见的情况,如前所述)。如果您的垂直线不完美(因此系数非常大),也会出现问题。

最后,我不确定这段代码是否针对速度进行了优化,但我并没有因此而冻结。最好的方法应该是将函数集成到ZedGraph类中,并在调用 Add 函数时计算每个系数(a和b),以便每次都不计算它们(因此每次鼠标移动)。

所以我希望代码可以帮助一些人创建一个可移动的游标,这在ZedGraph中非常缺失。

答案 2 :(得分:1)

我需要在图上拖动线对象。我花了很多时间才知道如何去做。以下代码专用于我的应用程序,虽然还不完整,但是可以正常工作,对于任何需要执行此操作的人,我认为它都是一个很好的起点。我的代码在VB中。它的本质是使用MouseDownEvent来确定光标是否足够接近要拖动的对象。然后在MouseMoveEvent中确定新位置并更新绘图。

version