Autoscroll property

rbulph

Well-known member
Joined
Feb 17, 2003
Messages
343
Is the Autoscroll facility for forms and panels any good? I notice two problems with it straightaway - it doesnt show up if a control is off the left or top of the container, only if it is off the right or bottom, and if a partly obscured control gets focus it scrolls into view, which I dont really like.

Is there a way to fix these or should I forget it and use my own scrollbars?
 
Why would you want a control to have focus and not be scrolled into view? As a user I couldnt think of anything more annoying than having to constantly scroll around to find the control with focus.
Admittedly not showing the scroll bars when items are to the left is a bad thing but why would you have a starting point with items alread off the left handside?
Personally if I have aform with a lot of controls I would use a tab control and organise the layout into a more structured form rather than relying on the autoscroll mechanism.
 
The form is an MDI child form. The MDI form has resizable panels. The controls can be moved around by the user in the child form to better see the relationship between them. So there are two ways that they could be obscured - by the user moving them or the user resizing the panels. Maybe the fact that they scroll into view when they get focus is acceptable, but the fact that no scrollbar shows when they are off at the left is definitely not.
 
rbulph said:
Maybe the fact that they scroll into view when they get focus is acceptable, but the fact that no scrollbar shows when they are off at the left is definitely not.
I would have to agree with you. My best advice: when a control is moved, check its coordinates. If it is off to the left, move everything to the right. If it is above the top, move everything down. Its that or implement your own scrolling.
 
