DataBind a Simple String Property to Textbox - vb.net

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.

Related

Binding Checkbox to a Shared Property in a custom class

So this should be simple I guess, but I have never done this.
Got a form on which I have a checkbox. I want this check box to be directly linked to a custom class's Boolean property, but somehow this does not work.
Public Class someClass
Private Shared _Filter_Neighbor_6X1 As Boolean = True
Shared Property Filter_Neighbor_6X1() As Boolean
Get
Return _Filter_Neighbor_6X1
End Get
Set(ByVal Value As Boolean)
_Filter_Neighbor_6X1 = Value
End Set
End Property
End Class
Public Class GameGUI
Private Sub GameGUI_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
chk_FilterAll.DataBindings.Add("Checked", someClass, "Filter_Neighbor_6X1")
end sub
End Class
The above does not work. It complains the "someClass" is a class.
I also tried:
chk_FilterAll.DataBindings.Add( _
New Binding("Checked", someClass, "Filter_Neighbor_6X1", _
False, DataSourceUpdateMode.OnPropertyChanged, False))
which hangs the app on start.
any help is appreciated.
Thanks!
You can't bind to a Shared property. You need to remove the Shared modifier from the property, create an instance of the class, and use that instance for the data binding. Your class would look something like this:
Public Class someClass
Private _Filter_Neighbor_6X1 As Boolean = True
Public Property Filter_Neighbor_6X1() As Boolean
Get
Return _Filter_Neighbor_6X1
End Get
Set(ByVal Value As Boolean)
_Filter_Neighbor_6X1 = Value
End Set
End Property
End Class
Then you can do something like:
Dim instanceOfSomeClass As New someClass()
chk_FilterAll.DataBindings.Add("Checked", instanceOfSomeClass, "Filter_Neighbor_6X1")
Note that if you don't have extra logic under the Get or Set of the property, you can get rid of the backing field and convert it to an Auto-Implemented (shorthand) property:*
Public Property Filter_Neighbor_6X1 As Boolean = true
Moreover, if you don't want to create an instance of the class each time, you can make it a Singleton:
Public Class someClass
Private Shared _instance As someClass
Private Sub New()
End Sub
Public Shared ReadOnly Property Instance As someClass
Get
If _instance Is Nothing Then _instance = New someClass()
Return _instance
End Get
End Property
Public Property Filter_Neighbor_6X1 As Boolean = True
End Class
Then, you can use it directly like this:
chk_FilterAll.DataBindings.Add("Checked", someClass.Instance, "Filter_Neighbor_6X1")
* You probably do need extra logic in this particular case in order to notify the change of the property so that the control can reflect that change. Check the other answer for more info.
If you don't want to get rid of the Shared modifier, you can add a read-only property which returns the value of the Shared one:
Public Class someClass
' ...
' ...
' TODO: Remember to use a different name.
Public ReadOnly Property MyNonSharedProperty As Boolean
Get
Return Filter_Neighbor_6X1
End Get
End Property
End Class
Then you can use it like this:
Dim instanceOfSomeClass As New someClass()
chk_FilterAll.DataBindings.Add("Checked", instanceOfSomeClass, "MyNonSharedProperty")
Or with the Singleton approach (check the other answer for details):
chk_FilterAll.DataBindings.Add("Checked", someClass.Instance, "MyNonSharedProperty")
However, please note that if you're implementing the INotifyPropertyChanged interface in your class*, you'll need to notify the change of the read-only property as well as the Shared one when the latter gets changed.
Full implementation:
Public Class someClass
Implements INotifyPropertyChanged
' ##################################
' Optional: Add Singleton logic here
' ##################################
Private _Filter_Neighbor_6X1 As Boolean = True
Public Property Filter_Neighbor_6X1() As Boolean
Get
Return _Filter_Neighbor_6X1
End Get
Set(ByVal Value As Boolean)
_Filter_Neighbor_6X1 = Value
NotifyPropertyChanged()
' TODO: Remember to change the name of the property.
NotifyPropertyChanged("MyNonSharedProperty")
End Set
End Property
' TODO: Remember to use a different name.
Public ReadOnly Property MyNonSharedProperty As Boolean
Get
Return Filter_Neighbor_6X1
End Get
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
* Which is something you should do when using the class as a DataBinding source.

