WPF 与 WinForms Oxyplot 自定义 Tooltip/Tracker

WPF vs WinForms Oxyplot Customize Tooltip/Tracker

我正在构建 WPF 应用程序并使用 OxyPlot 渲染图表。

为了最大化图表的性能,我切换到 OxyPlot.WindowsForms 并使用 WindowsFormsHost 嵌入图表。资料来源:https://oxyplot.userecho.com/de/communities/1/topics/35-wpf-performance

缩放是我正在渲染的图表中非常重要的部分,但是 OxyPlot.WindowsForms 似乎没有漂亮的工具提示。

Windows 窗体:

WPF:

有没有什么方法可以自定义 WindowsForms 工具提示,使其看起来像 WPF 工具提示?最重要的是点的纵横线。

查看组件的源代码,它并不是真正的工具提示,it's a Label. An option could be getting the private trackingLabel 并在控件区域应用更改(或者理想情况下用支持该形状的自定义标签替换它)。

也可以绘制十字线跟踪器,可以处理Paint绘图事件,并根据标签的中心绘制水平和垂直线。

好吧,我上面提到的只是一个快速解决方法,结果如下:

使用以下代码:

private Label trackerLabel;
private void Form1_Load(object sender, EventArgs e)
{
    var trackerLabelField = plotView1.GetType().GetField("trackerLabel",
        System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    trackerLabel = new BalloonLabel()
    {
        Visible = false,
        Parent = plotView1
    };
    trackerLabelField.SetValue(plotView1, trackerLabel);

    var myModel = new PlotModel { Title = "Example 1" };
    myModel.Series.Add(new FunctionSeries(Math.Cos, 0, 10, 0.1, "cos(x)"));
    this.plotView1.Model = myModel;
    this.plotView1.Paint += PlotView1_Paint;
    trackerLabel.VisibleChanged += TrackerLabel_VisibleChanged;
}
private void TrackerLabel_VisibleChanged(object sender, EventArgs e)
{
    plotView1.Invalidate();
}
private void PlotView1_Paint(object sender, PaintEventArgs e)
{
    if (trackerLabel.Visible)
    {
        var r = plotView1.Model.PlotArea;
        e.Graphics.DrawLine(Pens.Blue, trackerLabel.Left + trackerLabel.Width / 2, (int)r.Top,
            trackerLabel.Left + trackerLabel.Width / 2, (int)r.Bottom);
        e.Graphics.DrawLine(Pens.Blue, (int)r.Left, trackerLabel.Bottom, (int)r.Right, trackerLabel.Bottom);
    }
}

还有这个气球标签,我假设它是一个 good-enough 起点:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public class BalloonLabel : Label
{
    public BalloonLabel()
    {
        BackColor = SystemColors.Info;
        Padding = new Padding(5, 5, 5, 20);
        ArrowSize = new Size(10, 20);
        AutoSize = true;
    }
    private Size arrowSize;
    public Size ArrowSize
    {
        get { return arrowSize; }
        set
        {
            arrowSize = value;
            this.RecreateRegion();
        }
    }

    private GraphicsPath GetBalloon(Rectangle bounds)
    {
        GraphicsPath path = new GraphicsPath();
        path.StartFigure();

        if (arrowSize.Width > 0 && arrowSize.Height > 0)
        {
            path.AddLine(bounds.Left, bounds.Bottom - arrowSize.Height / 2, bounds.Left, bounds.Top);
            path.AddLine(bounds.Left, bounds.Top, bounds.Right, bounds.Top);
            path.AddLine(bounds.Right, bounds.Top, bounds.Right, bounds.Bottom - arrowSize.Height / 2);
            path.AddLine(bounds.Right, bounds.Bottom - arrowSize.Height / 2, bounds.Left + bounds.Width / 2 + arrowSize.Width / 2, bounds.Bottom - arrowSize.Height / 2);
            path.AddLine(bounds.Left + bounds.Width / 2 + arrowSize.Width / 2, bounds.Bottom - arrowSize.Height / 2,
                bounds.Left + bounds.Width / 2, bounds.Bottom);
            path.AddLine(bounds.Left + bounds.Width / 2, bounds.Bottom,
                bounds.Left + bounds.Width / 2 - arrowSize.Width / 2, bounds.Bottom - arrowSize.Height / 2);
            path.AddLine(bounds.Left + bounds.Width / 2 - arrowSize.Width / 2, bounds.Bottom - arrowSize.Height / 2,
                bounds.Left, bounds.Bottom - arrowSize.Height / 2);
        }
        else
        {
            path.AddLine(bounds.Left, bounds.Bottom, bounds.Left, bounds.Top);
            path.AddLine(bounds.Left, bounds.Top, bounds.Right, bounds.Top);
            path.AddLine(bounds.Right, bounds.Top, bounds.Right, bounds.Bottom);
            path.AddLine(bounds.Right, bounds.Bottom, bounds.Left, bounds.Bottom);
        }
        path.CloseFigure();
        return path;
    }

    private void RecreateRegion()
    {
        var r = ClientRectangle;
        using (var path = GetBalloon(r))
            this.Region = new Region(path);
        this.Invalidate();
    }
    protected override void OnSizeChanged(EventArgs e)
    {
        base.OnSizeChanged(e);
        this.RecreateRegion();
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        var r = ClientRectangle;
        r.Inflate(-1, -1);
        using (var path = GetBalloon(r))
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            using (var pen = new Pen(Color.Gray, 1) { Alignment = PenAlignment.Inset })
                e.Graphics.DrawPath(pen, path);
            if (Parent != null)
                Parent.Invalidate();
        }
    }
}

这是动画结果: