Binding a VB.NET label.text to an object property - vb.net

I want to have a label in a form whose text value changes depending upon the value of an instance of a class. It looks like I can bind the text value of the label to an object dataSource. When I try this it does not seem to work.
Me.Label4.DataBindings.Add(New System.Windows.Forms.Binding("Text", Me.ItemInfoBindingSource, "ItemNumber", True, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged))
My itemInfoBindingSource:
Me.ItemInfoBindingSource.DataSource = GetType(CFP.ItemInfo)
and the class definition:
Public Class ItemInfo
Public Property ItemNumber As String = "rename"
Public Property Description As String
Public Property FileLocation As String
Public Property CompileHistory As List(Of CompileHistory)
End Class
I think what I have done is to bind to a class, not an instance of a class. Thinking about it, what I really want to do is bind an instance of a class to a label... How?
Is this possible?

Yes, this is possible, but you need to raise an event to let the label know that the property has changed. If you were using a type like a BindingList, this would be done automatically, but you're trying to bind to a String which doesn't raise PropertyChanged events.
To add the event to your class:
Change your class definition to implement INotifyPropertyChanged
Add the corresponding PropertyChanged event
Change the auto-implemented property to an expanded property and raise the event.
Here's the result of these changes for just the ItemNumber property in your class:
Public Class ItemInfo
Implements System.ComponentModel.INotifyPropertyChanged
Private _itemNumber As String = "rename"
Public Property ItemNumber As String
Get
Return _itemNumber
End Get
Set(value As String)
_itemNumber = value
RaiseEvent PropertyChanged(Me,
New System.ComponentModel.PropertyChangedEventArgs("ItemNumber"))
End Set
End Property
Public Event PropertyChanged(sender As Object,
e As System.ComponentModel.PropertyChangedEventArgs) _
Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
I added a text box and label to a form, added the data binding in the Form.Load event, added a field called ItemInfoBindingSource of type ItemInfo, and updated the ItemNumber in the TextBox.TextChanged event.
Private ItemInfoBindingSource As New ItemInfo
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.DataBindings.Add("Text", Me.ItemInfoBindingSource, "ItemNumber")
End Sub
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) _
Handles TextBox1.TextChanged
ItemInfoBindingSource.ItemNumber = TextBox1.Text
End Sub
Now, when you type in the text box, ItemNumber.Set is called, and raises an event to let anything listening know that it's been changed. The label is listening, and it updates its Text property so that you can see the new value.

Related

DataBind a Simple String Property to Textbox

