Вот решение, которое выполняет то, о чем просила опера: Создайте PictureBox
, который показывает увеличенную часть Chart
, которая будет перемещаться при перемещении по диаграмме.
Выглядит хорошо, но все равно придется перемещаться по этим крошечным не увеличенным пикселям ..
Вот как это делается и настраивается:
При необходимости PictureBox zoomPBox
должен быть настроен снова;настройка включает в себя несколько измерений и создание скриншота Chart
.Для этого диаграмма временно увеличивается , а затем сбрасывается до исходного размера.
Примечание : Всякий раз, когда диаграмма изменяется или изменяется любым другим способом, процедура установки должнавызываться снова.
PictureBox zoomPBox
установлен на SizeMode Normal
и вложен в Panel
.В настройках мы увеличиваем zoomPBox
, чтобы вместить все Bitmap
.Panel zoomPanel
имеет AutoScroll = false
, чтобы избежать полос прокрутки.
Одна сложность - это автоматическое определение размера , которое делает элемент управления Chart.При его увеличении содержимое увеличивается, но, например, ни один из шрифтов не увеличивается.Это приводит к различным соотношениям сторон между нормалью и увеличенной областью графика.Чтобы синхронизировать движение, у нас не может быть этого.Поэтому мы не только хотим вырезать фактическую внутреннюю область графика без Legend
, Title
или Axes
из увеличенного скриншота, но также растянуть до такого же соотношения сторонкак необъятная область сюжета ..
Вот результат:
Код для 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();
}
Как видите, I Invalidate
the 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
в процентах соответствующей области контейнера.) Я использую функции, которые я разместил ранее, например, здесь .