在c#中缩放到一个点
我正在尝试编写一个自定义可滚动 C# 控件,该控件可缩放到图像上的特定点。 我面临的问题是,当启用双缓冲时,图像似乎会向左上角猛拉,然后正确缩放到单击鼠标的位置。 这似乎只在我设置 AutoScrollPosition 时发生。 我验证了我的 OnPaint 方法中没有发生这种情况。 这似乎是一些我无法追踪的内部行为。 有人解决这个问题了吗?
这是一些示例代码,演示了我想要完成的任务。 仅当图像相当大时,该问题似乎才会明显地呈现给用户。
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Drawing;
namespace Zoom
{
public class PointZoom : ScrollableControl
{
#region Private Data
private float _zoom = 1.0f;
private PointF _origin = PointF.Empty;
private Image _image;
private Matrix _transform = new Matrix();
#endregion
public PointZoom() {
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.AutoScroll = true;
UpdateScroll();
}
public Image Image {
get {
return _image;
}
set {
_image = value;
_origin = PointF.Empty;
_zoom = 1.0F;
UpdateScroll();
Invalidate();
}
}
protected override void OnPaintBackground(PaintEventArgs e) {
// don't allow the background to be painted
}
protected override void OnPaint(PaintEventArgs e) {
Graphics g = e.Graphics;
ClearBackground(g);
float dx = -_origin.X;
float dy = -_origin.Y;
_transform = new Matrix(_zoom, 0, 0, _zoom, dx, dy);
g.Transform = _transform;
DrawImage(g);
}
private void ClearBackground(Graphics g) {
g.Clear(SystemColors.Window);
}
protected override void OnScroll(ScrollEventArgs se) {
if (se.ScrollOrientation == ScrollOrientation.HorizontalScroll) {
_origin.X += se.NewValue - se.OldValue;
}
else {
_origin.Y += se.NewValue - se.OldValue;
}
Invalidate();
base.OnScroll(se);
}
protected override void OnMouseClick(MouseEventArgs e) {
ZoomToPoint(e.Location);
Invalidate();
}
private void UpdateScroll() {
if (_image != null) {
Size scrollSize = new Size(
(int)Math.Round(_image.Width * _zoom),
(int)Math.Round(_image.Height * _zoom));
Point position = new Point(
(int)Math.Round(_origin.X),
(int)Math.Round(_origin.Y));
this.AutoScrollPosition = position;
this.AutoScrollMinSize = scrollSize;
}
else {
this.AutoScrollMargin = this.Size;
}
}
private void ZoomToPoint(Point viewPoint) {
PointF modelPoint = ToModelPoint(viewPoint);
// Increase the zoom
_zoom *= 1.25F;
// calculate the new origin
_origin.X = (modelPoint.X * _zoom) - viewPoint.X;
_origin.Y = (modelPoint.Y * _zoom) - viewPoint.Y;
UpdateScroll();
}
private PointF ToModelPoint(Point viewPoint) {
PointF modelPoint = new PointF();
modelPoint.X = (_origin.X + viewPoint.X) / _zoom;
modelPoint.Y = (_origin.Y + viewPoint.Y) / _zoom;
return modelPoint;
}
private void DrawImage(Graphics g) {
if (null != _image) {
// set the transparency color for the image
ImageAttributes attr = new ImageAttributes();
attr.SetColorKey(Color.White, Color.White);
Rectangle destRect = new Rectangle(0, 0, _image.Width, _image.Height);
g.DrawImage(_image, destRect, 0, 0, _image.Width, _image.Height, GraphicsUnit.Pixel, attr);
}
}
protected override void Dispose(bool disposing) {
if (disposing) {
if (null != _image) {
_image.Dispose();
_image = null;
}
}
base.Dispose(disposing);
}
}
}
I am attempting to write a custom scrollable c# control that zooms to a particular point on an image. The problem that I am facing is that when double buffering is enabled, the image seems to jerk towards the top left corner and then zooms properly to the point where the mouse was clicked. This seems to only happen when I set the AutoScrollPosition. I verified that it does not happen in my OnPaint method. It appears to be some internal behavior that I cannot track down. Has anyone resolved this issue?
Here's some example code that demonstrates what I am trying to accomplish. The problem only seems to present itself noticeably to the user when the image is fairly large.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Drawing;
namespace Zoom
{
public class PointZoom : ScrollableControl
{
#region Private Data
private float _zoom = 1.0f;
private PointF _origin = PointF.Empty;
private Image _image;
private Matrix _transform = new Matrix();
#endregion
public PointZoom() {
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.AutoScroll = true;
UpdateScroll();
}
public Image Image {
get {
return _image;
}
set {
_image = value;
_origin = PointF.Empty;
_zoom = 1.0F;
UpdateScroll();
Invalidate();
}
}
protected override void OnPaintBackground(PaintEventArgs e) {
// don't allow the background to be painted
}
protected override void OnPaint(PaintEventArgs e) {
Graphics g = e.Graphics;
ClearBackground(g);
float dx = -_origin.X;
float dy = -_origin.Y;
_transform = new Matrix(_zoom, 0, 0, _zoom, dx, dy);
g.Transform = _transform;
DrawImage(g);
}
private void ClearBackground(Graphics g) {
g.Clear(SystemColors.Window);
}
protected override void OnScroll(ScrollEventArgs se) {
if (se.ScrollOrientation == ScrollOrientation.HorizontalScroll) {
_origin.X += se.NewValue - se.OldValue;
}
else {
_origin.Y += se.NewValue - se.OldValue;
}
Invalidate();
base.OnScroll(se);
}
protected override void OnMouseClick(MouseEventArgs e) {
ZoomToPoint(e.Location);
Invalidate();
}
private void UpdateScroll() {
if (_image != null) {
Size scrollSize = new Size(
(int)Math.Round(_image.Width * _zoom),
(int)Math.Round(_image.Height * _zoom));
Point position = new Point(
(int)Math.Round(_origin.X),
(int)Math.Round(_origin.Y));
this.AutoScrollPosition = position;
this.AutoScrollMinSize = scrollSize;
}
else {
this.AutoScrollMargin = this.Size;
}
}
private void ZoomToPoint(Point viewPoint) {
PointF modelPoint = ToModelPoint(viewPoint);
// Increase the zoom
_zoom *= 1.25F;
// calculate the new origin
_origin.X = (modelPoint.X * _zoom) - viewPoint.X;
_origin.Y = (modelPoint.Y * _zoom) - viewPoint.Y;
UpdateScroll();
}
private PointF ToModelPoint(Point viewPoint) {
PointF modelPoint = new PointF();
modelPoint.X = (_origin.X + viewPoint.X) / _zoom;
modelPoint.Y = (_origin.Y + viewPoint.Y) / _zoom;
return modelPoint;
}
private void DrawImage(Graphics g) {
if (null != _image) {
// set the transparency color for the image
ImageAttributes attr = new ImageAttributes();
attr.SetColorKey(Color.White, Color.White);
Rectangle destRect = new Rectangle(0, 0, _image.Width, _image.Height);
g.DrawImage(_image, destRect, 0, 0, _image.Width, _image.Height, GraphicsUnit.Pixel, attr);
}
}
protected override void Dispose(bool disposing) {
if (disposing) {
if (null != _image) {
_image.Dispose();
_image = null;
}
}
base.Dispose(disposing);
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
尝试以下操作。 这是我为在工作项目中使用而编写的。 我删除了一些额外的功能,但这里的功能超出了回答您的问题所需的功能。 特别值得注意的是 CenterOn 和 Zoom 方法。 另请注意,我没有清除背景,而是先绘制背景。 Clear 对我来说也有奇怪的副作用。 我还继承了面板,它也最适合我。
请随意将其转换为 C#。
Try the following. This is one I wrote for use in a project at work. I stripped out some of the extra functionality, but there's more here than needs to be to answer your question. Of particular note for you are the CenterOn and Zoom methods. Also notice I'm not clearing the background, but painting a background first. Clear had weird side effects for me too. I'm also inheriting from Panel which worked best for me as well.
Feel free to convert it to C#.