Click here to Skip to main content
15,886,110 members
Please Sign up or sign in to vote.
3.67/5 (2 votes)
See more:
Hello Coders,
I have a task to make vertical scrolling of form with 30 – 40 comboboxes
Comboboxes adding dynamically on runtime.
But when I rotate the mouse wheel scroll of panel executing brokenly
What wrong in this code? Thanks.
VB
Public Class Form1
    Inherits System.Windows.Forms.Form

    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Me.Panel1 = New System.Windows.Forms.Panel()
        Me.SuspendLayout()
        '
        'Panel1
        '
        Me.Panel1.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.Panel1.AutoScroll = True
        Me.Panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
        Me.Panel1.Location = New System.Drawing.Point(0, 0)
        Me.Panel1.Name = "Panel1"
        Me.Panel1.Size = New System.Drawing.Size(353, 408)
        Me.Panel1.TabIndex = 0
        '
        'Form1
        '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.ClientSize = New System.Drawing.Size(353, 408)
        Me.Controls.Add(Me.Panel1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)

    End Sub
    Friend WithEvents Panel1 As System.Windows.Forms.Panel

    Public Sub New()
        InitializeComponent()
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        'on load add to Panel1 few comboboxes
        Dim i As Integer
        Dim YPosition As Integer = 0
        For i = 0 To 35
            Dim Combo = New System.Windows.Forms.ComboBox()
            Combo.Items.AddRange(New Object() {"First", "Second", "Third"})
            Combo.Location = New System.Drawing.Point(55, 95)
            Combo.Size = New System.Drawing.Size(121, 21)
            Combo.SelectedIndex = 0
            Combo.Location = New Point(5, YPosition)
            AddHandler Combo.MouseWheel, AddressOf ComboBox_MouseWheel

            Me.Panel1.Controls.Add(Combo)
            YPosition += Combo.Height + 15

        Next

    End Sub

    Private Sub ComboBox_MouseWheel(sender As Object, e As MouseEventArgs)
        'http://blog.iordanov.info/?p=58
        If (e.Delta <> 0) Then
            Dim NewVerticalScrollValue As Integer
            '??? it's strange, but scroll execute in opposite direction ??? so need set minus
            Dim NewDelta As Integer = e.Delta * -1 ' CInt(e.Delta / 3) * -1
            NewVerticalScrollValue = Me.Panel1.VerticalScroll.Value() + NewDelta
            If (NewVerticalScrollValue > Me.Panel1.VerticalScroll.Maximum) Then
                NewVerticalScrollValue = Me.Panel1.VerticalScroll.Maximum
            End If
            If (NewVerticalScrollValue < Me.Panel1.VerticalScroll.Minimum) Then
                NewVerticalScrollValue = Me.Panel1.VerticalScroll.Minimum
            End If

            If Me.Panel1.VerticalScroll.Value <> NewVerticalScrollValue Then
                If sender IsNot Nothing AndAlso TypeOf sender Is System.Windows.Forms.ComboBox Then
                    DirectCast(sender, System.Windows.Forms.ComboBox).DroppedDown = False 'close combobox
                End If

                'If TypeOf e Is HandledMouseEventArgs Then
                '    Dim WheelEventArgs As HandledMouseEventArgs = TryCast(e, HandledMouseEventArgs)
                '    If WheelEventArgs IsNot Nothing Then
                '        WheelEventArgs.Handled = True 'don't scroll items in combobox but scroll panel
                '    End If
                'End If

                Me.Panel1.VerticalScroll.Value = NewVerticalScrollValue
                If Me.Panel1.VerticalScroll.Value <> NewVerticalScrollValue Then
                    Debug.Print(String.Format("CB different: {0} - {1} !!!!", Me.Panel1.VerticalScroll.Value, NewVerticalScrollValue))
                Else
                    Debug.Print(String.Format("CB: {0}", Me.Panel1.VerticalScroll.Value))
                End If
            End If
        End If
    End Sub

End Class
Posted
Comments
Krasovskiy 6-Dec-13 5:52am    
But if add to form textbox and set focus to it. All work good. Scrolling is smooth
For try it you could change cycle for next:
For i = 0 To 15
Dim Combo = New System.Windows.Forms.ComboBox()
Combo.Items.AddRange(New Object() {"First", "Second", "Third"})
Combo.Size = New System.Drawing.Size(121, 21)
Combo.SelectedIndex = 0
Combo.Location = New Point(5, YPosition)
AddHandler Combo.MouseWheel, AddressOf ComboBox_MouseWheel

