Search code examples
vb.netdata-bindingtextboxinotifypropertychanged

Proper Binding of a TextBox to an Object Property


I have a class with a property that I need to bind to a form TextBox in VB.NET. So far none of my attempts will have the TextBox updating in real time from the object properties.

I have the following code in my class

Public Class Servo

    Implements System.ComponentModel.INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Private _ServoID As Integer

    Property ServoID() As Integer

        Get
            Return _ServoID
        End Get

        Set(ByVal Value As Integer)

            _ServoID = Value
            Call LoadModelInfo()

            RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("ServoID"))

        End Set

    End Property


End Class

On the form load event I have the binding code

TextBoxCurrServoID.DataBindings.Add("Text", CurrentServoFormServoManager, "ServoID", False, DataSourceUpdateMode.OnPropertyChanged)

The object from the class is declared in module for global variables

Public CurrentServoFormServoManager As RobotModel2.Servo = New RobotModel2.Servo

The object/class works fine, I can write and read data from it's properties, no problems. If I attach/create a BindingList with it and set that as the datasource to a DataGridView the cells in the DataGridView update properly. I just can't seem to get it working with a TextBox. I can't see what I might be missing. Thanks.

EDIT - MORE INFORMATION:

After further research I found a similar post which mentioned that binding to a property of a class while the property is null can cause it not to work. You have to have data there BEFORE binding.

I added a button on the form and moved the binding code there and commented it out on the load event. I then load the form. Populate the CurrentServoFormServoManager object with data, then click the button to bind the TextBoxes to CurrentServoFormServoManager properties. And that works. I can programmatically change the data and the TextBoxes update.

The problem is. I cannot really use this as a solution. The object CurrentServoFormServoManager is constantly being destroyed and remade in this program. As soon as you make it = nothing or = to a new servo object the databinding breaks and has to be reset. I cannot do this since that event is happening outside of the form control. It is triggered by packets coming in from the serial port at different times. IE: I have to hang the form in order to wait for the serial packet to then rebind the data. And that is the solution I'm trying to get away from in the code.

I also attempted to populate the servo object, then bind the TextBoxes, and then only update the data without rebuilding the object and that can be updated from buttons on the form, but incoming threadsafe data from a serial packet will not update the TextBoxes. The data is properly updating in the object properties, and the datagrid which is bound through a BindingList updates fine, but the TextBoxes won't.

So... I'm still without a solution on how to bind TextBoxes to the properties of a class object that can be preset and left in place.


Solution

  • Possibly not the best solution, but if you are creating and destroying Servo objects (i.e. constantly changing the CurrentServoFormServoManager reference), then perhaps you need a container class that holds the Servo reference and exposes its properties by delegation, then raise an event when either the reference or the property changes.

    e.g. container class:

    Imports System.ComponentModel
    
    Public Class ServoContainer
      Implements System.ComponentModel.INotifyPropertyChanged
    
      Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    
      Private WithEvents _servo As Servo
    
      Sub New()
        _servo = New Servo
      End Sub
    
      Property Servo As Servo
        Get
          Return _servo
        End Get
        Set(value As Servo)
          _servo = value
          RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("ServoID"))
        End Set
      End Property
    
      Private Sub _servo_PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Handles _servo.PropertyChanged
        RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("ServoID"))
      End Sub
    
      Property ServoID As Integer
        Get
          Return _servo.ServoID
        End Get
        Set(value As Integer)
          _servo.ServoID = value
        End Set
      End Property
    End Class
    

    Instantiating the container:

    Public CurrentServoContainer As ServoContainer = New ServoContainer
    

    Replace your current textbox binding as follows:

    TextBoxCurrServoID.DataBindings.Add("Text", CurrentServoContainer, "ServoID", False, DataSourceUpdateMode.OnPropertyChanged)
    

    Now, both of the following lines will update the textbox:

    CurrentServoContainer.Servo.ServoID = 99
    

    and

    CurrentServoContainer.Servo = New Servo With {.ServoID = 98} 'replace with your actual constructor'