Wednesday, February 15, 2006

Let's say you have the following class:

   51 Public Class Person

   52     Private _name As String

   53 

   54     Public Event NameChanged As EventHandler

   55 

   56     Public Property Name() As String

   57         Get

   58             Return _name

   59         End Get

   60         Set(ByVal value As String)

   61             _name = value

   62             OnNameChanged(EventArgs.Empty)

   63         End Set

   64     End Property

   65 

   66     Protected Sub OnNameChanged(ByValAs EventArgs)

   67         RaiseEvent NameChanged(Me, e)

   68     End Sub

   69 End Class

We've set up the [Property]Changed events so that DataBinding will work correctly. Now, let's assume you want to bind the name property over to the text property of your TextBox control. We'll go the simple data binding route here for simplicity.

    3 Public Class Form1

    4     Private txtName As New TextBox

    5     Private _person As New Person

    6 

    7     Public Sub New()

    8         ' This call is required by the Windows Form Designer.

    9         InitializeComponent()

   10 

   11         ' Add any initialization after the InitializeComponent() call.

   12         Me.Controls.Add(txtName)

   13 

   14         _person.Name = "cool"

   15         txtName.DataBindings.Add("Text", _person, "Name")

   16     End Sub

   17 

   18     Private Sub Form_Click(ByVal sender As ObjectByValAs EventArgs) _

   19         Handles Me.Click

   20         _person.Name = "clicked"

   21     End Sub

   22 End Class

There! Now the txtName will display "clicked" whenever you click on the form. Magic! Now, let's change that Click event like so to see what happens:

   18     Private Sub Form_Click(ByVal sender As ObjectByValAs EventArgs) _

   19         Handles Me.Click

   20 

   21         Dim newPerson As New Person

   22         newPerson.Name = "new person"

   23         _person = newPerson

   24     End Sub

Why would I ever want to do is this way? Well, let's say you're remoting somewhere to get your data and your background thread returns a fully populated object. The easiest thing to do is to just point to the new object... except that it doesn't work.

All we did was change our instance over, but the textbox never changed. Why not? Because of REFERENCES, POINTERS, and MEMORY ADDRESSES! That's why! Check this out:

   18     Private Sub Form_Click(ByVal sender As ObjectByValAs EventArgs) _

   19         Handles Me.Click

   20 

   21         Dim newPerson As New Person

   22         newPerson.Name = "new person"

   23 

   24         Debug.WriteLine("Are references equal? "Object.ReferenceEquals(txtName.DataBindings("Text").DataSource, _person))

   25         _person = newPerson

   26         Debug.WriteLine("Are references equal? "Object.ReferenceEquals(txtName.DataBindings("Text").DataSource, _person))

   27     End Sub

Whoa! The first Debug.Writeline will give you TRUE, but the second will give you FALSE. You see, understanding how references and pointers work is important. The textbox's DataSource is holding onto a reference of the old Person instance. You now have two different instances in memory, whether you like it or not.

What do you do about it? Well, as far as I can tell, you can either update your DataSource reference (it did change after all) or you can move your properties over manually so that your references don't change. I'm open to suggestions on other methods here.

Comments are closed.