在悬停上定位元素 - WPF - C# - 动态数据显示

时间:2012-11-16 09:27:17

标签: c# wpf dynamic-data-display

在动态数据显示中,有一个名为MarkerPointsGraph的类。这派生自FrameWorkElement(通过一系列其他类,最直接的父类是PointsGraphBase)并覆盖OnRenderMethod以在图表上绘制一组标记。

它通过在屏幕上呈现的每个点调用适当的标记类型(例如,三角形,圆形等)一次的渲染方法来完成此操作。我需要找到一种方法来识别鼠标悬停在其中一个标记上的时间,以便我可以为该标记设置工具提示。

我有一组方法可以让我将一个点从屏幕位置转换为视口位置,再转换到数据位置和返回。即将屏幕值转换为相应的数据值或视口值,反之亦然。

我还在此框架元素上有工具提示打开事件以及每个标记的像素大小。只要用户在特定标记上悬停,我就需要确定他在哪个点上徘徊,并让markerpointsgraph设置工具提示值。

但是,转换值的转换和方法似乎没有正常工作,特别是在x方向。你的方向似乎很好。

下面是一些示例代码,可以解释这个想法:

                    double selectedPointX = 0;
                    double selectedPointY = 0;

                    CoordinateTransform transformLocal = this.primaryPlotter.Transform;

                    if (series.SeriesDescription.YAxisAffinity == AxisAffinity_Y.Y1)
                    {
                        selectedPointX = Mouse.GetPosition(this.primaryPlotter).ScreenToViewport(transformLocal).X; //Getting the mouse positions
                        selectedPointY = Mouse.GetPosition(this.primaryPlotter).ScreenToViewport(transformLocal).Y;
                    }
                    else if (series.SeriesDescription.YAxisAffinity == AxisAffinity_Y.Y2 && injSecondaryAxis != null)
                    {
                        transformLocal = injSecondaryAxis.Transform;

                        selectedPointX = Mouse.GetPosition(this.injSecondaryAxis).ScreenToViewport(transformLocal).X;
                        selectedPointY = Mouse.GetPosition(this.injSecondaryAxis).ScreenToViewport(transformLocal).Y;
                    }
                    else if (series.SeriesDescription.YAxisAffinity == AxisAffinity_Y.Y3 && injTertiaryAxis != null)
                    {
                        transformLocal = injTertiaryAxis.Transform;

                        selectedPointX = Mouse.GetPosition(this.injTertiaryAxis).ScreenToViewport(transformLocal).X;
                        selectedPointY = Mouse.GetPosition(this.injTertiaryAxis).ScreenToViewport(transformLocal).Y;
                    }

                    foreach (var item in SeriesList)
                    {
                        if (item.Key == GraphKey)
                        {
                            for (int i = 0; i < item.Value.Collection.Count; i++)
                            {
//Calculate the size of the marker on the screen and allow for some level of inaccuracy in identifying the marker i.e anywhere within the marker is allowed. 
                                double xlowerBound = item.Value.Collection[i].DataToViewport(transformLocal).X - series.MarkerSize;
                                double xUpperBound = item.Value.Collection[i].DataToViewport(transformLocal).X + series.MarkerSize;
                                double ylowerBound = item.Value.Collection[i].DataToViewport(transformLocal).Y - series.MarkerSize;
                                double yUpperBound = item.Value.Collection[i].DataToViewport(transformLocal).Y + series.MarkerSize;

                                //If point is within bounds
                                if (!(selectedPointX < xlowerBound || selectedPointX > xUpperBound || selectedPointY < ylowerBound || selectedPointY > yUpperBound))
                                {
                                    strToolTip = item.Value.Collection[i].X + ", " + item.Value.Collection[i].Y; //This point is set as the tooltip
                                    break;
                                }
                            }

                            break;
                        }
                    }

这里,injSecondary和injTertiary是两个注入绘图仪,提供两个垂直Y轴。它们的行为与主要的图表绘图仪非常相似。

这里发生了什么问题吗?出于某种原因,远远超过实际点击点的点是通过缓冲区子句。

2 个答案:

答案 0 :(得分:2)

嗯,看起来你对我的态度是错误的。如果您深入研究D3的源代码,您可以打开其中一个标记类并直接在那里编辑工具提示。每个System.Windows.Shapes元素都有一个Tooltip属性,您可以在标记类中指定该属性。您需要做的就是确定工具提示所包含的数据。

示例 - CircleElementPointMarker类:​​

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;

namespace Microsoft.Research.DynamicDataDisplay.PointMarkers
{
/// <summary>Adds Circle element at every point of graph</summary>
public class CircleElementPointMarker : ShapeElementPointMarker {

    public override UIElement CreateMarker()
    {
        Ellipse result = new Ellipse();
        result.Width = Size;
        result.Height = Size;
        result.Stroke = Brush;
        result.Fill = Fill;
        if (!String.IsNullOrEmpty(ToolTipText))
        {
            ToolTip tt = new ToolTip();
            tt.Content = ToolTipText;
            result.ToolTip = tt;
        }
        return result;
    }

    public override void SetMarkerProperties(UIElement marker)
    {
        Ellipse ellipse = (Ellipse)marker;

        ellipse.Width = Size;
        ellipse.Height = Size;
        ellipse.Stroke = Brush;
        ellipse.Fill = Fill;

        if (!String.IsNullOrEmpty(ToolTipText))
        {
            ToolTip tt = new ToolTip();
            tt.Content = ToolTipText;
            ellipse.ToolTip = tt;
        }
    }

    public override void SetPosition(UIElement marker, Point screenPoint)
    {
        Canvas.SetLeft(marker, screenPoint.X - Size / 2);
        Canvas.SetTop(marker, screenPoint.Y - Size / 2);
    }
}
}

您可以在此课程中看到我们内置了代码来处理工具提示。 ToolTipText是一个派生自父类的属性 - ShapeElementPointMarker。您所要做的就是为该属性分配您需要显示的数据。

答案 1 :(得分:0)

在绘图仪上使用cursorcoordinategraph代替Mouse.GetPosition以获取正确的屏幕位置并避免ScreenToViewport变换。此外,在创建边界时使用DataToScreen。