为 mschart 创建缩放图片框
To create a zoom picturebox for mschart
我想为 mschart 创建一个像 PictureBox Zoom 中那样的缩放图片框。当我将鼠标悬停在图表上时,图片框应该显示缩放视图,这样我就可以 select 一个合适的点。这会发生在 winforms C#.net 中吗?
这是一个解决方案,可以满足 op 的要求:创建一个 PictureBox
,显示 Chart
的 zoomed 部分,它将作为一个整体移动在图表中移动。
看起来不错,但仍然需要移动那些未缩放的微小像素..
这是如何完成和设置的:
必要时必须重新设置 PictureBox zoomPBox
;设置它需要进行一些 测量 并创建 Chart
的屏幕截图。为此,图表暂时放大,然后重置为原始大小。
注意:每当图表以任何其他方式调整大小或更改时,都必须再次调用设置例程。
PictureBox zoomPBox
设置为 SizeMode Normal
并且 嵌套 在 Panel
中。在设置中,我们放大 zoomPBox
以容纳整个 Bitmap
。 Panel zoomPanel
有 AutoScroll = false
以避免滚动条。
一个并发症是图表控件的自动调整大小。放大时,内容会放大,但例如none 的字体是。这导致正常绘图区域和缩放绘图区域之间 不同的纵横比 。为了保持运动同步,我们不能这样做。因此,我们不仅要 剪掉 没有 Legend
、Title
或 Axes
的实际内部绘图区域,而且还要 [=64] =]将其拉伸至与未缩放绘图区域相同的纵横比..
结果如下:
MouseMove
的代码没那么复杂..:[=38=]
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
的像素大小; (MSChart
中的 ElementPosition
在相应容器区域的 百分比 中包含 Location
和 Size
。)我使用我发布的功能之前,例如.
另一个解决方案是使用图表控件作为缩放视图。当鼠标移到原始图表上时,您可以查看以红色标记的数据点。如下所示:
代码如下:
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];
}
我想为 mschart 创建一个像 PictureBox Zoom 中那样的缩放图片框。当我将鼠标悬停在图表上时,图片框应该显示缩放视图,这样我就可以 select 一个合适的点。这会发生在 winforms C#.net 中吗?
这是一个解决方案,可以满足 op 的要求:创建一个 PictureBox
,显示 Chart
的 zoomed 部分,它将作为一个整体移动在图表中移动。
看起来不错,但仍然需要移动那些未缩放的微小像素..
这是如何完成和设置的:
必要时必须重新设置 PictureBox zoomPBox
;设置它需要进行一些 测量 并创建 Chart
的屏幕截图。为此,图表暂时放大,然后重置为原始大小。
注意:每当图表以任何其他方式调整大小或更改时,都必须再次调用设置例程。
PictureBox zoomPBox
设置为 SizeMode Normal
并且 嵌套 在 Panel
中。在设置中,我们放大 zoomPBox
以容纳整个 Bitmap
。 Panel zoomPanel
有 AutoScroll = false
以避免滚动条。
一个并发症是图表控件的自动调整大小。放大时,内容会放大,但例如none 的字体是。这导致正常绘图区域和缩放绘图区域之间 不同的纵横比 。为了保持运动同步,我们不能这样做。因此,我们不仅要 剪掉 没有 Legend
、Title
或 Axes
的实际内部绘图区域,而且还要 [=64] =]将其拉伸至与未缩放绘图区域相同的纵横比..
结果如下:
MouseMove
的代码没那么复杂..:[=38=]
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
的像素大小; (MSChart
中的 ElementPosition
在相应容器区域的 百分比 中包含 Location
和 Size
。)我使用我发布的功能之前,例如
另一个解决方案是使用图表控件作为缩放视图。当鼠标移到原始图表上时,您可以查看以红色标记的数据点。如下所示:
代码如下:
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];
}