在 C# Winforms 中放大到面板的中间
Zoom into the middle of a panel in C# Winforms
我有一个可以放大和缩小的应用程序,唯一的问题是我想放大和缩小但聚焦在面板位置的当前中间,而不是使用鼠标位置
有什么想法吗?
private void panel1_Paint(object sender, PaintEventArgs e)
{
SolidBrush brushs = new SolidBrush(Color.White);
e.Graphics.Clip = new Region(new Rectangle(0, 0, Viewer.Width, Viewer.Height));
e.Graphics.FillRegion(brushs, e.Graphics.Clip);
Graphics g = e.Graphics;
g.TranslateTransform(_ImgX, _ImgY);
g.ScaleTransform(_Zoom, _Zoom);
g.SmoothingMode = SmoothingMode.AntiAlias;
SolidBrush myBrush = new SolidBrush(Color.Black);
Pen p = new Pen(Color.Red);
foreach (CircuitData.ResistorRow resistorRow in ResistorData.Resistor)
{
RectangleF rec = new RectangleF((float)(resistorRow.CenterX - resistorRow.Length/ 2), (float)(resistorRow.CenterY - resistorRow.Width/ 2), (float)resistorRow.Length, (float)resistorRow.Width);
float orientation = 360 - (float)resistorRow.Orientation;
PointF center = new PointF((float)resistorRow.CenterX, (float)resistorRow.CenterY);
PointF[] points = CreatePolygon(rec, center, orientation);
if (!Double.IsNaN(resistorRow.HiX) && !Double.IsNaN(resistorRow.HiY))
{
g.FillEllipse(myBrush, (float)resistorRow.HiX - 2 , (float)resistorRow.HiY - 2, 4, 4);
g.DrawLine(p, new PointF((float)resistorRow.HiX , (float)resistorRow.HiY ), center);
}
g.FillPolygon(myBrush, points);
}
}
更新
缩放功能,我知道这是错误的,但如果有人可以帮助我修复逻辑或更好的想法。
private void trackBar1_Scroll(object sender, EventArgs e)
{
float oldZoom = _Zoom;
_Zoom = zoomTrackPad.Value / 10f;
int x = Math.Abs(Viewer.Width / 2 );
int y = Math.Abs(Viewer.Height / 2 );
int oldImageX = (int)(x / oldZoom);
int oldImageY = (int)(y / oldZoom);
int newImageX = (int)(x / _Zoom);
int newImageY = (int)(y / _Zoom);
_ImgX = newImageX - oldImageX + _ImgX;
_ImgY = newImageY - oldImageY + _ImgY;
Viewer.Invalidate();
}
谢谢
正如你所说,你的缩放逻辑是错误的。由于转换矩阵的工作方式,这是错误的。
变换矩阵一个接一个地附加,在你的情况下,你正在平移图像,就好像它已经缩放过然后缩放它,这将改变缩放原点并且不会居中。
我将在这里假设一些事情,如果有任何错误请评论。
假设您的可见区域(面板大小)为 100 * 100 像素,而您想要 "virtual" 内容大小为 200 * 200 像素。我认为你有两个滚动条来置换面板内的内容,如果你只是使用面板大小并且没有滚动条那么你应该调整你计算位移的方式。
因此,您的滚动条范围从 0 到(面板。(with/height)- 内容。(width/height)),在这种情况下,范围从 0 到 100。
首先让我们的内容在开头居中。为了使它居中,我们将 TranslateTransform 应用于 Graphics 对象,其中可见区域大小的一半减去虚拟区域大小的一半:
e.Graphics.TranslateTransform(panel.With / 2 - content.Width / 2, panel.Height / 2 - content.Height / 2);
这将使绘图居中,但是,嘿!我们没有使用定位滚动条!
Okokoko,让我们应用这些滚动条偏移量。首先,当我们将内容居中时,将这些滚动条的值设置为其最大值的一半,在我们的示例中,我们应将其设置为 50。
现在,正如我们所说,附加了转换,因此,没有什么能阻止您添加新的 TranslateTransform。您可以在一次调用中完成此操作,但为简单起见,我们在两次调用中执行此操作(另外,稍后应用缩放会更好)。
e.Graphics.TranslateTransform(panel.With / 2 - content.Width / 2, panel.Height / 2 - content.Height / 2);
e.Graphics.TranslateTransform(hScrollBar1.Value - (hScrollbar1.Maximum / 2), vScrollBar1.Value - (vScrollbar1.Maximum / 2));
如您所见,我们正在减去最大值的一半,我们已经将内容居中,因此当滚动条位于中心时,我们需要应用 0 的转换,我们的范围从 -Maximum / 2 到 Maximum / 2,在这个例子中是从 -50 到 50。
好吧,我们现在在屏幕中心有一个虚拟内容区域,我们可以在上面移动,现在我们需要应用缩放。
我看到你有一个滚动条,所以让我们按照我们的示例使用它。
滚动条的范围由您选择,我看到您使用的是 value / 10f,所以如果您的滚动条范围从 1 到 100,您将有一个从 0.1 到 10 的缩放,这是一个不错的开始选择.
现在,让我们将此滚动条设置为 10,以最初进行单一缩放。
现在有趣的部分来了,变换是按照您设置的顺序应用的,所以在翻译之前应用缩放与应用缩放是不一样的之后.
在我们的例子中,我们首先要将内容居中,转换到我们想要看到的位置(滚动条值),然后缩放。
e.Graphics.TranslateTransform(panel.With / 2 - content.Width / 2, panel.Height / 2 - content.Height / 2);
e.Graphics.TranslateTransform(hScrollBar1.Value - (hScrollbar1.Maximum / 2), vScrollBar1.Value - (vScrollbar1.Maximum / 2));
float scale = zScrollBar.Value / 10f;
e.Graphics.ScaleTransform(scale, scale);
好的,听起来不错,但是等等!它会惨败。为什么?因为当我们缩放时,我们的虚拟大小也会根据视图区域(面板大小)按比例放大,所以我们需要将此比例应用于内容大小(以及滚动条值):
float scale = zScrollBar.Value / 10f;
e.Graphics.TranslateTransform(panel.With / 2 - ((content.Width / 2) * scale), panel.Height / 2 - ((content.Height / 2) * scale));
e.Graphics.TranslateTransform((hScrollBar1.Value - (hScrollbar1.Maximum / 2)) * scale, (vScrollBar1.Value - (vScrollbar1.Maximum / 2)) * scale);
e.Graphics.ScaleTransform(scale, scale);
最后我们有一个完全可用的虚拟区域,您可以在其中缩放和移动它的边界。
我对图形有点生疏,所以也许有些计算是错误的,我希望不会,无论如何这会给你一个关于如何做到这一点的大概想法。
干杯!
P.D.: 如果你不想移动虚拟区域而只是放大面板的中心,跳过第二个平移变换,如果你想放大当前虚拟区域的可见中心只使用位移(不缩放它,变换已经有逻辑)而不是滚动条值。
我有一个可以放大和缩小的应用程序,唯一的问题是我想放大和缩小但聚焦在面板位置的当前中间,而不是使用鼠标位置
有什么想法吗?
private void panel1_Paint(object sender, PaintEventArgs e)
{
SolidBrush brushs = new SolidBrush(Color.White);
e.Graphics.Clip = new Region(new Rectangle(0, 0, Viewer.Width, Viewer.Height));
e.Graphics.FillRegion(brushs, e.Graphics.Clip);
Graphics g = e.Graphics;
g.TranslateTransform(_ImgX, _ImgY);
g.ScaleTransform(_Zoom, _Zoom);
g.SmoothingMode = SmoothingMode.AntiAlias;
SolidBrush myBrush = new SolidBrush(Color.Black);
Pen p = new Pen(Color.Red);
foreach (CircuitData.ResistorRow resistorRow in ResistorData.Resistor)
{
RectangleF rec = new RectangleF((float)(resistorRow.CenterX - resistorRow.Length/ 2), (float)(resistorRow.CenterY - resistorRow.Width/ 2), (float)resistorRow.Length, (float)resistorRow.Width);
float orientation = 360 - (float)resistorRow.Orientation;
PointF center = new PointF((float)resistorRow.CenterX, (float)resistorRow.CenterY);
PointF[] points = CreatePolygon(rec, center, orientation);
if (!Double.IsNaN(resistorRow.HiX) && !Double.IsNaN(resistorRow.HiY))
{
g.FillEllipse(myBrush, (float)resistorRow.HiX - 2 , (float)resistorRow.HiY - 2, 4, 4);
g.DrawLine(p, new PointF((float)resistorRow.HiX , (float)resistorRow.HiY ), center);
}
g.FillPolygon(myBrush, points);
}
}
更新 缩放功能,我知道这是错误的,但如果有人可以帮助我修复逻辑或更好的想法。
private void trackBar1_Scroll(object sender, EventArgs e)
{
float oldZoom = _Zoom;
_Zoom = zoomTrackPad.Value / 10f;
int x = Math.Abs(Viewer.Width / 2 );
int y = Math.Abs(Viewer.Height / 2 );
int oldImageX = (int)(x / oldZoom);
int oldImageY = (int)(y / oldZoom);
int newImageX = (int)(x / _Zoom);
int newImageY = (int)(y / _Zoom);
_ImgX = newImageX - oldImageX + _ImgX;
_ImgY = newImageY - oldImageY + _ImgY;
Viewer.Invalidate();
}
谢谢
正如你所说,你的缩放逻辑是错误的。由于转换矩阵的工作方式,这是错误的。
变换矩阵一个接一个地附加,在你的情况下,你正在平移图像,就好像它已经缩放过然后缩放它,这将改变缩放原点并且不会居中。
我将在这里假设一些事情,如果有任何错误请评论。
假设您的可见区域(面板大小)为 100 * 100 像素,而您想要 "virtual" 内容大小为 200 * 200 像素。我认为你有两个滚动条来置换面板内的内容,如果你只是使用面板大小并且没有滚动条那么你应该调整你计算位移的方式。
因此,您的滚动条范围从 0 到(面板。(with/height)- 内容。(width/height)),在这种情况下,范围从 0 到 100。
首先让我们的内容在开头居中。为了使它居中,我们将 TranslateTransform 应用于 Graphics 对象,其中可见区域大小的一半减去虚拟区域大小的一半:
e.Graphics.TranslateTransform(panel.With / 2 - content.Width / 2, panel.Height / 2 - content.Height / 2);
这将使绘图居中,但是,嘿!我们没有使用定位滚动条!
Okokoko,让我们应用这些滚动条偏移量。首先,当我们将内容居中时,将这些滚动条的值设置为其最大值的一半,在我们的示例中,我们应将其设置为 50。
现在,正如我们所说,附加了转换,因此,没有什么能阻止您添加新的 TranslateTransform。您可以在一次调用中完成此操作,但为简单起见,我们在两次调用中执行此操作(另外,稍后应用缩放会更好)。
e.Graphics.TranslateTransform(panel.With / 2 - content.Width / 2, panel.Height / 2 - content.Height / 2);
e.Graphics.TranslateTransform(hScrollBar1.Value - (hScrollbar1.Maximum / 2), vScrollBar1.Value - (vScrollbar1.Maximum / 2));
如您所见,我们正在减去最大值的一半,我们已经将内容居中,因此当滚动条位于中心时,我们需要应用 0 的转换,我们的范围从 -Maximum / 2 到 Maximum / 2,在这个例子中是从 -50 到 50。
好吧,我们现在在屏幕中心有一个虚拟内容区域,我们可以在上面移动,现在我们需要应用缩放。
我看到你有一个滚动条,所以让我们按照我们的示例使用它。
滚动条的范围由您选择,我看到您使用的是 value / 10f,所以如果您的滚动条范围从 1 到 100,您将有一个从 0.1 到 10 的缩放,这是一个不错的开始选择.
现在,让我们将此滚动条设置为 10,以最初进行单一缩放。
现在有趣的部分来了,变换是按照您设置的顺序应用的,所以在翻译之前应用缩放与应用缩放是不一样的之后.
在我们的例子中,我们首先要将内容居中,转换到我们想要看到的位置(滚动条值),然后缩放。
e.Graphics.TranslateTransform(panel.With / 2 - content.Width / 2, panel.Height / 2 - content.Height / 2);
e.Graphics.TranslateTransform(hScrollBar1.Value - (hScrollbar1.Maximum / 2), vScrollBar1.Value - (vScrollbar1.Maximum / 2));
float scale = zScrollBar.Value / 10f;
e.Graphics.ScaleTransform(scale, scale);
好的,听起来不错,但是等等!它会惨败。为什么?因为当我们缩放时,我们的虚拟大小也会根据视图区域(面板大小)按比例放大,所以我们需要将此比例应用于内容大小(以及滚动条值):
float scale = zScrollBar.Value / 10f;
e.Graphics.TranslateTransform(panel.With / 2 - ((content.Width / 2) * scale), panel.Height / 2 - ((content.Height / 2) * scale));
e.Graphics.TranslateTransform((hScrollBar1.Value - (hScrollbar1.Maximum / 2)) * scale, (vScrollBar1.Value - (vScrollbar1.Maximum / 2)) * scale);
e.Graphics.ScaleTransform(scale, scale);
最后我们有一个完全可用的虚拟区域,您可以在其中缩放和移动它的边界。
我对图形有点生疏,所以也许有些计算是错误的,我希望不会,无论如何这会给你一个关于如何做到这一点的大概想法。
干杯!
P.D.: 如果你不想移动虚拟区域而只是放大面板的中心,跳过第二个平移变换,如果你想放大当前虚拟区域的可见中心只使用位移(不缩放它,变换已经有逻辑)而不是滚动条值。