Databound TextBox not updating with Source - vb.net

I have a custom Class that implements INotifyPropertyChanged as follows (extraneous and repeated properties removed):
Public Class Map
Implements INotifyPropertyChanged
Private _wages As Decimal
Property Wages As Decimal
Get
Return _wages
End Get
Set(value As Decimal)
Debug.Print("Event Raised: " & _wages & " " & value)
_wages = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Wages))
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
The Class is being Serialized/Deserialized correctly. When I load the XML file the TextBox that displays wages is not updating to reflect the Wages value.
I have the (Devexpress) TextBox DataBindings set with:
txtWages.DataBindings.Add(New Binding("EditValue", mymap, "Wages", False, DataSourceUpdateMode.OnPropertyChanged))
When I load the file I can see the old and new value from Debug.Print:
Event Raised: 0 13
However, txtWages stays as 0
I have read these answers Here and Here and tried to look for ideas but I am out of ideas.
EDIT: It seems that if I populate the class with the Deserialized XML and then set the Bindings it works, but not when the bindings are set first. I would have thought it would work either way.

U have Error in code
PropertyChangedEventArgs need String (Name of property)
so "Wages"
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Wages"))

You should provide name of the property to the PropertyChangedEventArgs
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Nameof(Wages))
You did not show, but obviously when you "load" file and deserialize class you create new instance of Map and set it to mymap. Where control's binding will still refer to the original instance and listen it's events.
Create "wrapper" viewmodel with property of type Map and raise property change event when you load file.
Public Class ViewModel
Private _Map As Map
Property Map As Map
Get
Return _Map
End Get
Set(value As Map)
_Map = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Nameof(Wages))
End Set
End Property
End Class
Then in the form do binding
Dim viewmodel = new ViewModel()
viewmodel.Map = DeserializeMap() ' Your deserialize logic
txtWages.DataBindings.Add("EditValue",
viewmodel,
"Map.Wages",
False,
DataSourceUpdateMode.OnPropertyChanged)

Related

Winform TextBox databind to Listbox using INotifyPropertyChanged

I have set up Entity Framework with my VB.NET project. I have a model class that takes the data from a table from my MS SQL Server DB. My ListBox object is filled from the model class. What I am trying to do is when a user clicks on an item on from the listbox the text box is populated with the data from the table. If the user clicks on a mustang the text boxes are filled with the model, make, and year of a mustang.
I am using INotifyPropertyChanged in a viewmodel class that I thought would allow me to get each part of the dataset.
This code shows only one part of the requirements to better simplify the post
Public Class CarViewModel
Implements INotifyPropertyChanged
Private _SelectedCarModelName As CarModel
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property SelectedCarModelName () As CarModel
Get
Return _SelectedCarModelName
End Get
Set(ByVal value As CarModel)
_SelectedCarModelName = value
OnPropertyChanged("CarName")
End Set
End Property
Public Overridable Sub OnPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
My Textbox Data bind looks like this. My Textbox resides on the frm.vb.
txtCarName.DataBindings.Add("Text", _carViewModel.SelectedCarModelName.CarName, "CarName", True, DataSourceUpdateMode.OnPropertyChanged)
When I click on an item on the list it will access the property, but it will not set the property change. I know I am doing something wrong but I just don't know what it is.
******UPDATED*******
ViewModel
Public Class CarViewModel
Implements INotifyPropertyChanged
Private _SelectedCarModelName As CarModel
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property SelectedCarModelName As CarModel
Get
Return _SelectedCarModelName
End Get
Set(ByVal value As CarModel)
_SelectedCarModelName= value
OnPropertyChanged("SelectedCarModelName ")
End Set
End Property
Public Sub OnPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Windows Form TextBox
**************Edited*******************
private _CarViewModel As CarViewModel
Dim bs As New BindingSource(_CarViewModel , Nothing)
txtCarName.DataBindings.Add _
("Text", bs, "SelectedCarModelName", True, DataSourceUpdateMode.OnPropertyChanged, String.Empty)
After I have restarted everything, for some reason my machine likes that, I am not getting an error anymore. When I debug the code is iterating through the Getter 3x, the number of fields. but it is not getting an output for the textbox.

Binding a VB.NET label.text to an object property

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.

Binding control property to user control property