Is there a Singleton that raises events?

I have a singleton class, but I want its object to be able to raise events.
My current singleton code is as follows:
Private Shared ReadOnly _instance As New Lazy(Of WorkerAgent)(Function() New _
WorkerAgent(), LazyThreadSafetyMode.ExecutionAndPublication)
Private Sub New()
End Sub
Public Shared ReadOnly Property Instance() As WorkerAgent
Get
Return _instance.Value
End Get
End Property
Whenever I change ReadOnly _instance As New.. into ReadOnly WithEvents _instance As New...
I get an error saying ReadOnly is not valid on a WithEvents deceleration
Although I can create the instance in the property itself, but I liked the above code because it is using .NET Lazy keyword which probably have great multithreading benefits.
This isn't an answer to your question as asked but it demonstrates why that question doesn't make sense. It also requires a fair chunk of code so posting in a comment wasn't really an option. This is how your singleton class would raise events, i.e. just like any other class, and how a consumer would handle those events, i.e. just like for any other type.
Singleton:
Public Class WorkerAgent
Private Shared ReadOnly _instance As New Lazy(Of WorkerAgent)
Private _text As String
Public Shared ReadOnly Property Instance As WorkerAgent
Get
Return _instance.Value
End Get
End Property
Public Property Text As String
Get
Return _text
End Get
Set
If _text <> Value Then
_text = Value
OnTextChanged(EventArgs.Empty)
End If
End Set
End Property
Public Event TextChanged As EventHandler
Private Sub New()
End Sub
Protected Overridable Sub OnTextChanged(e As EventArgs)
RaiseEvent TextChanged(Me, e)
End Sub
End Class
Note that the instance event is raised when the instance property changes, just as for any other type, singleton or not.
Consumer:
Public Class Form1
Private WithEvents agent As WorkerAgent = WorkerAgent.Instance
Private Sub agent_TextChanged(sender As Object, e As EventArgs) Handles agent.TextChanged
'...
End Sub
End Class
The field that the single instance is assigned to is where WithEvents gets used. As your error message states, that field cannot be declared ReadOnly too. If they want a ReadOnly field then they need to use AddHandler to handle events.

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.

Binding Combobox to Object Datasource

