Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a WPF Tooltip on a Rect object

I'm working with an open source charting library that creates markers on a graph. (Dynamic Data Display) The markers that I'm creating are created with Rect objects. I noticed that all the Shapes in System.Windows.Shapes have a ToolTip property, but System.Windows.Rect does not. I want to have a tooltip pop up telling the user the price value of the marker when the mouse hovers over it. I was thinking about creating and popping up a tooltip when the mouse enters the area that the rect occupies, but I don't know how possible that is considering the rect will be zoomed/panned around by the chart. Any other suggestions?

Here is my code for the marker I am creating (From Felice Pollano Blog)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Shapes;
using System.Windows.Controls;
using System.Text;
using System.Windows;
using System.Windows.Media;

namespace Microsoft.Research.DynamicDataDisplay.PointMarkers
{
public class CandleStickPointMarker:ShapePointMarker,ITransformAware
{
    public double CandlestickWidth
    {
        get { return (double)GetValue(CandlestickWidthProperty); }
        set { SetValue(CandlestickWidthProperty, value); }
    }
    public static readonly DependencyProperty CandlestickWidthProperty =
        DependencyProperty.Register("CandlestickWidth", typeof(double), typeof(CandleStickPointMarker), new UIPropertyMetadata(4.0));
    public double CandlestickStrokeWidth
    {
        get { return (double)GetValue(CandlestickStrokeWidthProperty); }
        set { SetValue(CandlestickStrokeWidthProperty, value); }
    }

    public static readonly DependencyProperty CandlestickStrokeWidthProperty =
        DependencyProperty.Register("CandlestickStrokeWidth", typeof(double), typeof(CandleStickPointMarker), new UIPropertyMetadata(1.0));
    public Brush WhiteCandleFill
    {
        get { return (Brush)GetValue(WhiteCandleFillProperty); }
        set { SetValue(WhiteCandleFillProperty, value); }
    }

    public static readonly DependencyProperty WhiteCandleFillProperty =
        DependencyProperty.Register("WhiteCandleFill", typeof(Brush), typeof(CandleStickPointMarker), new UIPropertyMetadata(Brushes.White));



    public Brush WhiteCandleStroke
    {
        get { return (Brush)GetValue(WhiteCandleStrokeProperty); }
        set { SetValue(WhiteCandleStrokeProperty, value); }
    }

    public static readonly DependencyProperty WhiteCandleStrokeProperty =
        DependencyProperty.Register("WhiteCandleStroke", typeof(Brush), typeof(CandleStickPointMarker), new UIPropertyMetadata(Brushes.DarkGray));



    public Brush BlackCandleFill
    {
        get { return (Brush)GetValue(BlackCandleFillProperty); }
        set { SetValue(BlackCandleFillProperty, value); }
    }
    public static readonly DependencyProperty BlackCandleFillProperty =
        DependencyProperty.Register("BlackCandleFill", typeof(Brush), typeof(CandleStickPointMarker), new UIPropertyMetadata(Brushes.Black));
    public Brush BlackCandleStroke
    {
        get { return (Brush)GetValue(BlackCandleStrokeProperty); }
        set { SetValue(BlackCandleStrokeProperty, value); }
    }

    public static readonly DependencyProperty BlackCandleStrokeProperty =
        DependencyProperty.Register("BlackCandleStroke", typeof(Brush), typeof(CandleStickPointMarker), new UIPropertyMetadata(Brushes.Gray));
    public CoordinateTransform Transform { get; set; }
    public double High
    {
        get { return (double)GetValue(HighProperty); }
        set { SetValue(HighProperty, value); }
    }

    public static readonly DependencyProperty HighProperty =
        DependencyProperty.Register("High", typeof(double), typeof(CandleStickPointMarker), new UIPropertyMetadata(0.0));

    public double Low
    {
        get { return (double)GetValue(LowProperty); }
        set { SetValue(LowProperty, value); }
    }

    public static readonly DependencyProperty LowProperty =
        DependencyProperty.Register("Low", typeof(double), typeof(CandleStickPointMarker), new UIPropertyMetadata(0.0));


    public double Open
    {
        get { return (double)GetValue(OpenProperty); }
        set { SetValue(OpenProperty, value); }
    }

    public static readonly DependencyProperty OpenProperty =
        DependencyProperty.Register("Open", typeof(double), typeof(CandleStickPointMarker), new UIPropertyMetadata(0.0));