I have a user control that has some public properties (like Dirty :boolean) and an event (ControlValueChanged) that change that property.
I added that control to a form. In the form I have a button (btnOK) and I want to bind the property Enabled of the button to the Dirty property.
I read http://msdn.microsoft.com/en-us/library/ms229614.aspx but I face some problems to implement this to my project.
My code in the form:
btnOK.DataBindings.Add("Enabled", Me.wwdp, "Dirty") 'wwdp is my user Control
So from my research I have to add in my custom control:
Imports System.ComponentModel
Public Class wwDynamicPanel
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Public Property Dirty As Boolean
Get
Return mbDirty
End Get
Set(ByVal value As Boolean)
mbDirty = value
NotifyPropertyChanged()
End Set
End Property
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
I get an error : Type 'CallerMemberName' is not defined.
The thing is that I haven't found in msdn anything more.
I am very sorry. The link in MSDN was for framework 4.5
I found the right http://msdn.microsoft.com/en-us/library/ms184414(v=vs.100).aspx. for my framework
and I solved the problem.
I am leaving the question because someone else find it useful.
So the working code is:
Imports System.ComponentModel
Public Class wwDynamicPanel
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Public Property Dirty As Boolean
Get
Return mbDirty
End Get
Set(ByVal value As Boolean)
mbDirty = value
NotifyPropertyChanged("Dirty")
End Set
End Property
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub

Data annotation validation error message show up right away. EF 4.2/MVC3

I'm having an issue with my required data annotations messages showing up right away in my edit form even though there is data in the controls. If I click save everything works fine and if I give focus to the controls that have the error displayed and then change focus to another control the error message goes away... This seems only to be the issue in my edit form, create doesn't have the same issue.
I'm using EF 4.2 with the dbcontext generator and using the validation data annotations via a metadata class. My class points to the metadata class using the metadataType attribute. class below:
<MetadataType(GetType(PersonsMetaData))>
Partial Public Class Persons
End Class
Public Class PersonsMetaData
Private mPersonFirstName As Object
Private mPersonLastName As Object
Private mPersonPrimaryEmail As Object
<Required()> _
Public Property PersonFirstName As Object
Get
Return mPersonFirstName
End Get
Set(value As Object)
mPersonFirstName = value
End Set
End Property
<Required()> _
Public Property PersonLastName As Object
Get
Return mPersonLastName
End Get
Set(value As Object)
mPersonLastName = value
End Set
End Property
<Required()> _
Public Property PersonPrimaryEmail As Object
Get
Return mPersonPrimaryEmail
End Get
Set(value As Object)
mPersonPrimaryEmail = value
End Set
End Property
End Class
My edit view has be generated using the mvc 3 framework.
Any advice would be appreciated

Custom CollectionEditor never triggers a property's "set" method

I am trying to implement a way of persisting a collection in a custom settings class. I have successfully created the settings class (inheriting ApplicationSettingsBase) and can save properties using the built-in editors on a PropertyGrid, but my custom implementation of a property grid for collections doesn't persist any of the values I add. Here's my code:
Imports System.Configuration
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.ComponentModel.Design
Public Class CustomSettings
Inherits ApplicationSettingsBase
<UserScopedSetting()> _
<DefaultSettingValue("White")> _
Public Property BackgroundColor() As Color
Get
BackgroundColor = Me("BackgroundColor")
End Get
Set(ByVal value As Color)
Me("BackgroundColor") = value
End Set
End Property
<UserScopedSetting()> _
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
<Editor(GetType(CustomStringCollectionEditor), GetType(UITypeEditor))> _
Public Property EmailAddresses() As Collection
Get
EmailAddresses = Me("EmailAddresses")
End Get
Set(ByVal value As Collection)
Me("EmailAddresses") = value
End Set
End Property
End Class
Public Class CustomStringCollectionEditor
Inherits CollectionEditor
Public Sub New()
MyBase.New(GetType(Collection))
End Sub
Protected Overrides Function CreateInstance(ByVal itemType As System.Type) As Object
Return String.Empty
End Function
Protected Overrides Function CreateCollectionItemType() As System.Type
Return GetType(String)
End Function
End Class
I set a breakpoint on the Set methods for both the BackgroundColor property and the EmailAddresses property. The BackgroundColor property works as it should - it breaks on the Set statement and stores the property correctly. But when I close the custom CollectionEditor dialog, the EmailAddresses "Set" method is never called. How can I get my custom editor to actually save the property once it's done being edited?
I think I fixed it (with help from this question). I added an override to the EditValue function in my custom editor. Here is the code:
Public Overrides Function EditValue(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
Dim result As Object = MyBase.EditValue(context, provider, value)
DirectCast(context.Instance, CustomSettings).EmailAddresses = DirectCast(result, List(Of String))
Return result
End Function
I also moved from a collection to a list - I read somewhere that was a safer way to go. I also added a constructor to my CustomSettings class that set the EmailAddresses property to a new List(Of String) if it was unset to begin with. I found that the first time it ran, I could edit the list and add items, but they wouldn't be persisted:
Public Sub New()
If Me("EmailAddresses") Is Nothing Then
Me("EmailAddresses") = New List(Of String)
End If
End Sub
And now it's all working like it should. But if this isn't the best way or there's an easier way to do it, please chime in.