I've got a form bound to an object datasource. It has one text box and one combo box. I set up one binding source for the main object and one binding source for the combo box. When I run the form, the text box is bound correctly, and the list of values in the combo box is bound correctly, but the ValueMember of the combo box isn't working correctly.
The combo box shows the correct list, but it's selected index is 0 instead of what it should be 2. When I change the value in the text box, it's bound object's Property.Set method is called correctly, but the same Property.Set method is not called for the combo box.
I know I can hack up the OnSelectedIndex change methods in the form, but I would like to know what I am doing wrong in just using the Bindings.
Here is the code on the form:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Dim NameValueBindingSource1 As New BindingSource()
Dim WorkOrderBindingSource1 As New BindingSource
'Create main object to bind to
Dim wo As New WorkOrder
wo.WOIndex = "2012-0111"
wo.WorkOrderType = 3
'Create list object for combo box
Dim NameValues As BindingList(Of NameValue)
NameValues = FillNameValueList()
'Bind Text Box to Binding Source
WorkOrderBindingSource1.DataSource = wo
WOIndexTextBox1.DataBindings.Add("Text", WorkOrderBindingSource1, "WOIndex")
'Bind Combo Box to Binding Source
NameValueBindingSource1.DataSource = NameValues
WorkOrderTypeCombo.DataSource = NameValueBindingSource1
WorkOrderTypeCombo.DisplayMember = "Value"
WorkOrderTypeCombo.ValueMember = "Code"
End Sub
Function FillNameValueList() As BindingList(Of NameValue)
Dim bl As New BindingList(Of NameValue)
Dim nv As NameValue
nv = New NameValue
bl.Add(New NameValue("Short", 0))
bl.Add(New NameValue("Middle", 1))
bl.Add(New NameValue("Long", 2))
bl.Add(New NameValue("Very Long", 3))
Return bl
End Function
End Class
Here's the code for the main object - "WorkOrder"
Imports System.ComponentModel
Public Class WorkOrder
Implements IEditableObject
Implements INotifyPropertyChanged
Private mWOIndex As String
Private mWorkOrderType As Integer
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Public Property WOIndex As String
Get
Return mWOIndex
End Get
Set(value As String)
mWOIndex = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("WOIndex"))
End Set
End Property
Public Property WorkOrderType As Integer
Get
Return mWorkOrderType
End Get
Set(value As Integer)
mWorkOrderType = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("WorkOrderType"))
End Set
End Property
Public Sub BeginEdit() Implements System.ComponentModel.IEditableObject.BeginEdit
End Sub
Public Sub CancelEdit() Implements System.ComponentModel.IEditableObject.CancelEdit
End Sub
Public Sub EndEdit() Implements System.ComponentModel.IEditableObject.EndEdit
End Sub
End Class
Here's the code for the object used in the combo box
Imports System.ComponentModel
Public Class NameValue
Implements IEditableObject
Implements INotifyPropertyChanged
Private mValue As String
Private mCode As Integer
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Public Property Code As Integer
Get
Return mCode
End Get
Set(value As Integer)
mCode = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Code"))
End Set
End Property
Public Property Value As String
Get
Return mValue
End Get
Set(value As String)
mValue = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Value"))
End Set
End Property
Public Sub BeginEdit() Implements System.ComponentModel.IEditableObject.BeginEdit
End Sub
Public Sub CancelEdit() Implements System.ComponentModel.IEditableObject.CancelEdit
End Sub
Public Sub EndEdit() Implements System.ComponentModel.IEditableObject.EndEdit
End Sub
Public Sub New(InitValue As String, InitCode As Integer)
Value = InitValue
Code = InitCode
End Sub
End Class
In your code, you are merely assigning the DataSource to the ComboBox, but you're not establishing any DataBinding for it.
You need a line like this (using C# here):
WorkOrderTypeCombo.DataBindings.Add(new System.Windows.Forms.Binding("SelectedValue", WorkOrderBindingSource1, "WorkOrderType", true));
Hope this helps

VB.NET CheckedListBox Tag?

Is there a Tag for an Item in the CheckedListBox? Or something similar? I would like to be able to store and ID associated with the item that I'm displaying.
You don't need a Tag property. The control accepts any object, that means you don't have to put just strings in it. Make a class that has a string (and overrridden ToString()) and any other data members you need.
Public Class Form1
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
CheckedListBox1.Items.Add(New MyListBoxItem() With {.Name = "One", .ExtraData = "extra 1"})
CheckedListBox1.Items.Add(New MyListBoxItem() With {.Name = "Two", .ExtraData = "extra 2"})
End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
For Each obj As Object In CheckedListBox1.CheckedItems
Dim item As MyListBoxItem = CType(obj, MyListBoxItem)
MessageBox.Show(String.Format("{0}/{1} is checked.", item.Name, item.ExtraData))
Next
End Sub
End Class
Public Class MyListBoxItem
Private _name As String
Private _extraData As String
Public Property Name As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Property ExtraData As String
Get
Return _extraData
End Get
Set(ByVal value As String)
_extraData = value
End Set
End Property
Public Overrides Function ToString() As String
Return Name
End Function
End Class
(The overridden ToString() dictates what will be displayed in the box.)
You can inherit your own control from CheckedListBox and create a property, in C# it would be like this, the rest of the functionality remains the same as it is inherited so no further additional code required:
public class MyCheckedListbox : System.Windows.Forms.CheckedListBox{
private object thisObj;
public object Tag{
get{ return this.thisObj; }
set{ this.thisObj = value; }
}
}
Edit: Decided to include the VB.NET version for everyone's benefit also...
Public Class MyCheckedListBox Inherits System.Windows.Forms.CheckedListBox
Private thisObj As Object
Public Property Tag As Object
Get
Tag = thisObj
End Get
Set (objParam As Object)
thisObj = objParam
End Set
End Property
End Class
Of course, this is plain and uses boxing but works nicely...
Hope this helps
Translation of tommieb75 answer to VB.NET:
Public Class MyCheckedListbox
Inherits System.Windows.Forms.CheckedListBox
Private thisObj As Object
Public Property Tag() As Object
Get
Return Me.thisObj
End Get
Set(ByVal value As Object)
Me.thisObj = value
End Set
End Property
End Class
I use the translator at www.developerfusion.com/tools