Как добавить выделение области прямоугольника для панорамируемого / масштабируемого элемента управления Winforms VB.Net? - PullRequest
0 голосов
/ 08 ноября 2019

это мой первый вопрос после нахождения этого сайта.

В настоящее время я пытаюсь создать пользовательский элемент управления VB.NET, содержащий панель, на которой должны отображаться некоторые графические объекты (круги, прямоугольники, линии). нарисовано во время события Paint. Это может упростить случай, когда панель всегда будет квадратной (ширина = высота). Использование Picturebox для рисования, к сожалению, не вариант для меня. Я также не хочу использовать автопрокрутку и иметь видимые полосы прокрутки.

Панель должна панорамироваться / масштабироваться пользователем. Панорамирование должно быть выполнено нажатием средней кнопки мыши, а масштабирование - прокруткой колесика мыши. Я уже нашел хорошие примеры, как реализовать эти функции, и пока они работают очень хорошо. Теперь я также хочу добавить функцию, позволяющую пользователю масштабировать определенную (квадратную) область панели с помощью прямоугольника выбора, который отображается, когда он нажимает левую кнопку мыши, и его размер регулируется во время перемещения мыши. с нажатой левой кнопкой. (Это должно быть похожее поведение, например, масштабирование в документе PDF). Вот где я застрял.

Я извлек часть кода, которая отвечает за панель и ее события, и это то, что я до сих пор:

Public Class Form1

    Private zoomstart as Point
    Private zoomfirst as Point
    Private zoomwidth as Integer    
    Private zoomrect as Rectangle
    Private WithEvents tmrMarch as New Timer
    Private MarchOffset as Integer = 0
    Private OffsetDelta as Integer = 2
    Private DashPattern() as Single = {5, 5}

    Private zoom As Single = 1.0
    Private startx as Integer = 0
    Private starty as Integer = 0
    Private offsetx as Integer = 0
    Private offsety as Integer = 0
    Private mouseDownPt as Point
    Private initialwidth As Integer

    Public WithEvents Canvas1 As New Canvas

    Private Enum T_MouseAction
        RectangleZooming
        WheelZooming
        Panning
        None
    End Enum

    Private MouseAction As T_MouseAction = T_MouseAction.None


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Canvas1.Size = New Size(Me.ClientSize.Width, Me.ClientSize.Width)
        Canvas1.AutoScroll = False
        initialwidth = Canvas1.Width
        Me.Controls.Add(Canvas1)
    End Sub


    Private Sub Canvas1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Canvas1.Paint
        Select Case MouseAction
            Case T_MouseAction.None
                e.Graphics.TranslateTransform(offsetx, offsety)
                e.Graphics.ScaleTransform(zoom, zoom)
            Case T_MouseAction.Panning
                e.Graphics.TranslateTransform(offsetx, offsety)
                e.Graphics.ScaleTransform(zoom, zoom)
            Case T_MouseAction.RectangleZooming
                e.Graphics.TranslateTransform(offsetx, offsety)
                e.Graphics.ScaleTransform(zoom, zoom)
            Case T_MouseAction.WheelZooming
                e.Graphics.ScaleTransform(zoom, zoom)
                e.Graphics.TranslateTransform(offsetx, offsety)
        End Select
        Call DrawImage(e.Graphics)
        e.Graphics.ResetTransform
        If MouseAction = T_MouseAction.RectangleZooming Then
            MarchOffset = MarchOffset + OffsetDelta
            Dim pen as New Pen(Color.Black, 2)
            pen.DashPattern = DashPattern
            pen.DashOffset = MarchOffset
            pen.Color = Color.Red
            e.Graphics.DrawRectangle(pen, zoomrect)
        End If 
    End Sub


    Private Sub DrawImage(ByVal gr As Graphics)
        Dim rect As Rectangle
        rect = New Rectangle(0, 0, initialwidth, initialwidth)
        gr.FillEllipse(Brushes.LightGreen, rect)
        gr.DrawEllipse(Pens.Green, rect)
        rect = New Rectangle(0.375 * initialwidth, 0.375 * initialwidth, 0.25 * initialwidth, 0.375 * initialwidth)
        gr.FillEllipse(Brushes.LightBlue, rect)
        gr.DrawEllipse(Pens.Blue, rect)
        rect = New Rectangle(0.1875 * initialwidth, 0.25 * initialwidth, 0.625 * initialwidth, 0.625 * initialwidth)
        gr.DrawArc(Pens.Red, rect, 20, 140)
        rect = New Rectangle(0.1875 * initialwidth, 0.1875 * initialwidth, 0.1875 * initialwidth, 0.25 * initialwidth)
        gr.FillEllipse(Brushes.White, rect)
        gr.DrawEllipse(Pens.Black, rect)
        rect = New Rectangle(0.25 * initialwidth, 0.25 * initialwidth, 0.125 * initialwidth, 0.125 * initialwidth)
        gr.FillEllipse(Brushes.Black, rect)
        rect = New Rectangle(0.625 * initialwidth, 0.1875 * initialwidth, 0.1875 * initialwidth, 0.25 * initialwidth)
        gr.FillEllipse(Brushes.White, rect)
        gr.DrawEllipse(Pens.Black, rect)
        rect = New Rectangle(0.6875 * initialwidth, 0.25 * initialwidth, 0.125 * initialwidth, 0.125 * initialwidth)
        gr.FillEllipse(Brushes.Black, rect)
    End Sub



    Private Sub tmrMarch_Tick(ByVal sender as Object, ByVal e as EventArgs) Handles tmrMarch.Tick
        Canvas1.Refresh
    End Sub    




    Private Sub Canvas1_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Canvas1.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Middle Then
            MouseAction = T_MouseAction.Panning
            mouseDownPt = e.Location
            startx = offsetx
            starty = offsety
        End If
        If e.Button = Windows.Forms.MouseButtons.Left Then
            zoomstart = e.Location
            tmrMarch.Interval = 100
            tmrMarch.Enabled = True
        End If        
    End Sub

    Private Sub Canvas1_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Canvas1.MouseUp
        Cursor = Cursors.Default
        tmrMarch.Enabled = False
        If MouseAction = T_MouseAction.RectangleZooming Then
            Dim oldzoom as Single = zoom

            zoom = 1 ' <=== ?

            zoom = Math.Truncate(zoom / 0.2) * 0.2

            Dim oldoffsetx, oldoffsety as Integer
            Dim newoffsetx, newoffsety as Integer
            oldoffsetx = CInt(zoomrect.X / oldzoom)
            oldoffsety = CInt(zoomrect.Y / oldzoom)
            newoffsetx = CInt(zoomrect.X / zoom)
            newoffsety = CInt(zoomrect.Y / zoom)

            offsetx = newoffsetx - oldoffsetx + offsetx ' <=== ?
            offsety = newoffsety - oldoffsety + offsety ' <=== ?    
        End If
        MouseAction = T_MouseAction.None
        Canvas1.Refresh
    End Sub    


    Private Sub Canvas1_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Canvas1.MouseMove
        If e.Button = Windows.Forms.MouseButtons.Middle
            Cursor = Cursors.Hand
            Dim mousePosNow as Point = e.Location
            Dim deltaX, deltaY as Integer
            deltaX = mousePosNow.X - mouseDownPt.X
            deltaY = mousePosNow.Y - mouseDownPt.Y
            offsetx = CInt(startx + deltaX)
            offsety = CInt(starty + deltaY)
            MouseAction = T_MouseAction.Panning
            Canvas1.Refresh
        End If

        If e.Button = Windows.Forms.MouseButtons.Left Then
            Dim loc as Point
            loc = e.Location
            Dim sizex, sizey as Integer
            sizex = Math.Abs(zoomstart.X - loc.X)
            sizey = Math.Abs(zoomstart.Y - loc.Y)
            zoomwidth = Math.Max(sizex, sizey)
            If loc.X < zoomstart.X Then
                zoomfirst.X = loc.X
            Else
                zoomfirst.X = zoomstart.x
            End If
            If loc.Y < zoomstart.Y Then
                zoomfirst.Y = loc.Y
            Else
                zoomfirst.Y = zoomstart.Y
            End If
            If zoomwidth > 10 Then
                MouseAction = T_MouseAction.RectangleZooming
            End If
            zoomrect = New Rectangle(zoomfirst, New Size(zoomwidth, zoomwidth))
            Canvas1.Refresh
        End If        
    End Sub    


    Private Sub Canvas1_MouseWheel(ByVal sender as Object, ByVal e as MouseEventArgs) Handles Canvas1.MouseWheel
        If MouseAction = T_MouseAction.Panning Then
            Exit Sub
        End If
        Dim oldzoom as Single = zoom
        If e.Delta > 0 Then
            zoom = zoom + 0.2
        End If
        If e.Delta < 0 Then
            zoom = Math.Max(zoom - 0.2, 0.2)
        End If
        Dim mousePosNow as Point = e.Location
        Dim x, y as Integer
        x = mousePosNow.X
        y = mousePosNow.Y
        Dim oldoffsetx, oldoffsety as Integer
        Dim newoffsetx, newoffsety as Integer
        oldoffsetx = CInt(x / oldzoom)
        oldoffsety = CInt(y / oldzoom)
        newoffsetx = CInt(x / zoom)
        newoffsety = CInt(y / zoom)
        offsetx = newoffsetx - oldoffsetx + offsetx
        offsety = newoffsety - oldoffsety + offsety
        MouseAction = T_MouseAction.WheelZooming
        Canvas1.Refresh
    End Sub    


    Private Sub Canvas1_MouseEnter(ByVal sender as Object, ByVal e as EventArgs) Handles Canvas1.MouseEnter
        Canvas1.Focus
    End Sub

    Private Sub Canvas1_MouseLeave(ByVal sender as Object, ByVal e as EventArgs) Handles Canvas1.MouseLeave
        Me.Focus
    End Sub    

End Class


Public Class Canvas
    Inherits Panel

    Public Sub New
        Me.DoubleBuffered = True
    End Sub
End Class

Кредиты идут наРод Стивенс для кода смайлика, который в данном случае является просто заполнителем для графики, которая будет позже нарисована в пользовательском элементе управления. (http://csharphelper.com/blog/2014/11/scale-a-drawing-so-it-fits-a-target-area-in-c/)

Прямоугольник масштабирования (марширующие муравьи) уже правильно создан в событии MouseMove. В событии MouseUp я хочу применить масштабирование и масштабировать выбранную область до размера панели. Событие рисования, фактическое масштабирование обрабатывается операциями ScaleTransform и TranslateTransform.

Но я не могу понять, как рассчитать соответствующий коэффициент масштабирования и смещения по x / y, чтобы выбранная область масштабировалась до размера панели. ориентироваться на код, который используется для масштабирования колесика мыши. Я немного запутался, поскольку мне кажется, что на самом деле задействованы два коэффициента масштабирования: один, на который влияет работа колеса мыши, и тот, который связан соперация выделения прямоугольника. Я также пытался вычислить коэффициент масштабирования как что-то вроде «selection.width / panel.width», но это только приводит к «скачкообразному» поведению на панели навигации и не масштабируется должным образом.

Любая помощь будет оценена. Большое спасибо заранее.

...