I have a Simple Property called Customer as string
I want to bind this property to a Textbox.Text Databinding
I use the INotifyPropertyChanged Interface.
If I want to add the Databindings with
TextBox1.DataBindings.Add("Text", Customer, "Text")
I get an Error with:
You cannot bind text to the property or column for the DataSource.
Parameter name: dataMember
Public Class Form1
Implements INotifyPropertyChanged
Private _Customer As String = "DEFAULT"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.DataBindings.Add("Text", Customer, "Text")
End Sub
Public Property Customer As String
Get
Return _Customer
End Get
Set
_Customer = Value
NotifyPropertyChanged()
End Set
End Property
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
The reason is that you can't bind to a property directly, you need to bind to an object which contains the property. Also you can't use a property that is inside the Form1 class. You need to set up an instance of an object.
I've created a sample that uses a class named Customer with a single property called Name. I've also created a base class. This is not required, but is useful if you create multiple classes.
Public Class BaseNotify
Implements INotifyPropertyChanged
Friend Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Friend Sub NotifyPropertyChanged(<CallerMemberName()> Optional propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Public Class Customer
Inherits BaseNotify
Private _name As String = "DEFAULT"
Public Property Name As String
Get
Return _name
End Get
Set
If (_name = Value) Then Return
_name = Value
NotifyPropertyChanged()
End Set
End Property
End Class
Finally set up the form (I also renamed the textbox to something more meaningful.
Public Class Form1
Private _customer As Customer
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
BindProperties()
End Sub
Private Sub BindProperties()
_customer = New Customer()
Me.tbName.DataBindings.Add("Text", _customer, NameOf(Customer.Name))
End Sub
End Class
Using NameOf is recommended, as it won't break the code if you decide to change the property name at a later stage.

Change control text when property changes

I need to change my label MyLabel.Text once property Path is changed. Look below my code. I know how to do it from outside my class FrmImport that i could just subscribe to PropertyChanged event however i am not sure how to do it from FrmImport class itself. You can see that i've implemented INotifyPropertyChanged and created OnPropertyChanged which is called once path is set: OnPropertyChanged("Path"). Now i do not belive to subscribe my event in FrmImport itself but rather than that I think i just need to make somehow: MyLabel.DataBindings.Add(??) to make it work but right now stack on it. Can anybody help?
Public Class FrmImport
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(propName As String)
If propName IsNot Nothing Then
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
End If
End Sub
Private _path As String
Public Property Path As String
Get
Return _path
End Get
Set
_path = Value
OnPropertyChanged("Path")
End Set
End Property
Private Sub FrmImport_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' ??? MyLabel.DataBindings.Add("Text", Me.Path)
End Sub
or should i just make like this without any INotifyProperty usage?:
Public Property Path As String
Get
Return _path
End Get
Set
_path = Value
OnPropertyChanged("Path")
MyLabel.Text= Me.Path
End Set
End Property
I usually do binding when the data is stored outside of the form. For example, if you had a PathInformation class with a bunch of properties that needed to be displayed. There need to be something in place for the class to tell the form it got updated and the form to tell to class to update itself.
When the variable is directly in the form, binding isn't really needed. Just update the path directly. No need for OnPropertyChanged because nothing outside of the form need to know of the change (since they already called the Path property).
But this is borderline opinion and depends also on the rest of the code.

vb.net - how to change a property of a form element from another module

I am trying to change the background color of a button (cmdLogQry) from a Set-procedure of a public property (LogQry) - according to the new value of the property.
It works if the property is being changed in the code belongig to the form containing the button (in the Click method of the same or even another button). But it does not work if the property is being changed from another module (handler procedure for COM ports DataReceived event). No error message or anything - the LogQry gets its value changed all right, but the color of the button does not change.
What do I do wrong?
Public Class Handler
Private _logQry As Boolean = False
Public Property LogQry() As Boolean
Get
Return _logQry
End Get
Set(ByVal value As Boolean)
_logQry = value
If value Then
frmMain.cmdLogQry.BackColor = Color.Red
Else
frmMain.cmdLogQry.BackColor = Color.Blue
End If
End Set
End Property
Private Sub comPort_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
...
LogQry = Not LogQry ' does NOT change color
...
End Sub
End Class
Public Class frmMain
Private comm As New Handler()
...
Private Sub cmdLogQry_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdLogQry.Click
comm.LogQry = Not comm.LogQry ' does change color
End Sub
...
End Class
This problem is caused by the default instance of the form class created by the VB.NET implementation. More on default instances could be found here and in this answer from Hans Passant.
Essentially when you define a form class, VB.NET compiler creates a default instance of that class named with the same name of the class, but this creates a lot of misunderstanding in an object oriented environment like NET.
To fix your problem you need to implement a constructor in your Handler class that receives the actual instance of frmMain, store it inside a class variable and use that instance when you want to modify something on the actual displayed form
Public Class Handler
Private _logQry As Boolean = False
Private _mainInstance As frmMain
Public Sub New(mainInstance as frmMain)
_mainInstance = mainInstance
End Sub
Public Property LogQry() As Boolean
Get
Return _logQry
End Get
Set(ByVal value As Boolean)
_logQry = value
If value Then
_mainInstance.cmdLogQry.BackColor = Color.Red
Else
_mainInstance.cmdLogQry.BackColor = Color.Blue
End If
End Set
End Property
....
End Class
Now, when you create the Handler instance pass the reference to the current frmMain
Public Class frmMain
Private comm As Handler
...
Private Sub cmdLogQry_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdLogQry.Click
comm = new Handler(Me)
comm.LogQry = Not comm.LogQry ' does change color
End Sub
...
End Class
Keep in mind that this solution creates also problems. It couples the class Handler to your frmMain and the two are now inseparable. Probably a better approach is to create an Event in the Handler class so, every form that wants to be notified could subscribe to the event and receieves the information when needed.

How to update UI when a property change

I'm trying to update my UI when a property in my BL class changes. Please can someone advise the best way to do this in vb.net
Not a really precise question so I will explain the standard way (in my opinion).
Implement the INotifyPropertyChanged interface in your class and handle the PropertyChanged event of your object.
First the the class of the object that contains the property in question:
Public Class MySweetClass
Implements System.ComponentModel.INotifyPropertyChanged
Private _MyProperty As String
Public Property MyProperty As String
Get
Return _MyProperty
End Get
Set(value As String)
_MyProperty = value
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("MyProperty"))
End Set
End Property
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Notice that the PropertyChanged event is raised once the value of the property changes.
In your form handle this event:
Public Class Form1
Private WithEvents MySweetObject As MySweetClass
Private Sub MySweetObject_PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Handles MySweetObject.PropertyChanged
'Update gui here
End Sub
End Class
This lets you update the GUI whenever the value changes.

Event Raised in an Object also fires in a separate Object of same Class?

VB NET 2010, Framework 3.5
Trying to understand why this works. I create two objects from the the same Class1. GlobalClass1 with Global Scope and LocalClass1 with Module Scope.
In UControl's Load event I set LocalClass1 = GlobalClass1 From this point on anytime I change value of GlobalClass1.TestProperty the property value is also updated in LocalClass1. The Events in LocalClass1 are triggered when GlobalClass1 events fire.
This is the result I was looking for => being able to have a Global Object's events fire in several other Class Modules and User Controls.
I don't quite understand why simply setting the Local Object = Global Object causes the Local Object to automatically update it's property values when the Global Object properties are updated or why the Local Events automatically fire when the Global Object is raising the event?
Module Module1
Public WithEvents frm As New MainForm
Public WithEvents GlobalClass1 As New Class1
Public Sub Main()
frm.Init()
frm.ShowDialog()
End Sub
End Module
Public Class MainForm
Private uiUserControl As UControl
Public Function Init() As Boolean
uiUserControl = New UControl
uiUserControl.Location = New System.Drawing.Point(60, 80)
Me.Controls.Add(uiUserControl)
Return True
End Function
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Static count As Integer
GlobalClass1.TestProperty = count.ToString ' This line propagates to the Private Local LocalClass1 and cause it's event to fire??
count += 1
End Sub
End Class
Public Class UControl
Private WithEvents LocalClass1 As New Class1
Private Sub UControl_Load(sender As Object, e As System.EventArgs) Handles Me.Load
LocalClass1 = GlobalClass1
End Sub
Private Sub LocalClass1_TestPropertyChanged() Handles LocalClass1.TestEvent
Me.TextBox1.Text = LocalClass1.TestProperty 'This Event fires when events are raised in the other object => GlobalClass1??
End Sub
End Class
Public Class Class1
Public Event TestEvent()
Private _testProperty As String
Public Property TestProperty() As String
Get
Return _testProperty
End Get
Set(ByVal value As String)
_testProperty = value
RaiseEvent TestEvent()
End Set
End Property
End Class
The class instances (objects) are both referencing the same memory space allocated to them on the heap.
from comments:
The problem is with this line: LocalClass1 = GlobalClass1
They started out as different objects but then you made them refer to the same thing. Object references - or reference types - work differently than value types like integer:
Dim x As Int32
Dim y As Int32 = 42
x = y
As value types, the value of y is assigned to x. A reference type is essentially a wrapper or alias for a pointer. Your assignment code therefore replaced the original pointer to a New Class1, with the pointer already assigned to GlobalClass1
Read more about Value vs Reference types at MSDN