Thursday, October 25, 2012

How to prevent value-changed events from firing on form load in VB.NET

How to prevent value-changed events from firing on form load in VB.NET



Imagine you want a MsgBox to start if someone trigger an event. When your start the code, the Msgbox is called before the form is completely loaded and likely you get an empty. Look at the code below.


Public Class Form1
   
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        With ListBox1
            .Enabled = True 'if the listox is enable or disabled
            .Sorted = True ' if you want ti list sorted
            .BorderStyle = BorderStyle.Fixed3D ' the border style
            .Visible = True
            .ScrollAlwaysVisible = True 'presence of scroll all time
            .MultiColumn = False 'add a new column if number of items reach max height
        End With

        ListBox1.Items.Add("allo1")
        ListBox1.Items.Add("allo2")
        ListBox1.Items.Add("allo3")
        ListBox1.Items.Add("allo4")
        ListBox1.Items.Add("allo5")
        ListBox1.Items.Add("allo1")
        ListBox1.Items.Add("allo2")
        ListBox1.Items.Add("allo3")
        ListBox1.Items.Add("allo4")
        ListBox1.Items.Add("allo5")
        ListBox1.Items.Add("allo1")
        ListBox1.Items.Add("allo2")
        ListBox1.Items.Add("allo3")
        ListBox1.Items.Add("allo4")
        ListBox1.Items.Add("allo5")


        ListBox1.SelectedIndex = 2 '0 is the first one, 2 is the third.
        ListBox1.SelectedItem = "allo3" 'will always select the first he encounter

        ListBox1.SelectionMode = SelectionMode.MultiSimple 'no need to use shift or ctrl, only space or left-click
        ListBox1.SelectionMode = SelectionMode.MultiExtended  'no need to use shift or ctrl with left-click

    End Sub
    Private Sub ListBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ListBox1.SelectedIndexChanged

        MsgBox(ListBox1.SelectedIndex)



    End Sub


End Class



Last post wasn’t easy to understand because I wanted to show how a ListBox works. But when the ListBox reacts incorrectly, I feel that I a have to give more details. So now, is time or never to clear things. Is very important to understand how the form is loaded. I will do my best to make it simple.




Step 1 : create the form


We have to do at least something.


Double click your form and you will get this:


Public Class Form1
   
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        

    End Sub

End Class

What you is a class and every in every class, there is a constructor. The constructor is called first, because it create the object. It creates the space in the computer. Creating the object doesn’t means the object is initialized or ready. Again, the constructor new in the class form is :

Hey! Dear computer. I need like 10 Ko of Ram to store this form right now!


Where is the new function?


Let’s be lazy and use Visual Basic Express 2012 or Visual Studio 2012 to print it for us automatically.

Place your cursor anywhere in your form class but outside a function. Go on top of your screen and click on the ComboBox with Declarations in it.



If you choose New, it will put the function at the end of your class. Whatever the function new is printed before the load form function really doesn’t matter. The new function will always be the first function.


So here is the code in the form1 class. You have form1_Load and the New function.


Public Class Form1

    Private Sub form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    End Sub

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

    End Sub
End Class





Where is the InitializeComponent ?


Visual Studio or Visual Basic build a function named InitializeComponent. Inside that function, you could find all the little settings you made from the form1.vb Designer. 

Each time you place a control in the form, each time you customise the properties of your control inside that form designer, it translate them in the InitializeComponent function.

So where is the InitializeComponent ? Well... instead of telling where to find it, I’ll tell you how to find it.

Place your cursor overt the InitializeComponent, left click on it. Then press F12 on your keyboard. Visual Basic Express or Visual Studio will automatically bring you to the function definition.

And what could we find in that InitializeComponent function? Well see it for yourself. Simple settings.

So what do we have to understand? The program creates the form1 object using the new function. Then it calls the InitializeComponent inside that New function. The InitializeComponent set some general properties like size , color, and so on.

Ok, is time to move to the next step.


Step 3 : insert an event.


Alright, in the form I simply inserted and ListBox. To be honest, you could insert any control. It is really not important. Our goal here is to understand how thing works. The more you understand, the more you get better.

From the form designer, insert a ListBox. Double-click on that ListBox. Normally, your code should look like this:



Public Class Form1

    Private Sub form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    End Sub

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

    End Sub

    Private Sub ListBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ListBox1.SelectedIndexChanged

    End Sub

End Class


Your cursor should be in the ListBox1_SelectedIndexChanged function. That function whatever is called is really not important. You could rename it. What is important is at the end of the function line, there is a little phrase like this:


         Handles ListBox1.SelectedIndexChanged


Please don’t try to change that part.



Now, I want you to bring another event function from the ListBox1. Left-click inside the SelectedIndexChanged function and then try to click the ComboBox in the top right of your Visual Basic Express or Visual Studio. Look for the SizeChanged event and select it.


You should have a new ListBox1_SelectedIndexChanged function. OK. Now place a Msgbox and write something in it.


    Private Sub ListBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
        MsgBox("I love pizza")
    End Sub






Now run the program and notice what is happening.



You might get the Msgbox box 4 times. It happens to me 4 times…. And then the form loads.