    public override void Render(System.Windows.Media.DrawingContext dc, Point screenPoint)
    {
        Point screenOpen = GetScreenPoint(Open,screenPoint.X);
        Point screenHigh = GetScreenPoint(High,screenPoint.X);
        Point screenLow = GetScreenPoint(Low, screenPoint.X);
        //screenPoint is the CLOSE by gentleman agreement.
        var close = screenPoint.ScreenToData(Transform).Y;
        Pen strokePen;
        if (Open >= close) // black
        {
            strokePen = new Pen(BlackCandleStroke, CandlestickStrokeWidth);
            var h = -screenOpen.Y + screenPoint.Y;
            Rect blkRect = new Rect(screenPoint.X - CandlestickWidth / 2, screenOpen.Y, CandlestickWidth, h);                       
            dc.DrawRectangle(BlackCandleFill,strokePen, blkRect);
            dc.DrawLine(strokePen, screenLow, screenPoint);
            dc.DrawLine(strokePen, screenHigh, screenOpen);
        }
        else // white
        {
            strokePen=new Pen(WhiteCandleStroke, CandlestickStrokeWidth);
            var h = screenOpen.Y - screenPoint.Y;
            Rect whtRect = new Rect(screenPoint.X - CandlestickWidth / 2, screenPoint.Y, CandlestickWidth, h);
            dc.DrawRectangle(WhiteCandleFill, strokePen, whtRect);
            dc.DrawLine(strokePen, screenLow, screenOpen);
            dc.DrawLine(strokePen, screenHigh, screenPoint);
        }
    }

    private Point GetScreenPoint(double Open,double screenX)
    {
        Point screen = new Point(0, Open);
        return new Point(screenX,screen.DataToScreen(Transform).Y);
    }
}

}

like image 759
Jason Higgins Avatar asked Sep 05 '25 08:09

Jason Higgins


2 Answers

I don't know if this can help you much, but I suppose the candlestick marker should be similar to a rectangle marker. The code below shows how I created a rectangle marker in D3 (actually a square), using the Polygon class, in a similar way to the CircleElementPointMarker, with the tooltip functionality working.

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

namespace Microsoft.Research.DynamicDataDisplay.PointMarkers
{
    /// <summary>Adds Rectangle element at every point of graph</summary>
    public class RectangleElementPointMarker : ShapeElementPointMarker
    {
        Polygon Rectangle;

        public override UIElement CreateMarker()
        {
            Rectangle = new Polygon();
            Rectangle.Stroke = (Pen != null) ? Pen.Brush : Brushes.White;
            Rectangle.StrokeThickness = (Pen != null) ? Pen.Thickness : 0;
            Rectangle.Fill = Fill;
            Rectangle.HorizontalAlignment = HorizontalAlignment.Center;
            Rectangle.VerticalAlignment = VerticalAlignment.Center;
            Rectangle.Width = Size;
            Rectangle.Height = Size;

            Point Point1 = new Point(-Rectangle.Width / 2, -Rectangle.Height / 2);
            Point Point2 = new Point(-Rectangle.Width / 2, Rectangle.Height / 2);
            Point Point3 = new Point(Rectangle.Width / 2, Rectangle.Height / 2);
            Point Point4 = new Point(Rectangle.Width / 2, -Rectangle.Height / 2);
            PointCollection myPointCollection = new PointCollection();
            myPointCollection.Add(Point1);
            myPointCollection.Add(Point2);
            myPointCollection.Add(Point3);
            myPointCollection.Add(Point4);
            Rectangle.Points = myPointCollection;

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

            return Rectangle;
        }

        public override void SetPosition(UIElement marker, Point screenPoint)
        {

            Canvas.SetLeft(marker, screenPoint.X - Size / 2);
            Canvas.SetTop(marker, screenPoint.Y - Size / 2);
        }
    }
}

The code was compiled it on .NET 4.0, VS2010

like image 183
sɐunıɔןɐqɐp Avatar answered Sep 07 '25 21:09

sɐunıɔןɐqɐp


The easiest way to achieve that is to follow these steps:

  1. create a Canvas (name it tooltipLayer) above your dc area (with same width and Height and boundaries as dc).
  2. For each rectangle in dc, add one canvas (name them tooltipContainers) to tooltipLayer.
  3. Each tooltipContainer has width and height equal to zero, Margin equal to "X,Y,0,0" where X and Y are its corresponding rectangle's X and Y, and also have one border as child element.
  4. each border must have the same width and height as its corresponding rectangle, and set its background to something barely visible (like "#01000000")
  5. add tooltip to each border
like image 32
Bizhan Avatar answered Sep 07 '25 21:09

Bizhan