Figured out a way to do this. You just need a form with a single Panel on it at design time. The Panel should be undocked and neither it nor the form should have AutoScroll on. Any comments welcome.
Code:
Public Class Form1

    Dim DownP As Point  Point where mouse went down, in sub panel co-ordinates.

    Private Declare Function SetScrollInfo Lib "user32" (ByVal hwnd As Integer, ByVal n As SCROLLBAR_FLAG, ByRef lpcScrollInfo As SCROLLINFO, ByVal bool As Boolean) As Integer
   
    Public Enum SCROLLBAR_FLAG
        SB_HORZ = 0
        SB_VERT = 1
    End Enum

    Private HasScroll(SCROLLBAR_FLAG.SB_VERT) As Boolean    Flag as to whether the scrollbars are there.

    Public Structure Rect
        Dim Left As Integer
        Dim Top As Integer
        Dim Right As Integer
        Dim Bottom As Integer
    End Structure

    Public Enum SCROLLBAR_MASK
        SIF_RANGE = &H1S
        SIF_PAGE = &H2S
        SIF_POS = &H4S
        SIF_DISABLENOSCROLL = &H8S
        SIF_TRACKPOS = &H10S
    End Enum

    Public Structure SCROLLINFO
        Dim cbSize As Integer
        Dim fMask As SCROLLBAR_MASK
        Dim nMin As Integer
        Dim nMax As Integer
        Dim nPage As Integer
        Dim nPos As Integer
        Dim nTrackPos As Integer
    End Structure
 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        SetPanelSize()
    End Sub

    Private Sub Panel1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseDown

        If e.Button = Windows.Forms.MouseButtons.Left Then
            Dim f As New Panel
            f.BackColor = Color.Red
            f.Location = New Point(e.X, e.Y)
            f.Size = New Size(40, 20)
            f.BorderStyle = BorderStyle.FixedSingle
            Panel1.Controls.Add(f)

            AddHandler f.MouseDown, AddressOf Panel_MouseDown
            AddHandler f.MouseMove, AddressOf Panel_MouseMove

            SetPanelSize()
            ScrollSet()

        End If
    End Sub

    Private Sub Panel_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)

        If e.Button = Windows.Forms.MouseButtons.Left Then DownP = New Point(e.X, e.Y)

    End Sub

    Private Sub Panel_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
        With DirectCast(sender, Panel)
            If .Capture Then
                .Location = New Point(.Left + e.X - DownP.X, .Top + e.Y - DownP.Y)
                SetPanelSize()
                ScrollSet()
            End If

        End With
    End Sub

    Private Sub SetPanelSize()

        Set the size of the panel to be big enough to contain all sub panels and to fill the client area of the form.
        Dim D As Rect
        D = GetUsedArea()

        If Panel1s left isnt the same as Ds left, then will be adjusting Panel1s left, so need to move _
        subpanels to keep them in the same relative positions.
        For Each F As Panel In Panel1.Controls
            F.Left = F.Left + Panel1.Left - D.Left
            F.Top = F.Top + Panel1.Top - D.Top
        Next

        Panel1.Size = New Size(Math.Max(Me.ClientRectangle.Width, D.Right) - D.Left, Math.Max(Me.ClientRectangle.Height, D.Bottom) - D.Top)
        Panel1.Location = New Point(D.Left, D.Top)

    End Sub

    Private Function GetUsedArea() As Rect

        Find the area taken up by the subpanels in co-ordinates relative to the Form.
        Dim F As Panel
        Dim D As Rect

        For Each F In Panel1.Controls
            D.Left = Math.Min(D.Left, F.Left + Panel1.Left)
            D.Top = Math.Min(D.Top, F.Top + Panel1.Top)
            D.Right = Math.Max(D.Right, F.Right + Panel1.Left)
            D.Bottom = Math.Max(D.Bottom, F.Bottom + Panel1.Top)
        Next

        GetUsedArea = D

    End Function

    Private Sub ScrollSet()

        Dim Dirn As SCROLLBAR_FLAG
        For Dirn = SCROLLBAR_FLAG.SB_HORZ To SCROLLBAR_FLAG.SB_VERT

            Dim SBI As SCROLLINFO
            Set up or hide horizontal and vertical scrollbars as necessary.
            With SBI
                .cbSize = Len(SBI)
                .fMask = SCROLLBAR_MASK.SIF_RANGE Or SCROLLBAR_MASK.SIF_POS Or SCROLLBAR_MASK.SIF_PAGE
                .nMax = IIf(Dirn = SCROLLBAR_FLAG.SB_HORZ, Panel1.Width, Panel1.Height)
                .nPage = IIf(Dirn = SCROLLBAR_FLAG.SB_HORZ, Me.ClientRectangle.Width, Me.ClientRectangle.Height)

                If .nPage <> .nMax Then
                    .nPos = -IIf(Dirn = SCROLLBAR_FLAG.SB_HORZ, Panel1.Left, Panel1.Top)
                Else
                    .nMax = 0
                    .nPage = 0
                    .nPos = 0
                End If

                SetScrollInfo(Me.Handle.ToInt32, Dirn, SBI, True)

                If HasScroll(Dirn) <> (.nPage <> .nMax) Then
                    HasScroll(Dirn) = (.nPage <> .nMax)
                    SetPanelSize()  ClientRectangle size will have changed so better reset things.
                    Panel1.Invalidate() otherwise bits of scrollbar seem to get left lying around.
                    For Each F As Panel In Panel1.Controls
                        F.Invalidate()
                    Next
                End If

            End With
        Next

    End Sub

    Private Sub Form1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) Handles Me.Scroll

        Dim Dirn As SCROLLBAR_FLAG

        Dirn = IIf(e.ScrollOrientation = ScrollOrientation.HorizontalScroll, SCROLLBAR_FLAG.SB_HORZ, SCROLLBAR_FLAG.SB_VERT)

        If HasScroll(Dirn) Then stupid event continues to fire even after the scrollbar has been removed, so have to check for this.
            If Dirn = SCROLLBAR_FLAG.SB_HORZ Then
                Panel1.Left = -e.NewValue
            Else
                Panel1.Top = -e.NewValue
            End If

            SetPanelSize()
            ScrollSet()
        End If

    End Sub

    Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize

        SetPanelSize()
        ScrollSet()

    End Sub

    Private Sub Form1_MouseWheel(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseWheel

        If HasScroll(SCROLLBAR_FLAG.SB_VERT) Then
            Form1_Scroll(sender, New ScrollEventArgs(ScrollEventType.SmallIncrement, Me.VerticalScroll.Value - CInt(e.Delta / 12), ScrollOrientation.VerticalScroll))
        ElseIf HasScroll(SCROLLBAR_FLAG.SB_HORZ) Then
            Form1_Scroll(sender, New ScrollEventArgs(ScrollEventType.SmallIncrement, Me.VerticalScroll.Value - CInt(e.Delta / 12), ScrollOrientation.HorizontalScroll))
        End If

    End Sub
End Class
 
Back
Top