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();
}
}
}
这是动画结果:
我正在构建 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();
}
}
}
这是动画结果: