locked
RaiseEvent not Raising the Event RRS feed

  • Question

  • I have the typical Person class:

    Public Class Person Implements INotifyPropertyChanged Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private clsFirstName As String <other properties> Public Property FirstName As String Get Return clsFirstName End Get Set(ByVal value As String) If value = String.Empty Then If clsFirstName <> String.Empty Then clsFirstName = String.Empty OnPropertyChanged("FirstName") End If Else If value <> clsFirstName Then clsFirstName = value OnPropertyChanged("FirstName") End If End If End Set End Property Protected Sub OnPropertyChanged(ByVal name As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name)) End Sub End Class

    In code behind on the MainWindow I have

    Public Sub PropertyChanged(sender As Object, args As PropertyChangedEventArgs)
    		Debug.WriteLine(args.PropertyName)
    End Sub

    When the FirstName name changes, the PropertyChanged event is not being triggered.  I put a breakpoint at the "Set" line and stepped though the code.  Sub On PropertyChanged is called and the RaiseEvent (presumably) raises the PropertyChanged event.  But that routine behind the MainWindow is never executed.  What am I doing wrong?

    Sunday, January 17, 2016 10:49 AM

Answers

  • Thanks for the valuable replies.  I have dug into the issue a bit more and can characterize the problem somewhat differently.

    My form has a grid to display the properties of People where:

    Dim People As IList(Of Person) = New List(Of Person)

    I populate People as follows (showing just the first name):

    Dim p As Person
    p = New Person
    p.FirstName = "Joe"
    People.Add(p)
    p = New Person
    p.FirstName = "Mary"

    etc. for all the data.

    As Frank L. Smith noted, I want to avoid "false alarms" so after I have People filled, I use it as the data context for the grid and create the Event Handler as follows:

    dgrMain.DataContext = People
    AddHandler p.PropertyChanged, AddressOf PropertyChanged

    I thought the event was not being handled because I did all my testing on the first couple of rows on the grid.  Just by chance, I tried the last row in the grid and the event was handled properly.  Now I see the problem: I am adding the handler ONLY to the last person added to People.

    I'm not sure exactly how to go about adding the event handler to People but Frank L. Smith (and others) have provided enough info to get me going on it.  Stand by ...... and thanks again.

    Monday, January 18, 2016 1:54 AM

All replies

  • Try at least

    Public Sub PropertyChanged(sender As Object, args As PropertyChangedEventArgs) handles PropertyChanged


    Success
    Cor

    Sunday, January 17, 2016 11:12 AM
  • It seems you need to add a handler in your MainWindow. Something like:

    'Your object declaration'

    Public WithEvents MyPerson as Person

    'Constructing it first'

    MyPerson = new Person

    'then the handler:' AddHandler myPerson.OnPropertyChanged, AddressOf Handler_Person_PropertyChanged

    'Later on in the form you declare'

    Private Sub Handler_Person_PorpertyChanged(ByVal eventInfo As String, ByVal eventMessage As String)
            MessageBox.Show(eventMessage & Environment.NewLine)
    End Sub

    Have you read the msdn on the subject?: https://msdn.microsoft.com/en-us/library/edzehd2t%28v=vs.110%29.aspx



    Cyrille Precetti
    Bonne Année! Happy New Year!



    Sunday, January 17, 2016 11:17 AM
  • I don`t see a problem with the code in the Person Class so,  it must have something to do with how you are using the class in the Main Form.  You should show the code that creates/adds the Person classes in the main form.

     As a simple example

    Public Class Form1
    
        Private ListOfPersons As New List(Of Person)
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim p As New Person
            AddHandler p.PropertyChanged, AddressOf Person_PropertyChanged
            ListOfPersons.Add(p)
        End Sub
    
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            ListOfPersons(0).FirstName = "Mike" 'change the name of the person
        End Sub
    
        Private Sub Person_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
            MessageBox.Show(e.PropertyName & " was changed")
        End Sub
    
    End Class
    


    If you say it can`t be done then i`ll try it

    • Edited by IronRazerz Sunday, January 17, 2016 11:28 AM
    Sunday, January 17, 2016 11:22 AM
  • SezMe,

    I have never found a use for that Interface. Your class knows when something changed because your class is doing the changing!

    For what it's worth, I'd just create my own event and use a sub to raise it (that's not actually needed but it's typical), raise it when the setter of any property is invoked, and be done with it.

    *****

    Do note this: When the class is first instantiated, the event will be raised so you need to handle that if you don't want "false alarms".


    If I had eight hours to chop down a tree, I'd spend six sharpening my axe. -- Abraham Lincoln

    Sunday, January 17, 2016 3:13 PM
  • SezMe,

    I realize that the class that I'll show next is different than what you have. Mine is a collection class, but if you think about it, with "Person" you'll end up with a collection somehow:

    Public Class People Inherits System.Collections.CollectionBase Public Enum ModificationType FirstName LastName End Enum Public Event NameChanged(ByVal sender As Object, ByVal e As EventArgs) Protected Overridable Sub OnNameChanged() RaiseEvent NameChanged(Me, New EventArgs) End Sub Public Class CollectionItems Private _firstName As String Private _lastName As String Friend Sub New(ByVal firstName As String, _ ByVal lastName As String) Try If String.IsNullOrWhiteSpace(firstName) Then Throw New ArgumentException("The first name cannot be null or empty.") ElseIf String.IsNullOrWhiteSpace(lastName) Then Throw New ArgumentException("The last name cannot be null or empty.") Else _firstName = firstName.Trim _lastName = lastName.Trim End If Catch ex As Exception Throw End Try End Sub Public Property FirstName As String Get Return _firstName End Get Friend Set(ByVal value As String) Try If String.IsNullOrWhiteSpace(value) Then Throw New ArgumentException("The first name cannot be null or empty.") Else _firstName = value.Trim End If Catch ex As Exception Throw End Try End Set End Property Public Property LastName As String Get Return _lastName End Get Friend Set(ByVal value As String) Try If String.IsNullOrWhiteSpace(value) Then Throw New ArgumentException("The last name cannot be null or empty.") Else _lastName = value.Trim End If Catch ex As Exception Throw End Try End Set End Property End Class Public Sub Add(ByVal firstName As String, _ ByVal lastName As String) Try If List.Count > 0 Then Dim ci As CollectionItems = Item(firstName, lastName, False) If ci IsNot Nothing Then Throw New ArgumentException("This is a duplicate entry.") Else List.Add(New CollectionItems(firstName, lastName)) End If Else List.Add(New CollectionItems(firstName, lastName)) End If Catch ex As Exception Throw End Try End Sub Public Sub Remove(ByVal index As Integer) Try If index > Count - 1 OrElse index < 0 Then Throw New _ ArgumentOutOfRangeException("Index", _ "The collection does not contain this index." & vbCrLf) Else List.RemoveAt(index) End If Catch ex As Exception Throw End Try End Sub Public Sub Remove(ByVal firstName As String, _ ByVal lastName As String) Try Dim ci As CollectionItems = Item(firstName, lastName) If ci IsNot Nothing Then List.Remove(ci) End If Catch ex As Exception Throw End Try End Sub Public Sub ModifyName(ByVal firstName As String, _ ByVal lastName As String, _ ByVal type As ModificationType, _ ByVal newValue As String) Try Dim ci As CollectionItems = Item(firstName, lastName) If ci IsNot Nothing Then If String.IsNullOrWhiteSpace(newValue) Then Throw New ArgumentException("The revised name cannot be null or empty.") Else Select Case type Case ModificationType.FirstName If ci.FirstName.ToLower.Replace(" "c, "") <> newValue.ToLower.Replace(" "c, "") Then ci.FirstName = newValue OnNameChanged() End If Case ModificationType.LastName If ci.LastName.ToLower.Replace(" "c, "") <> newValue.ToLower.Replace(" "c, "") Then ci.LastName = newValue OnNameChanged() End If End Select End If End If Catch ex As Exception Throw End Try End Sub Public ReadOnly Property Item(ByVal index As Integer) As CollectionItems Get Try If index > Count - 1 OrElse index < 0 Then Throw New _ ArgumentOutOfRangeException("Index", _ "The collection does not contain this index." & vbCrLf) Else Return DirectCast(List(index), CollectionItems) End If Catch ex As Exception Throw End Try End Get End Property Public ReadOnly Property Item(ByVal firstName As String, _ ByVal lastName As String, _ Optional ByVal throwIfNotFound As Boolean = True) As CollectionItems Get Try If String.IsNullOrWhiteSpace(firstName) Then Throw New ArgumentException("The first name cannot be null or empty.") ElseIf String.IsNullOrWhiteSpace(lastName) Then Throw New ArgumentException("The last name cannot be null or empty.") Else Dim qry As System.Collections.Generic.IEnumerable(Of CollectionItems) = _ From ci As CollectionItems In List.Cast(Of CollectionItems)() _ Where ci.FirstName.ToLower.Replace(" "c, "") = firstName.ToLower.Replace(" "c, "") AndAlso _ ci.LastName.ToLower.Replace(" "c, "") = lastName.ToLower.Replace(" "c, "") If qry.Count <> 1 Then If throwIfNotFound Then Throw New ArgumentException("The person specified is not in the collection.") Else Return Nothing End If Else Return qry.First End If End If Catch ex As Exception Throw End Try End Get End Property End Class


    Because of the way that I have it laid out, the event will only be raised when they modify the person's name; not when it's being added or removed. You could, though, also set up two more events: One when a new name is added and another when a name is removed.

    This is very simple empty EventArgs, but you could do much more elaborate things if you chose to (for example, to know whether it was the first name or the last name that got modified).

    To test it - and I hope you will - this is what I did:

    Option Strict On Option Explicit On Option Infer Off Public Class Form1 Private WithEvents _people As People Private Sub Form1_Load(sender As System.Object, _ e As System.EventArgs) _ Handles MyBase.Load _people = New People AddNewNames() ModifyPerson() End Sub Private Sub AddNewNames() Try _people.Add("George", "Washington") _people.Add("Abe", "Lincoln") _people.Add("Max", "Headroom") Stop ' If you'll now please hover your mouse over the ' class-scoped variable named "_people" and expand ' the inner list, you'll see the three names which ' were just added. ' ' Following that, let the program continue or, if ' you want, step into it to see what's actually ' going on with it. Catch ex As Exception MessageBox.Show(String.Format("An error occurred:{0}{0}{1}", _ vbCrLf, ex.Message), _ "Program Error", _ MessageBoxButtons.OK, _ MessageBoxIcon.Warning) End Try End Sub Private Sub ModifyPerson() Try _people.ModifyName("george", "wash in g ton", _ People.ModificationType.FirstName, _ "William") Catch ex As Exception MessageBox.Show(String.Format("An error occurred:{0}{0}{1}", _ vbCrLf, ex.Message), _ "Program Error", _ MessageBoxButtons.OK, _ MessageBoxIcon.Warning) End Try End Sub Private Sub _ _people_NameChanged(sender As Object, _ e As System.EventArgs) _ Handles _people.NameChanged MessageBox.Show("Name Was Changed") End Sub End Class


    I hope that might give you food for thought. :)


    If I had eight hours to chop down a tree, I'd spend six sharpening my axe. -- Abraham Lincoln

    Sunday, January 17, 2016 4:02 PM
  • That interface is used extensively in WPF for databinding.  It is used to notify the GUI that a property has changed and therefore the GUI should echo the change.

    The only time it is really needed is when the values in a bound object change as a result of code changes to a property.  Changes made by the user in the GUI don't require this interface.

    I have never need to "capture" these changes with this interface , only notify using the RaiseEvent


    Lloyd Sheen

    Sunday, January 17, 2016 4:37 PM
  • Thanks for the valuable replies.  I have dug into the issue a bit more and can characterize the problem somewhat differently.

    My form has a grid to display the properties of People where:

    Dim People As IList(Of Person) = New List(Of Person)

    I populate People as follows (showing just the first name):

    Dim p As Person
    p = New Person
    p.FirstName = "Joe"
    People.Add(p)
    p = New Person
    p.FirstName = "Mary"

    etc. for all the data.

    As Frank L. Smith noted, I want to avoid "false alarms" so after I have People filled, I use it as the data context for the grid and create the Event Handler as follows:

    dgrMain.DataContext = People
    AddHandler p.PropertyChanged, AddressOf PropertyChanged

    I thought the event was not being handled because I did all my testing on the first couple of rows on the grid.  Just by chance, I tried the last row in the grid and the event was handled properly.  Now I see the problem: I am adding the handler ONLY to the last person added to People.

    I'm not sure exactly how to go about adding the event handler to People but Frank L. Smith (and others) have provided enough info to get me going on it.  Stand by ...... and thanks again.

    Monday, January 18, 2016 1:54 AM
  • SezMe,

    In the end, you have to work out for yourself anyway. Whether you use that interface or another or your own event, you have to work through it.

    You're on to what's going on and that's a good thing!

    Keep at it and don't give up. :)


    If I had eight hours to chop down a tree, I'd spend six sharpening my axe. -- Abraham Lincoln

    Monday, January 18, 2016 1:58 AM
  • Yes, I've read that link many times.  Obviously I did not absorb it all correctly - see my comment below.
    Monday, January 18, 2016 2:00 AM