Me.Panel1.Controls.Add(Combo)
YPosition += Combo.Height + 15

Dim TxtBox As New TextBox
TxtBox.Width = 230 'Me.Panel1.Width - 10
Me.Panel1.Controls.Add(TxtBox)
TxtBox.Location = New Point(5, YPosition)
YPosition += TxtBox.Height + 15

Next

1 solution

You have encountered the joy of a control (ComboBox) having focus for a event message(MouseWheel) that you would prefer to have sent to its parent instead. In this case, the ComboBox has also has predefined function associated with the MouseWheel (it moves the selected item up/down the item list). I have modified your code to deal with this by allowing the message to pass to the control if the the pointer is positioned over it or to send the message to the control's parent (hard coded to be the Panel) if it pointer is not over the control. I tried to comment it enough to follow, but if you have any questions, feel free to ask.

Imports System.Runtime.InteropServices

Public Class Form1
   Inherits System.Windows.Forms.Form
   ' Implement IMessageFilter so that we can intercept the scroll message
   Implements IMessageFilter

    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Me.Panel1 = New PanelWithExposedWndProc ' **** note change
        Me.SuspendLayout()
        '
        'Panel1
        '
        Me.Panel1.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.Panel1.AutoScroll = True
        Me.Panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
        Me.Panel1.Location = New System.Drawing.Point(0, 0)
        Me.Panel1.Name = "Panel1"
        Me.Panel1.Size = New System.Drawing.Size(353, 408)
        Me.Panel1.TabIndex = 0
        '
        'Form1
        '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.ClientSize = New System.Drawing.Size(353, 408)
        Me.Controls.Add(Me.Panel1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)
    End Sub

    Public Sub New()
        InitializeComponent()
    End Sub

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        'on load add to Panel1 few comboboxes
        Dim i As Integer
        Dim YPosition As Integer = 0
        For i = 0 To 35
            Dim Combo As New System.Windows.Forms.ComboBox()
            Combo.Items.AddRange(New Object() {"First", "Second", "Third"})
            Combo.Location = New System.Drawing.Point(55, 95)
            Combo.Size = New System.Drawing.Size(121, 21)
            Combo.SelectedIndex = 0
            Combo.Location = New Point(5, YPosition)

            Me.Panel1.Controls.Add(Combo)
            YPosition += Combo.Height + 15
        Next
    End Sub

    'Friend WithEvents Panel1 As System.Windows.Forms.Panel
   ' Use the sub classed panel instead, need this to send it the scroll message
   Friend WithEvents Panel1 As PanelWithExposedWndProc

   Private Const WM_MOUSEWHEEL As Int32 = &H20A
   Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean Implements System.Windows.Forms.IMessageFilter.PreFilterMessage
      If m.Msg = WM_MOUSEWHEEL Then
         ' Get then control that message is directed to from the handle
         Dim c As Control = Control.FromChildHandle(m.HWnd)

         If c IsNot Nothing AndAlso c.Parent IsNot Nothing And c.Parent Is Panel1 Then
            ' The control in Focus is parented to Panel1
            ' Determine if the mouse pointer is currently over the control receiving the
            ' mouse wheel message
            ' 1st extract the mouse position from LParam
            Dim MouseLocation As New Point(m.LParam.ToInt32() And &HFFFF, m.LParam.ToInt32() >> 16)
            If Not c.ClientRectangle.Contains(c.PointToClient(MouseLocation)) Then
               ' pointer is not over the control, so block the message from going to the control in focus
               ' and resend the message to Panel1 so that it scrolls
               m.HWnd = Panel1.Handle
               Panel1.ProcessMessage(m)
               Return True ' returning True blocks the message
            End If
         End If
      End If
   End Function

   Private Sub Form1_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
      ' Message Filter is application wide, so only apply when this form is active
      Application.AddMessageFilter(Me)
   End Sub

   Private Sub Form1_Deactivate(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Deactivate
      ' Message Filter is application wide, so only apply when this form is active
      ' Since this form has been deactivate, remove the filter
      Application.RemoveMessageFilter(Me)
   End Sub
End Class

Public Class PanelWithExposedWndProc
   Inherits Panel
   ' This method just exposes the protected WndProc method
   Friend Sub ProcessMessage(ByVal msg As Message)
      Me.WndProc(msg)
   End Sub
End Class
 
Share this answer
 
v2

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