我想为PictureBox Zoom创建一个用于mschart的缩放图片框。当我将鼠标悬停在图表上时,图片框应显示缩放视图,以便我可以选择适当的点。可以在winforms C#.net中发生这种情况吗?
答案 0 :(得分:1)
以下是一种解决方案,可以按照用户要求进行:创建一个PictureBox
,其中显示Chart
的 zoomed 部分,该部分将随着图表的移动而移动。
看起来不错,但仍然需要在这些微小的未缩放像素之间移动。
这是完成和设置的方法:
在必要时必须重新设置PictureBox zoomPBox
;进行设置需要进行一些测量并创建Chart
的屏幕截图。为此,图表被临时放大,然后重置为原始大小。
注意:每当更改图表大小或以任何其他方式更改图表时,都必须再次调用设置例程。
PictureBox zoomPBox
设置为SizeMode Normal
,并在Panel
中嵌套。在设置中,我们放大zoomPBox
以容纳整个Bitmap
。 Panel zoomPanel
具有AutoScroll = false
以避免滚动条。
一个并发症是Chart控件执行的自动调整大小。放大时,内容会放大,但例如没有一种字体。这将导致法线和缩放图区域之间的不同的长宽比。为了使运动保持同步,我们无法做到这一点。因此,我们不仅要从缩放的屏幕截图中切出没有Legend
,Title
或Axes
的实际内部绘图区域,而且还要拉伸 strong>与未缩放的绘图区域具有相同的长宽比。
这是结果:
MouseMove
的代码不那么涉及..:
private void chart_MouseMove(object sender, MouseEventArgs e)
{
if (zoomPBox.Image == null) return;
Rectangle ri = Rectangle.Round(
InnerPlotPositionClientRectangle(chart, chart.ChartAreas[0]));
Size szi = zoomPBox.Image.Size;
Size szp = zoomPanel.ClientSize;
Point cp = new Point( e.X - ri.X , e.Y - ri.Y );
float zx = 1f * szi.Width / ri.Width;
float zy = 1f * szi.Height / ri.Height; // should be the same
int x = round( szp.Width / 2 - cp.X * zx );
int y = round( szp.Height / 2 - cp.Y * zy );
zoomPBox.Location = new Point(x, y); // now we move the pBox into position
zoomPBox.Invalidate();
}
您可以看到我Invalidate
PictureBox
;也就是说,它可以在自身上绘制十字线以更好地控制;这是Paint
事件:
private void zoomPBox_Paint(object sender, PaintEventArgs e)
{
Size sz = zoomPanel.ClientSize;
int x = sz.Width / 2 - zoomPBox.Left;
int y = sz.Height / 2 - zoomPBox.Top;
e.Graphics.DrawLine(Pens.LightGray, 0, y, zoomPBox.Width, y);
e.Graphics.DrawLine(Pens.LightGray, x, 0, x, zoomPBox.Height);
}
现在开始设置例程:
void setupZoomBox(Chart chart, PictureBox pbox, float zoom)
{
ChartArea ca = chart.ChartAreas[0];
Size sz = chart.ClientSize;
Size szi = new Size(round(sz.Width * zoom), round(sz.Height * zoom));
Bitmap bmp2 = null;
chart.Refresh();
// original plot area
Rectangle pao = Rectangle.Round(InnerPlotPositionClientRectangle(chart, ca));
float ro = 1f * (pao.Width+2) / (pao.Height+2); // original aspect ratio
chart.ClientSize = szi;
chart.Refresh(); // enforce immediate layout
// zoomed plot area
Rectangle paz = Rectangle.Round(InnerPlotPositionClientRectangle(chart, ca));
float rz = 1f * paz.Width / paz.Height; // zoomed aspect ratio
// target rectangle, same aspect ratio as unzoomed area
int th = paz.Height;
int tw = round(paz.Height * ro );
// if (ro > rz)
//tw = round(th * ro); //else th = round(tw / ro);
Rectangle tgtR = new Rectangle(0, 0, tw, th);
// bitmap to hold only the zoomed inner plot area
bmp2 = new Bitmap(tgtR.Width, tgtR.Height);
// source area: Only the inner plot area plus 1 line of axis pixels:
Rectangle srcR = Rectangle.Round(
new RectangleF(paz.X - 1, paz.Y - 1, paz.Width + 2, paz.Height + 2));
// bitmap to hold the whole zoomed chart:
using (Bitmap bmp = new Bitmap(szi.Width, szi.Height))
{
Rectangle drawR = new Rectangle(0, 0, szi.Width, szi.Height);
chart.DrawToBitmap(bmp, drawR); // screenshot
using (Graphics g = Graphics.FromImage(bmp2)) // crop stretched
g.DrawImage(bmp, tgtR, srcR, GraphicsUnit.Pixel);
}
chart.ClientSize = sz; // reset chart
// you should dispose of the old Image if there is one before setting the new one!!
pbox.Image = bmp2;
pbox.ClientSize = bmp2.Size;
}
在某些地方,我需要获得所谓的InnerPlotPosition
的像素大小; (ElementPosition
中的MSChart
在相应容器区域的百分比中包括Location
和Size
。)我使用之前发布的函数,例如here。
答案 1 :(得分:1)
另一种解决方案是将图表控件用作缩放视图。当鼠标移到原始图表上时,您可以查看标记为红色的数据点。如下所示:
这是代码:
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
Point mousePoint = new Point(e.X, e.Y);
double mouse_Xvalue = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X);
double mouse_Yvalue = chart1.ChartAreas[0].AxisY.PixelPositionToValue(e.Y);
DataPoint Prev_DataPoint = chart1.Series[0].Points.Select(x => x)
.Where(x => x.XValue >= mouse_Xvalue)
.DefaultIfEmpty(chart1.Series[0].Points.First()).First();
DataPoint Next_DataPoint = chart1.Series[0].Points.Select(x => x)
.Where(x => x.XValue <= mouse_Xvalue)
.DefaultIfEmpty(chart1.Series[0].Points.Last()).Last();
double diff_prev = Math.Abs(Prev_DataPoint.XValue - mouse_Xvalue);
double diff_next = Math.Abs(Next_DataPoint.XValue - mouse_Xvalue);
int zoffset = 15;
int setindexX = diff_prev < diff_next ?
chart1.Series[0].Points.IndexOf(Prev_DataPoint)
: chart1.Series[0].Points.IndexOf(Next_DataPoint);
int setXmin = (setindexX - zoffset) >= 0 ? (setindexX - zoffset)
: 0;
int setXmax = (setindexX + zoffset) < chart1.Series[0].Points.Count
? (setindexX + zoffset)
: chart1.Series[0].Points.Count - 1;
if (zoomchart.Series.Count > 0)
zoomchart.Series.Clear();
Series series = new Series();
Series series2 = new Series();
series.Points.Clear();
series2.Points.Clear();
for (int i = setXmin; i <= setXmax; i++)
series.Points.AddXY(chart1.Series[0].Points[i].XValue,
chart1.Series[0].Points[i].YValues[0]);
series.Color = chart1.Series[0].Color;
series.ChartType = SeriesChartType.Line;
series2.Points.AddXY(chart1.Series[0].Points[setindexX].XValue,
chart1.Series[0].Points[setindexX].YValues[0]);
series2.Color = Color.Red;
series2.ChartType = SeriesChartType.Point;
series2.Points[0].Label = series2.Points[0].XValue.ToString("F2") + ", "
+ series2.Points[0].YValues[0].ToString("F2");
zoomchart.Series.Add(series);
zoomchart.Series.Add(series2);
zoomchart.Invalidate();
zoomchart.ChartAreas[0].AxisX.Minimum = series.Points[0].XValue;
zoomchart.ChartAreas[0].AxisX.Maximum = series.Points.FindMaxByValue("X").XValue;
zoomchart.ChartAreas[0].AxisY.Minimum = series.Points.FindMinByValue().YValues[0];
zoomchart.ChartAreas[0].AxisY.Maximum = series.Points.FindMaxByValue().YValues[0];
}