RaiseEvent not handling from within constructor of custom class

jason2li

Member
Joined
Nov 18, 2005
Messages
22
Location
St. Louis, MO
Programming Experience
5-10
Hi, I've been developing for years, and have been using my own events just as long, but this one has me stumped. I could have sworn this used to work.

(I'm using VS2005)

VB.NET:
Public Class Form1

    Private WithEvents Test As TestClass

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

        Test = New TestClass
        Test.Add(100)

    End Sub

    Private Sub Test_Added(ByVal Index As Integer) Handles Test.Added
        Console.WriteLine(Index)
    End Sub

End Class

Public Class TestClass
    Inherits CollectionBase

    Public Event Added(ByVal Index As Integer)

    Public Sub New()

        For i As Integer = 0 To 99
            Add(i)
        Next

    End Sub

    Public Sub Add(ByVal Index As Integer)
        RaiseEvent Added(Index)
    End Sub

End Class

This is a simple example class to demonstrate. I instantiate the class, and run a loop from the constructor, which adds several items to the collection. Once something is added, it calls the RaiseEvent. This is not handled, and I don't know why. I'm assuming it has to do with the fact that the class object doesn't technically exist on our main form yet (since we're in the middle of constructing it), but I thought I've done this before.

Anyway, once the constructor is complete, I add another item from the main form by calling the "Add" procedure. This one is handled correctly.

So the only thing that is written in the console window is

VB.NET:
100

Is there any way to make this work? I know I can just not do the work from within the constructor, and everything will work fine. But I'm curious if there is a workaround besides that.
 
From what I can see you do not have a handler for the event. The event is firinig but does not know where to go. Before you go through the loop use the AddHandler keyword and pass it a place to go and the control you wish to addhandler for.


And also you should try to call mybase.new in the constructor at the end. And in the item property you should also call mybase.item.add and this will actually add it to the collection.
 
Thanks for responding so quickly...

From what I can see you do not have a handler for the event. The event is firinig but does not know where to go. Before you go through the loop use the AddHandler keyword and pass it a place to go and the control you wish to addhandler for.

The event handler is on the main form. I don't want to handle it from within the custom class, I want the custom class to alert the main form that an item as been added. And like I said, it works perfectly fine when you call "Pictures.Add(100)" from the main form, the event is handled exactly how I wanted it to.

And also you should try to call mybase.new in the constructor at the end.

I've never used this. What exactly does this do? I added it and it didn't seem to do anything.

And in the item property you should also call mybase.item.add and this will actually add it to the collection.

Oops, yes, thanks. I forgot to add that. I normally use "List.Add", which accomplishes the same thing (I think).
 
Not until after SubNew constructor method is finished is the instance created and returned, and not until this assignment has happened will your withevents start receiving events from it.

A workaround if you need to do this in constructor and also need to get events from this is to delay the work by running it asynchronous, for example with a BackgroundWorker. Here is an example of that:
VB.NET:
    Public Class TestClass
        Inherits CollectionBase

        Public Event Added(ByVal Index As Integer)
        Private WithEvents worker As New System.ComponentModel.BackgroundWorker

        Public Sub New()
            worker.WorkerReportsProgress = True
            worker.RunWorkerAsync()
        End Sub

        Public Sub Add(ByVal Index As Integer)
            RaiseEvent Added(Index)
        End Sub

        Private Sub worker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) _
        Handles worker.DoWork
            Threading.Thread.Sleep(1)
            For i As Integer = 0 To 99
                worker.ReportProgress(0, i)
            Next
        End Sub

        Private Sub worker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
        Handles worker.ProgressChanged
            Add(e.UserState)
        End Sub
    End Class
 
It is unusual to inherit a class from CollectionBase with .Net 2.0, it is probably because you haven't heard of Generics yet. Inherit instead a List(Of T) where T is the strong type of collection you want to create, and overload the methods you need to. For example:
VB.NET:
    Public Class IntegerList
        Inherits List(Of Integer)

        Public Overloads Sub Add(ByVal item As Integer)
            MyBase.Add(item)
        End Sub

    End Class
Usage would be:
VB.NET:
            Dim t1 As New IntegerList
            t1.Add(1)
If you want this collection to be more generic in regards to types you declare it a Class(Of T), example:
VB.NET:
    Public Class XList(Of X)
        Inherits List(Of X)

        Public Overloads Sub Add(ByVal item As X)

        End Sub

    End Class
Usage like this, user(developer) say when creating the instance what type the collection should be:
VB.NET:
            Dim t1 As New XList(Of Integer)
            t1.Add(1)
edit note: you could also implement IList(Of T) instead of inheriting, but you would have to implement all members.
 
Last edited:
It is unusual to inherit a class from CollectionBase with .Net 2.0, it is probably because you haven't heard of Generics yet. Inherit instead a List(Of T) where T is the strong type of collection you want to create, and overload the methods you need to.

You're right, I haven't heard of "Generics". This is great! Thanks.
 
Back
Top