Again what is happening? Each time the ListBox is resized, it calls the event function. But it calls it 4 times? What is going on?

Well, the first one is relatively easy to find out. The first time the function ListBox1_SizeChanged is called is in the InitializeComponent function. When the program set the size of your ListBox1.




        Me.ListBox1.Size = New System.Drawing.Size(195, 199)


You could try to surround the code with a lot of MsgBox  to really get it. Here the code with the MsgBox:

This is form1.vb


Public Class Form1

    Private Sub form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    End Sub

    Public Sub New()
        MsgBox("Function New : Started")
        ' This call is required by the designer.
        InitializeComponent()
        MsgBox("Function New : back to New Function")
        ' Add any initialization after the InitializeComponent() call.

    End Sub

    Private Sub ListBox1_Resize(sender As Object, e As System.EventArgs) Handles ListBox1.Resize
        ''Msgbox("ListBox1_Resize")
        'Dim oListBox As ListBox
        'oListBox = sender
        'MsgBox(oListBox.Size.Width & " " & oListBox.Size.Height)
    End Sub



    Private Sub ListBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
        'Msgbox("I love pizza")
    End Sub


    Private Sub ListBox1_SizeChanged(sender As Object, e As System.EventArgs) Handles ListBox1.SizeChanged
        MsgBox("I love Hot Dog")
    End Sub

 
End Class



And form1.vb [Designer]


<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer. 
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        MsgBox("Function InitializeComponent : Starting")
        Me.ListBox1 = New System.Windows.Forms.ListBox()
        MsgBox("Function InitializeComponent : ListBox1 Created")
        Me.SuspendLayout()
        '
        'ListBox1
        '
        Me.ListBox1.FormattingEnabled = True
        Me.ListBox1.Location = New System.Drawing.Point(30, 38)
        Me.ListBox1.Name = "ListBox1"
        Me.ListBox1.ScrollAlwaysVisible = True
        MsgBox("Function InitializeComponent : ListBox1 will get size")
        Me.ListBox1.Size = New System.Drawing.Size(195, 199)
        MsgBox("Function InitializeComponent : ListBox1 got the size")
        Me.ListBox1.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(284, 255)
        Me.Controls.Add(Me.ListBox1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)
        MsgBox("Function InitializeComponent : Leaving Function")
    End Sub
    Friend WithEvents ListBox1 As System.Windows.Forms.ListBox


End Class



The ListBox1 event is called one first time after we put a dimension in the ListBox1. For the 3 others times the event is called, they are no way we could easily know where they are coming from. Besides, there is no point to know exactly how they are called or why. People designed the System.Windows.Forms.ListBox to inherit other Controls. Those Controls Inherits others Objects and so on.

The simplest way to fix our problem, is the put a kind of switch that prevent our event from firing from the beginning and to put that switch to On when the form is completely loaded.

Using the form1’s build-in function called OnShown will help us.



Step 4: Overriding the OnShown function:


Your class form1 is built from a parent class called System.Windows.Forms.Form

Some functions in System.Windows.Forms.Form are very accessible like the events or the new function while other are harder to get.

To use those function, you need to use the Overrides to override a hidden function inside System.Windows.Forms.Form

Honestly, is not very important to fully understand how things are called or how thing works. Today, is time to use that function at our own advantage.

So let’s override the OnShown function and use it:


   Protected Overrides Sub OnShown(e As System.EventArgs)
        MyBase.OnShown(e)
    End Sub



Step 5: Placing the on-off variable:


You almost completed your mission. Is time to add a variable in your class. That variable will stop your code from firing events.

Your function is now available, we could put on and off Boolean to activate or disable the ListBox event.

I decided to name it EventsOn and I set it to false right at the beginning of the New function. I set the Boolean EventsOn at true once all my initial variables are set.

Here is what your code would look like with the Msgbox adjusted and with initial items in the List Box


Public Class Form1
    Private EventsOn As Boolean

    Protected Overrides Sub OnShown(e As System.EventArgs)
        MyBase.OnShown(e)
        EventsOn = True
    End Sub

    Private Sub form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    End Sub

    Public Sub New()
        'MsgBox("Function New : Started")
        EventsOn = False
        ' This call is required by the designer.
        InitializeComponent()
        'MsgBox("Function New : back to New Function")
        ' Add any initialization after the InitializeComponent() call.
        ListBox1.Items.Add("click me")
        ListBox1.Items.Add("click here")
    End Sub

    Private Sub ListBox1_Resize(sender As Object, e As System.EventArgs) Handles ListBox1.Resize
        ''Msgbox("ListBox1_Resize")
        'Dim oListBox As ListBox
        'oListBox = sender
        'MsgBox(oListBox.Size.Width & " " & oListBox.Size.Height)
    End Sub
    Private Sub ListBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
        If EventsOn = True Then
            MsgBox("I love pizza")
        End If

    End Sub

    Private Sub ListBox1_SizeChanged(sender As Object, e As System.EventArgs) Handles ListBox1.SizeChanged
        If EventsOn = True Then
            MsgBox("I love Hot Dog")
        End If
    End Sub

 
End Class



Final result:



Download: ListBoxSample.zip 






No comments:

Post a Comment