自定义 Winforms 设计器控件同时缩放和平移控件
Posted
技术标签:
【中文标题】自定义 Winforms 设计器控件同时缩放和平移控件【英文标题】:Zoom and Pan controls simultaneously for a custom Winforms designer control 【发布时间】:2021-10-15 14:59:00 【问题描述】:我正在尝试在 winforms 中创建一个“设计师”。这将向用户呈现一个所见即所得的“画布”,用户可以将图像、文本和形状拖放到该画布上。然后,他们可以通过带有手柄的“选择器”框来选择、移动和调整它们的大小。用户也可以一次选择多个对象,因此需要多个选择器。您还必须能够缩放和平移画布。
必须通过winforms。我决定的方法是在Paint
事件中按程序将所有项目绘制到单个控件(PicturBox
作为画布)上。对于选择器,我将使用放置在 Canvas 顶部的控件作为拖动手柄角的指南。然后我会让这个控件不可见。
我制作了一个自定义的PictureBox
(ScaledPictureBox
),它是可扩展的,并将其放置在启用自动滚动的Panel
中。这很好用 - 实现了缩放和平移。然后我在上面放置了另一个ScaledPictureBox
作为选择器。
下面的代码可以很好地以 100% 平移。但是,当我在缩放时平移时,选择器的位置是关闭的。这是一个视频:
Video
代码如下:
Imports System.Drawing.Drawing2D
Partial Public Class ScaledPictureBox
Inherits PictureBox
Public Property ScaleM As Matrix
Private Property Zoom As Single
Private Property OriginalSize As Size
Public Sub New()
ScaleM = New Matrix()
SizeMode = PictureBoxSizeMode.Zoom
End Sub
Public Sub InitImage()
OriginalSize = Me.Size
Size = OriginalSize
SetZoom(100)
End Sub
Public Sub SetZoom(ByVal zoomfactor As Single)
If zoomfactor <= 0 Then Throw New Exception("Zoom must be positive")
Dim oldZoom As Single = Zoom
Zoom = zoomfactor / 100.0F
ScaleM.Reset()
ScaleM.Scale(Zoom, Zoom)
If OriginalSize <> Size.Empty Then Size = New Size(CInt((OriginalSize.Width * Zoom)), CInt((OriginalSize.Height * Zoom)))
End Sub
Public Function ScalePoint(ByVal pt As PointF) As PointF
Return New PointF(pt.X / Zoom, pt.Y / Zoom)
End Function
End Class
测试表格:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Canvas.InitImage()
Selector.InitImage()
End Sub
Private Sub TrackBar1_Scroll(sender As Object, e As EventArgs) Handles TrackBar1.Scroll
Canvas.SetZoom(CInt(TrackBar1.Value))
Selector.SetZoom(CInt(TrackBar1.Value))
Canvas.Invalidate()
Selector.Invalidate()
End Sub
Dim moving As Boolean = False
Dim CanvasClickPoint As New Point
Dim SelectorLocation As New Point
Private Sub Canvas_MouseDown(sender As Object, e As MouseEventArgs) Handles Canvas.MouseDown
If (ModifierKeys And Keys.Control) = Keys.Control Then
moving = True
CanvasClickPoint = e.Location
SelectorLocation = Selector.Location
End If
End Sub
Private Sub Canvas_MouseMove(sender As Object, e As MouseEventArgs) Handles Canvas.MouseMove
If moving Then
Dim CanvasNewPoint As Point = New Point(Canvas.Left + (e.Location.X - CanvasClickPoint.X),
Canvas.Top + (e.Location.Y - CanvasClickPoint.Y))
SelectorLocation.X = SelectorLocation.X + (e.Location.X - CanvasClickPoint.X)
SelectorLocation.Y = SelectorLocation.Y + (e.Location.Y - CanvasClickPoint.Y)
Dim SelectorLocationPointf As PointF = SelectorLocation
SelectorLocation = Point.Round(Selector.ScalePoint(SelectorLocationPointf))
Selector.Location = SelectorLocation
Canvas.Location = CanvasNewPoint
End If
End Sub
Private Sub Canvas_MouseUp(sender As Object, e As MouseEventArgs) Handles Canvas.MouseUp
moving = False
End Sub
End Class
这是框架讨论的控件位置的测试设置:
我猜这与我对Selector.ScalePoint
的使用有关 - 选择器的位置需要应用一些缩放,就好像你不需要一样,它对于 100% 缩放以外的任何东西都是关闭的。然而,数学和技术超出了我的能力,因为我解除了ScaledPictureBox
的代码,虽然我可以大致理解ScalePoint
背后的想法,但我无法完全理解它。希望有人可以帮忙
Visual Studio 项目下载(小)HERE
【问题讨论】:
你为什么不简单地根据选择的比例调整图片框的大小(在SizeMode = Zoom
,确定你不想拉伸你的图像,对吧?)?目前尚不清楚 PictureBox 的初始大小是什么以及是什么决定了它。另一个 PictureBox(无论是什么用途)可以具有始终相同或按较大控件的初始大小缩放的初始大小。父级是较小的 PictureBox 到较大的一个,因此当您移动图像时,较小的 PicureBox 跟随其父级,没有其他事情可做。 -- 我建议用这两个控件构建一个UserControl。
谢谢。 ScaledPictureBox
确实调整了图片框的大小。我很确定你建议它做什么。问题在于缩放例程。
我建议仅缩放图片框的大小。我这么说是因为您提到了 drawing 并且那里有一个 Matrix,即使在您发布的代码中的任何地方都没有绘制任何东西并且 Matrix 的使用是未知的。 -- 当您将缩放的Selector
设置为较大的 PictureBox 时,当另一个 PictureBox 被移动时,无需在代码中移动它,因为它是它的父级,并且无需任何进一步计算即可跟随。 -- 还提到,您应该构建一个包含全部功能的 UserControl。
@Jimi - 完美,谢谢 - 我错过了基础。将 Selector 与 Canvas 以及其他一些代码抖动一起育儿起到了作用(工作代码发布在下面)。它的附加好处本质上提供了一个“透明控件”,您在另一篇文章中也帮助了我。再次重写......
【参考方案1】:
感谢 Jimi,找到答案。我错过了在 VS Designer 中向控件添加控件并不一定使其成为父控件的子控件。这在代码中与其他一些修复一起使它工作。可扩展控件:
Imports System.Drawing.Drawing2D
Partial Public Class ScaledPictureBox
Inherits PictureBox
Public Property ScaleM As Matrix
Private Property OriginalSize As Size
Private Property Zoom As Single
Public Sub New()
ScaleM = New Matrix()
SizeMode = PictureBoxSizeMode.Zoom
End Sub
Public Sub InitImage()
OriginalSize = Me.Size
Size = OriginalSize
SetZoom(100)
End Sub
Public Sub SetZoom(ByVal zoomfactor As Single)
If zoomfactor <= 0 Then Throw New Exception("Zoom must be positive")
Dim oldZoom As Single = Zoom
Zoom = zoomfactor / 100.0F
ScaleM.Reset()
ScaleM.Scale(Zoom, Zoom)
If OriginalSize <> Size.Empty Then Size = New Size(CInt((OriginalSize.Width * Zoom)), CInt((OriginalSize.Height * Zoom)))
End Sub
Public Function ScalePoint(ByVal pt As PointF) As PointF
Return New PointF(pt.X / Zoom, pt.Y / Zoom)
End Function
End Class
以及如何使用:
Public Class Form1
Dim moving As Boolean = False
Dim CanvasClickPoint As New Point
Dim SelectorLocation As New Point
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Canvas.InitImage()
Selector.InitImage()
SelectorLocation = Selector.Location
Canvas.Controls.Add(Selector)
End Sub
Private Sub TrackBar1_Scroll(sender As Object, e As EventArgs) Handles TrackBar1.Scroll
Zoom(TrackBar1.Value)
End Sub
Private Sub Zoom(zoom As Integer)
Canvas.SetZoom(CInt(zoom))
Selector.SetZoom(CInt(zoom))
Selector.Location = New Point(SelectorLocation.X * (zoom / 100), SelectorLocation.Y * (zoom / 100))
End Sub
Private Sub Canvas_MouseDown(sender As Object, e As MouseEventArgs) Handles Canvas.MouseDown
If (ModifierKeys And Keys.Control) = Keys.Control Then
moving = True
CanvasClickPoint = e.Location
SelectorLocation = Point.Round(Canvas.ScalePoint(New PointF(Selector.Location.X, Selector.Location.Y)))
End If
End Sub
Private Sub Canvas_MouseMove(sender As Object, e As MouseEventArgs) Handles Canvas.MouseMove
If moving Then
Dim CanvasNewPoint As Point = New Point(Canvas.Left + (e.Location.X - CanvasClickPoint.X),
Canvas.Top + (e.Location.Y - CanvasClickPoint.Y))
Canvas.Location = CanvasNewPoint
End If
End Sub
Private Sub Canvas_MouseUp(sender As Object, e As MouseEventArgs) Handles Canvas.MouseUp
moving = False
Canvas.Invalidate()
End Sub
End Class
【讨论】:
以上是关于自定义 Winforms 设计器控件同时缩放和平移控件的主要内容,如果未能解决你的问题,请参考以下文章