Click here to Skip to main content
15,884,099 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
I'm currently making a node diagram control for Winforms, the control inherits from the Container class. The control can contain nodes, and a node is basically just an "empty" class with properties, so nodes are handled by the control (drawn, moved, etc).

My control needs to support zooming and panning. Currently I'm zooming by using the Graphics.ScaleTransform method, and panning by using AutoScroll.

The two work great by themselves, but using them together doesn't work.

To make panning "better", I have hidden the scrollbars, set the AutoScrollMinSize to 9000 and centered the AutoScrollPosition (4500). So that it hopefully feels like you have a canvas to work on, so that you can go any direction that you'd like.

That works quite well, however when zooming (ScaleTransform), the nodes will be moved x amount to the left top corner of the form (if zooming out) or to the right bottom corner (if zooming in).

Youtube video of problem: https://www.youtube.com/watch?v=uJBAHtNhung

Custom control OnPaint:
VB
G.TranslateTransform(AutoScrollPosition.X, AutoScrollPosition.Y)

G.ScaleTransform(Zoom, Zoom)

'Draw grid
If ShowGrid Then
    Using Pen As New Pen(GridColor.ToBrush())
        For row As Integer = 0 To viewportRect.Right Step GridSize.Width
            G.DrawLine(Pen, New Point(row, 0), New Point(row, 0 + viewportRect.Bottom))
        Next

        For col As Integer = 0 To viewportRect.Bottom Step GridSize.Height
            G.DrawLine(Pen, New Point(0, col), New Point(0 + viewportRect.Right, col))
        Next
    End Using
End If

'Draw connections
For Each Connection As Connection In Connections
    Connection.Draw(G)
Next

'Draw all the nodes
For Each Node As Node In Nodes
    Node.Draw(G)
Next

'Draw the active tool (Multi select or NodeLinker)
If ActiveTool IsNot Nothing Then
    ActiveTool.OnDraw(G)
End If


Zoom:
VB
If e.Delta < 0 Then
    NodeContainer.Zoom -= 0.1F
Else
    NodeContainer.Zoom += 0.1F
End If


Pan:
VB
If Panning Then
    Delta = New Point(StartPoint.X - e.X, StartPoint.Y - e.Y)
    NodeContainer.AutoScrollPosition = New Point(Delta.X - NodeContainer.AutoScrollPosition.X, Delta.Y - NodeContainer.AutoScrollPosition.Y)
End If


How would I got about solving this, or should I be going about this a different way (maybe draw an image instead, and then reposition that image, etc.)?

Here's a link to the whole solution so that you can go through the code and toy around with it.

Download Me Via DropBox

And no, it's not the prettiest at the moment, but I'm just trying to get it functional atm.

Best regards
Posted

That's right, making auto-scroll working with ScaleTransform does not look to be very trivial, probably because this is not exactly what System.Windows.Forms.ScrollableControl was designed for; it is primarily designed to hold some piece of UI which has fixed pixel size and need to be scrolled because it does not fit its parent control when it is resized. I would avoid using this behavior. And yet, it's possible to use with scaling; you simply need to accurately calculate the size of zoomed child control and make it fit on the scrollable parent.

In my past answer, I described another approach:
Zoom image in C# .net mouse wheel[^].

Alternatively, it can be used in combination with ScaleTransform.

—SA
 
Share this answer
 
Thank you Sergey Alexandovich for making me look for other solutions.

The answer was to remove autoscrolling, then relocate all the nodes whenever zooming or panning occurs.

Zoom:
VB
oldZoom = NodeContainer.Zoom

    If e.Delta < 0 Then
        NodeContainer.Zoom = Math.Max(NodeContainer.Zoom - 0.1F, 0.01F)
    Else
        NodeContainer.Zoom = Math.Min(NodeContainer.Zoom + 0.1F, 10.0F)
    End If

    For Each Node As Node In NodeContainer.Nodes
        oldZoomLocation = New Point(e.X / oldZoom, e.Y / oldZoom)
        newZoomLocation = New Point(e.X / NodeContainer.Zoom, e.Y / NodeContainer.Zoom)

        Node.Location = New Point(newZoomLocation.X - oldZoomLocation.X + Node.Location.X, newZoomLocation.Y - oldZoomLocation.Y + Node.Location.Y)
    Next


Pan:
VB
Delta = New Point(e.X - StartPoint.X, e.Y - StartPoint.Y)

    For Each Node As Node In NodeContainer.Nodes
        Node.Location = New Point(Node.Location.X + (Delta.X / NodeContainer.Zoom), Node.Location.Y + (Delta.Y / NodeContainer.Zoom))
    Next

    StartPoint = New Point(e.X, e.Y)
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900