自定义 Winforms 设计器控件同时缩放和平移控件

     2023-03-29     88

关键词:

【中文标题】自定义 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

【讨论】:

android自定义控件之可平移、缩放、旋转图片控件

参考技术A先上效果图源码单点拖动图片对图片进行平移操作。双手缩放图片大小和旋转图片到一定的角度。图片缩放的时候不能大于最大的缩放因子和小于最小的缩放因子。大于最大缩放因子或者小于最小缩放因子需要对图像... 查看详情

Winforms:在设计器中创建对象并对其进行自定义

】Winforms:在设计器中创建对象并对其进行自定义【英文标题】:Winforms:Createobjectindesignerandcustomizeit【发布时间】:2016-05-1002:40:22【问题描述】:我正在Winforms(C#)中创建一个新组件。该组件获取一些属性来修改行为。用户应该在... 查看详情

如何使用缩放和平移为 MapView 添加自定义平面图

】如何使用缩放和平移为MapView添加自定义平面图【英文标题】:HowtoaddcustomfloorplanforMapViewwithzoomandpan【发布时间】:2015-06-1907:35:15【问题描述】:我正在使用swift开发一个iOS应用程序。我需要显示用户可以缩放和平移的自定义平... 查看详情

如何将设计器支持添加到 Winforms 扩展控件?

】如何将设计器支持添加到Winforms扩展控件?【英文标题】:HowtoadddesignersupporttoWinformsextensioncontrol?【发布时间】:2022-01-1818:43:56【问题描述】:我正在使用C#和.Net5.0框架创建一个Windows窗体应用程序,并且想创建一个按钮控件的... 查看详情

如何在winforms设计器中公开用户控件的整个子控件

】如何在winforms设计器中公开用户控件的整个子控件【英文标题】:Howtoexposewholesubcontrolofusercontrolatwinformsdesigner【发布时间】:2012-10-0810:12:20【问题描述】:例如我想创建一个包含标签和文本框的用户控件(Windows窗体)。我想将... 查看详情

pyqtgraph 自定义缩放问题

】pyqtgraph自定义缩放问题【英文标题】:pyqtgraphcustomscalingissue【发布时间】:2017-02-0314:41:28【问题描述】:我使用pyqtgraph库进行绘图。我真的很喜欢情节上的鼠标交互(缩放,平移,...)。对于我的一些情节,我想在滚动鼠标滚... 查看详情

控件的自定义设计器

】控件的自定义设计器【英文标题】:CustomDesignerforaControl【发布时间】:2010-02-0913:37:13【问题描述】:我有一个派生自SplitContainer的自定义类:namespaceBuilder.ComponentspublicpartialclassProjectSidebar:SplitContainerpublicProjectSidebar()InitializeCompo... 查看详情

android弧形菜单设计

...实现,但在手机分辨率众多,机型繁杂的情况下,这种自定义形状的菜单设计就需要自己精细雕琢。弧形菜单设计在网上文章非常多,本人结合自己设计经验,简单描述该功能重点实现部分,同时大多数文章只是在讲述如果实现... 查看详情

使用自定义 WinForms 控件,我可以更改嵌套控件停靠在里面的矩形吗?

】使用自定义WinForms控件,我可以更改嵌套控件停靠在里面的矩形吗?【英文标题】:WithacustomWinFormscontrol,canIchangetherectanglethatnestedcontrolsdockinside?【发布时间】:2016-02-1719:01:57【问题描述】:我正在尝试创建一个行为类似于GroupBo... 查看详情

自定义 WinForms ErrorProvider 以在控件条目中显示其图标

】自定义WinFormsErrorProvider以在控件条目中显示其图标【英文标题】:CustomizingWinFormsErrorProvidertodisplayitsiconinsidecontrol\'sentry【发布时间】:2010-11-1103:52:07【问题描述】:我有一些自定义/用户控件,在大多数情况下都有标签和条目... 查看详情

保留缩放功能,同时删除滚动(平移)功能 PyQt

】保留缩放功能,同时删除滚动(平移)功能PyQt【英文标题】:Remainthezoomfeaturewhileremovethescroll(pan)featurePyQt【发布时间】:2021-04-1810:14:32【问题描述】:我正在做一个图像查看器,允许用户浏览他们的文件夹并显示文件夹中的第... 查看详情

Winforms Designer:如何禁用我的用户控件成为容器?

】WinformsDesigner:如何禁用我的用户控件成为容器?【英文标题】:WinformsDesigner:Howtodisablemyusercontrolfrombeingacontainer?【发布时间】:2012-08-0920:31:00【问题描述】:我有一个相对简单的设置。我有一个自定义用户控件,上面有一堆组... 查看详情

为啥在 C# Winforms 中控件停靠到其父级时,设计器设置了控件的“大小”属性?

】为啥在C#Winforms中控件停靠到其父级时,设计器设置了控件的“大小”属性?【英文标题】:Whyisthe"Size"propertyofacontrolsetbythedesignerwhenthecontrolisdockedtoitsparentinC#Winforms?为什么在C#Winforms中控件停靠到其父级时,设计器设置... 查看详情

报表开发神器!devexpressreportingv19.1:winforms平台新功能

...DevExpressReporting全新发布了v19.1版本,本文主要为大家介绍WinForms、ASP.NetCore平台、VisualStudio报表设计器中发布的新功能。DevExpressReportingv19.1下载VisualStudio报表设计器用于图像属性的ImagePicker现在使用ImagePicker对话框在VisualStudio中编... 查看详情

WinForms 不同的 DPI 布局

】WinForms不同的DPI布局【英文标题】:WinFormsDifferentDPILayouts【发布时间】:2010-12-2311:34:34【问题描述】:不知何故,通过VisualStudio和设计器创建的表单和控件能够根据Windows的当前DPI/字体大小自行缩放。我的UI的一部分是一个选项... 查看详情

多个控件到单个控件c#winforms上(代码片段)

有没有办法让控制像Panel,并插入其他几个组件,如Label?我已经制作了一个自定义控件,并将工具箱中的一些控件添加到它的[Designer]中,但是在将自定义控件插入主项目时这些项目是不可见的。答案我终于弄明白了。基本上做... 查看详情

如何在VS2010 WPF设计器中控制自定义控件的默认属性

】如何在VS2010WPF设计器中控制自定义控件的默认属性【英文标题】:HowtocontroldefaultpropertiesforcustomcontrolinVS2010WPFdesigner【发布时间】:2011-05-1601:52:02【问题描述】:我有一个继承自Button的类。在该类的XAML中,我指定了宽度和高度... 查看详情

qt编写自定义控件属性设计器

...已经6年有余,在工业控制领域,有一些应用场景需要自定义绘制一些控件满足特定的需求,比如仪器仪表、组态等,而且需要直接用户通过属性设计的形式生成导出控件及界面数据,下次导入使用,要想从内置控件或者自定义... 查看详情