且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何将图像设置为沿图表轴或沿图表轴放置?

更新时间:2023-02-02 17:07:07

这是一个好主意.

最简单的方法是在图表的Paint事件(可能是PrePaint)中绘制图像.

The simplest way is to draw the image in a Paint event of the Chart, maybe PrePaint.

开始工作..我们将使用

Let's go to work.. We will use the DrawImage overload that allows us zooming as well as cropping. For this we need two rectangles.

第一个挑战是始终获得正确的目标矩形.

The first challenge is to always get the correct target rectangle.

为此,我们需要将InnerPlotPosition从相对位置转换为绝对像素.

For this we need to convert the InnerPlotPosition from relative positions to absolute pixels.

这两个功能会有所帮助:

These two functions will help:

RectangleF ChartAreaClientRectangle(Chart chart, ChartArea CA)
{
    RectangleF CAR = CA.Position.ToRectangleF();
    float pw = chart.ClientSize.Width / 100f;
    float ph = chart.ClientSize.Height / 100f;
    return new RectangleF(pw * CAR.X, ph * CAR.Y, pw * CAR.Width, ph * CAR.Height);
}

RectangleF InnerPlotPositionClientRectangle(Chart chart, ChartArea CA)
{
    RectangleF IPP = CA.InnerPlotPosition.ToRectangleF();
    RectangleF CArp = ChartAreaClientRectangle(chart, CA);

    float pw = CArp.Width / 100f;
    float ph = CArp.Height / 100f;

    return new RectangleF(CArp.X + pw * IPP.X, CArp.Y + ph * IPP.Y, 
                            pw * IPP.Width, ph * IPP.Height);
}

使用这些数字设置目标矩形非常简单:

With these numbers setting the destination rectangle is as simple as:

Rectangle tgtR = Rectangle.Round(new RectangleF(ipr.Left, ipr.Bottom - 15, ipr.Width, 15));

您可以根据需要选择一个高度.

You can chose a height as you like..

下一个挑战是源矩形.

不缩放就简单地是:

Rectangle srcR = new Rectangle( 0, 0, bmp.Width, bmp.Height);

但是对于缩放和平移,我们需要对其进行缩放;为此,我们可以使用x轴和ScaleViewMinimumMaximum值.

But for zooming and panning we need to scale it; for this we can use the x-axis and the ScaleView's Minimum and Maximum values.

我们计算轴上第一个点和最后一个点的因子:

We calculate factors for the first and last spot on the axis:

double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);

现在我们得到的源矩形可能是这样的:

now we get the source rectangle maybe like this:

int x  = (int)(bmp.Width * f1);
int xx = (int)(bmp.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);

让我们放在一起:

private void chart_PrePaint(object sender, ChartPaintEventArgs e)
{
    // a few short names
    Graphics g = e.ChartGraphics.Graphics;  
    ChartArea ca = chart.ChartAreas[0];
    Axis ax = ca.AxisX;

    // pixels of plot area
    RectangleF ipr = InnerPlotPositionClientRectangle(chart, ca);

    // scaled first and last position
    double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
    double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);

    // actual drawing with the zooming overload
    using (Bitmap bmp = (Bitmap)Bitmap.FromFile(imagePath))
    {
        int x  = (int)(bmp.Width * f1);
        int xx = (int)(bmp.Width * f2);
        Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);
        Rectangle tgtR = Rectangle.Round(
                         new RectangleF(ipr.Left , ipr.Bottom - 15, ipr.Width, 15));
        g.DrawImage(bmp, tgtR, srcR, GraphicsUnit.Pixel);
    }
}

一些注意事项:

  • 我当然建议使用Image资源,而不是总是从磁盘加载!

  • Of course I would recomend to use an Image resource instead of always loading from disk!

图形将始终覆盖数据点以及网格.你也可以.

The Drawing will always overlay the data points and also the grids. You can either..

  • 选择不同的最小值腾出空间
  • 缩小图像
  • 将其移动到x轴标签下方
  • 使图像半透明
  • 将x轴设置得很胖,以使其可以容纳图像带:ax.LineWidth = 10
  • choose a different minimum to make room
  • make the image smaller
  • move it below the x-axis labels
  • make the image semi-transparent
  • make the x-axis so fat that it can hold the image strip : ax.LineWidth = 10

对于后一种解决方案,您可能希望根据缩放状态偏移y位置.又快又脏:int yoff = (ax.ScaleView.IsZoomed ? 12 : 5);.为了避免出现黑色条纹,还应将轴设置为透明"或"chart.BackColor ..

For the latter solution you would want to offset the y-position depending on the zoom state. Quick and dirty: int yoff = (ax.ScaleView.IsZoomed ? 12 : 5);. To avoid black stripes also make the axis Transparent or chart.BackColor..

更新:

您还可以还原为使用StripLine.它可以缩放其BackgroundImage,并且每当更改比例视图时(即缩放或平移时),您都必须创建合适的图像.为此,上面的许多代码将用于创建新图像.请参阅这篇文章,以了解如何在图表中添加和替换不同的NamedImage ! (有关标记图像的相关部分接近结尾!)

You can also revert to using a StripLine. It can scale its BackgroundImage and you would have to create a suitable image whenever changing the scaleview, i.e. when zooming or panning. For this much of the above code would be used to create the new images. See this post for examples of adding and replacing varying NamedImage to a Chart! (The relevant portion is close to the end about the marker images!)

事实上,我找到了***的解决方案,并添加了第二个答案.

In fact I found that way the best solution and have added a second answer.