- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试编写一个自定义可滚动的 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);
}
}
}
最佳答案
尝试以下操作。这是我为工作中的一个项目而写的。我去掉了一些额外的功能,但这里有比回答您的问题所需的更多的功能。您需要特别注意的是 CenterOn 和 Zoom 方法。另请注意,我没有清除背景,而是先绘制背景。 Clear 对我也有奇怪的副作用。我也继承自 Panel,它也最适合我。随意将其转换为 C#。
Imports System.Drawing.Drawing2D
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Public Class ctlViewer
Inherits Panel
Protected Const C_SmallChangePercent As Integer = 2
Protected Const C_LargeChangePercent As Integer = 10
Protected mimgImage As Image
Protected mintActiveFrame As Integer
Protected mdecZoom As Decimal
Protected mpntUpperLeft As New Point
Protected mpntCenter As New Point
Protected mblnDragging As Boolean = False
Private mButtons As MouseButtons
#Region " Constructor"
Public Sub New()
MyBase.New()
Me.SetStyle(ControlStyles.ContainerControl, False)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.SetStyle(ControlStyles.UserPaint, True)
Me.SetStyle(ControlStyles.ResizeRedraw, True)
Me.SetStyle(ControlStyles.UserPaint, True)
Me.SetStyle(ControlStyles.DoubleBuffer, True)
ZoomFactor = 1.0
Me.AutoScroll = True
Me.BackColor = Color.FromKnownColor(KnownColor.ControlDark)
End Sub
#End Region
#Region " Properties"
''' <summary>
''' Image object representing the TIFF image.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property Image() As Image
Get
Return mimgImage
End Get
Set(ByVal Value As Image)
AutoScrollPosition = New Point(0, 0)
mimgImage = Value
RaiseEvent ImageLoaded(New ImageLoadedEventArgs(Value))
UpdateScaleFactor()
Invalidate()
End Set
End Property
''' <summary>
''' Viewing area of image
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property ViewPort() As Rectangle
Get
Dim r As New Rectangle
Dim pul As Point = Me.CoordViewerToSrc(New Point(0, 0))
Dim pbr As Point = Me.CoordViewerToSrc(New Point(Me.Width, Me.Height))
r.Location = pul
r.Width = pbr.X - pul.X
r.Height = pbr.Y - pul.Y
Return r
End Get
End Property
''' <summary>
''' Gets or sets the zoom / scale factor for the image being displayed.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property ZoomFactor() As Decimal
Get
Return mdecZoom
End Get
Set(ByVal Value As Decimal)
If Value < 0 OrElse Value < 0.00001 Then
Value = 0.00001F
End If
mdecZoom = Value
UpdateScaleFactor()
Invalidate()
RaiseEvent ZoomChanged(New ImageViewerEventArgs(Me.Image))
End Set
End Property
#End Region
#Region " Event Signatures"
Public Event ImageMouseDown(ByVal e As ImageMouseEventArgs)
Public Event ImageMouseMove(ByVal e As ImageMouseEventArgs)
Public Event ImageMouseUp(ByVal e As ImageMouseEventArgs)
Public Event ImageLoaded(ByVal e As ImageLoadedEventArgs)
Public Event ZoomChanged(ByVal e As ImageViewerEventArgs)
Public Event ImageViewPortChanged(ByVal e As ImageViewerEventArgs)
Public Event ViewerPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
#End Region
#Region " Public Subs/Functions"
''' <summary>
''' Pans the viewer by X,Y up to the bounds of the image.
''' </summary>
''' <param name="x"></param>
''' <param name="y"></param>
''' <remarks></remarks>
Public Sub Pan(ByVal x As Integer, ByVal y As Integer)
Me.AutoScrollPosition = New Point(Math.Abs(Me.AutoScrollPosition.X) + x, Math.Abs(Me.AutoScrollPosition.Y) + y)
Me.Invalidate()
End Sub
''' <summary>
''' Zoom image
''' </summary>
''' <param name="decZoom"></param>
''' <remarks></remarks>
Public Sub Zoom(ByVal decZoom As Decimal)
ZoomFactor = decZoom
End Sub
''' <summary>
''' Zoom image and scroll to rectangle coordinates.
''' </summary>
''' <param name="decZoomFactor"></param>
''' <param name="objRectangleToCenter"></param>
''' <remarks></remarks>
Public Sub Zoom(ByVal decZoomFactor As Decimal, ByVal objRectangleToCenter As Rectangle)
Dim intCenterX As Int32 = objRectangleToCenter.X + objRectangleToCenter.Width / 2
Dim intCenterY As Int32 = objRectangleToCenter.Y + objRectangleToCenter.Height / 2
Me.CenterOn(New Point(intCenterX, intCenterY))
Me.ZoomFactor = decZoomFactor
End Sub
''' <summary>
''' Zoom to fit image on screen.
''' </summary>
''' <param name="minZoom"></param>
''' <param name="maxZoom"></param>
''' <remarks></remarks>
Public Sub ZoomToFit(ByVal minZoom As Decimal, ByVal maxZoom As Decimal)
If Not Me.Image Is Nothing Then
Dim ItoVh As Single = Me.Image.Height / (Me.Height - 2)
Dim ItoVw As Single = Me.Image.Width / (Me.Width - 2)
Dim zf As Single = 1 / Math.Max(ItoVh, ItoVw)
If (((zf > minZoom) And minZoom <> 0) Or minZoom = 0) _
And ((zf < maxZoom) And maxZoom <> 0) Or maxZoom = 0 Then
Me.Zoom(zf)
End If
End If
End Sub
''' <summary>
''' Zoom to fit width of image
''' </summary>
''' <param name="minZoom"></param>
''' <param name="maxZoom"></param>
''' <remarks></remarks>
Public Sub ZoomToWidth(ByVal minZoom As Decimal, ByVal maxZoom As Decimal)
If Image Is Nothing Then
Me.AutoScrollMargin = Me.Size
Me.AutoScrollMinSize = Me.Size
mpntCenter = New Point(0, 0)
mpntUpperLeft = New Point(0, 0)
Exit Sub
End If
Dim intOff As Integer = 0
If ScrollStateVScrollVisible Then
intOff = ScrollStateVScrollVisible
End If
Dim ItoVw As Single = Me.Image.Width / (Me.Width - 2)
Dim zf As Single = 1 / ItoVw
If (Me.Image.Height * zf) >= Me.Height Then
ItoVw = Me.Image.Width / (Me.Width - 22)
zf = 1 / ItoVw
End If
If (((zf > minZoom) And minZoom <> 0) Or minZoom = 0) _
And ((zf < maxZoom) And maxZoom <> 0) Or maxZoom = 0 Then
Me.Zoom(zf)
End If
End Sub
''' <summary>
''' Adjust scrollbars to zoomed size of image
''' </summary>
''' <remarks></remarks>
Protected Sub UpdateScaleFactor()
If Image Is Nothing Then
Me.AutoScrollMargin = Me.Size
Me.AutoScrollMinSize = Me.Size
mpntCenter = New Point(0, 0)
mpntUpperLeft = New Point(0, 0)
Else
Me.AutoScrollMinSize = New Size(CInt(Me.Image.Width * ZoomFactor + 0.5F), CInt(Me.Image.Height * ZoomFactor + 0.5F))
End If
Me.HorizontalScroll.LargeChange = Me.Size.Width * (C_LargeChangePercent / 100)
Me.VerticalScroll.LargeChange = Me.Size.Height * (C_LargeChangePercent / 100)
Me.HorizontalScroll.SmallChange = Me.Size.Width * (C_SmallChangePercent / 100)
Me.VerticalScroll.SmallChange = Me.Size.Height * (C_SmallChangePercent / 100)
End Sub
''' <summary>
''' Convert a point of the original image to screen coordinates adjusted for zoom and pan.
''' </summary>
''' <param name="pntPoint"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function CoordSrcToViewer(ByVal pntPoint As Point) As Point
Dim pntResult As New Point
pntResult.X = pntPoint.X * Me.ZoomFactor + Me.AutoScrollPosition.X
pntResult.Y = pntPoint.Y * Me.ZoomFactor + Me.AutoScrollPosition.Y
Return pntResult
End Function
''' <summary>
''' Convert a screen point to the corrseponding coordinate of the original image.
''' </summary>
''' <param name="pntPoint"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function CoordViewerToSrc(ByVal pntPoint As Point) As Point
Dim pntResult As New Point
pntResult.X = (pntPoint.X - Me.AutoScrollPosition.X) / Me.ZoomFactor
pntResult.Y = (pntPoint.Y - Me.AutoScrollPosition.Y) / Me.ZoomFactor
Return pntResult
End Function
''' <summary>
''' Returns an offset needed to move the center point to make visible.
''' </summary>
''' <param name="imagePoint"></param>
''' <returns></returns>
''' <remarks></remarks>
Friend Function PointIsVisible(ByVal imagePoint As Point) As Point
Dim pntViewer As Point = Me.CoordSrcToViewer(imagePoint)
Dim pntSize As New Point((pntViewer.X - Me.Width) / Me.ZoomFactor, (pntViewer.Y - Me.Height) / Me.ZoomFactor)
If pntViewer.X > 0 And pntViewer.X < Me.Width Then
pntSize.X = 0
End If
If pntViewer.Y > 0 And pntViewer.Y < Me.Height Then
pntSize.Y = 0
End If
If pntViewer.X < 0 Then
pntSize.X = pntViewer.X
End If
If pntViewer.Y < 0 Then
pntSize.Y = pntViewer.Y
End If
Return pntSize
End Function
''' <summary>
''' Centers view on coordinates of the original image.
''' </summary>
''' <param name="X"></param>
''' <param name="Y"></param>
''' <remarks></remarks>
Public Sub CenterOn(ByVal X As Integer, ByVal Y As Integer)
CenterOn(New Point(X, Y))
End Sub
''' <summary>
''' Centers view on a point of the original image.
''' </summary>
''' <param name="pntCenter"></param>
''' <remarks></remarks>
Public Sub CenterOn(ByVal pntCenter As Point)
Dim midX As Integer = Me.Width / 2
Dim midY As Integer = Me.Height / 2
Dim intX As Integer = (pntCenter.X * ZoomFactor - midX)
Dim intY As Integer = (pntCenter.Y * ZoomFactor - midY)
Me.AutoScrollPosition = New Point(intX, intY)
Me.Invalidate()
End Sub
''' <summary>
''' Returns image coordinate which is centered in viewer.
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetCenterPoint() As Point
Dim pntResult As Point
pntResult = CoordViewerToSrc(New Point(Me.Width / 2, Me.Height / 2))
If pntResult.X > Me.Image.Width Or pntResult.Y > Image.Height Then
pntResult = Nothing
End If
Return pntResult
End Function
''' <summary>
''' Fire viewport changed event.
''' </summary>
''' <remarks></remarks>
Private Sub FireViewPortChangedEvent()
Dim e As New ImageViewerEventArgs(Me.Image)
RaiseEvent ImageViewPortChanged(e)
End Sub
Private Sub FireViewerPaintEvent(ByVal e As PaintEventArgs)
RaiseEvent ViewerPaint(Me, e)
End Sub
#End Region
#Region " Overrides"
''' <summary>
''' Paint image in proper position and zoom. All work is done with a Matrix object.
''' The coordinates of the graphics instance of the ctlViewer_OnPaint event
''' are transformed. This allows drawing on the "paper image" rather than "over the viewport"
''' </summary>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
If mimgImage Is Nothing Then
MyBase.OnPaintBackground(e)
Return
Else
Debug.WriteLine("ctl painting")
Dim mx As New Matrix
e.Graphics.FillRectangle(New SolidBrush(Me.BackColor), 0, 0, Me.Width, Me.Height)
mx.Translate(Me.AutoScrollPosition.X, Me.AutoScrollPosition.Y)
mx.Scale(ZoomFactor, ZoomFactor)
e.Graphics.SetClip(New Rectangle(0, 0, Me.Width, Me.Height))
e.Graphics.InterpolationMode = InterpolationMode.Low
e.Graphics.SmoothingMode = SmoothingMode.HighSpeed
e.Graphics.Transform = mx
Dim ia As New ImageAttributes
e.Graphics.DrawImage(Image, _
New Rectangle(-Me.AutoScrollPosition.X / ZoomFactor, _
-Me.AutoScrollPosition.Y / ZoomFactor, _
Me.Width / ZoomFactor, _
Me.Height / ZoomFactor), _
Me.ViewPort.Left, Me.ViewPort.Top, Me.ViewPort.Width, Me.ViewPort.Height, _
GraphicsUnit.Pixel, ia)
ia.Dispose()
End If
Me.mpntCenter = Me.GetCenterPoint
FireViewPortChangedEvent()
MyBase.OnPaint(e)
e.Graphics.ResetClip()
e.Graphics.ResetTransform()
Me.FireViewerPaintEvent(e)
End Sub
''' <summary>
''' Pan image and raise event.
''' </summary>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
Me.mButtons = e.Button
RaiseEvent ImageMouseDown(New ImageMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, Me.ZoomFactor, Me.AutoScrollPosition))
MyBase.OnMouseDown(e)
End Sub
''' <summary>
''' Stop panning image and raise event.
''' </summary>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
Me.Cursor = Cursors.Arrow
MyBase.OnMouseUp(e)
RaiseEvent ImageMouseUp(New ImageMouseEventArgs(e.Button, e.Clicks, e.X, e.Y, e.Delta, Me.ZoomFactor, Me.AutoScrollPosition))
Me.mButtons = Windows.Forms.MouseButtons.None
End Sub
''' <summary>
''' Pan image if PanOnMouseMove is True. Fire the ImageMouseMove event.
''' </summary>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
Static oldX As Integer
Static oldy As Integer
Try
oldX = e.X
oldy = e.Y
Catch ex As Exception
Throw ex
Finally
MyBase.OnMouseMove(e)
RaiseEvent ImageMouseMove(New ImageMouseEventArgs(Me.mButtons, e.Clicks, e.X, e.Y, e.Delta, Me.ZoomFactor, Me.AutoScrollPosition))
End Try
End Sub
''' <summary>
''' Catch a panel scroll event.
''' </summary>
''' <param name="m"></param>
''' <remarks></remarks>
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_VSCROLL As Integer = 277 '115 hex
Const WM_HSCROLL As Integer = 276 '0x114;
MyBase.WndProc(m)
If Not m.HWnd.Equals(Me.Handle) Then
Return
End If
If m.Msg = WM_VSCROLL Or m.Msg = WM_HSCROLL Then
Me.Invalidate()
End If
End Sub
#End Region
End Class
关于c# - 缩放到c#中的一个点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/516958/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!