Linq to SQL to ObservableCollection for treeview in VB.Net - vb.net

I have some Linq to SQL table classes that are joined together. I currently have it bound to a treeview using just the LINQ to SQL query. It works, but it doesn't show when stuff is added or removed from the database.
I implemented INotifyPropertyChanged but it isn't updating the treeview.
I also tried using Bindable Linq, but it doesn't seem to make a difference.
I found an example of a way to easily create ObservableCollections without having to create more classes: jimlynn.wordpress.com/2008/12/09/using-observablecollection-with-linq/, (which is kind of important because I have a future project looming that will require interacting with a lot of tables (30 or so) and just creating the Linq to SQL classes is going to be a pain).
Property ModelQuery As ObservableCollection(Of dbModels) = New ObservableCollection(Of dbModels)().PopulateFrom(From mm In tblModels.AsBindable Order By mm.ModelName)
Is this a good way to go, or am I going to have to create a separate ObservableCollection and maintain them both in code. If I'm going to use this binding stuff, I'm really looking for a way to have stuff just linked together so I don't have to update multiple structures whenever a change is made.
Main table:
<Table(Name:="Models")> Public Class dbModels
Implements INotifyPropertyChanged
Private _changed As Boolean
Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Protected Overridable Sub OnPropertyChanged(ByVal Propertyname As String)
If Not Propertyname.Contains("Changed") Then
Changed = True
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propertyname))
End Sub
Public Property Changed() As Boolean
Get
Return _changed
End Get
Set(ByVal value As Boolean)
If _changed <> value Then
_changed = value
OnPropertyChanged("Changed")
End If
End Set
End Property
Private _ModelID As Integer
<Column(Storage:="_ModelID", DbType:="int IDENTITY NOT NULL", IsPrimaryKey:=True, IsDbGenerated:=True, Name:="ModelID")> _
Public Property ModelID() As Integer
Get
Return Me._ModelID
End Get
Set(value As Integer)
Me._ModelID = value
End Set
End Property
Private _ModelName As String
<Column(Storage:="_ModelName", DbType:="Varchar(200)", Name:="ModelName")> _
Public Property ModelName() As String
Get
Return Me._ModelName
End Get
Set(value As String)
Me._ModelName = value
End Set
End Property
Private _ModelYears As EntitySet(Of dbModelYears) = New EntitySet(Of dbModelYears)
<Association(Storage:="_ModelYears", DeleteRule:="CASCADE", OtherKey:="ModelID")> _
Public Property ModelYears As EntitySet(Of dbModelYears)
Get
Return _ModelYears
End Get
Set(value As EntitySet(Of dbModelYears))
_ModelYears.Assign(value)
End Set
End Property
End Class
Joined table:
<Table(Name:="ModelYears")> Public Class dbModelYears
Implements INotifyPropertyChanged
Private _changed As Boolean
Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Protected Overridable Sub OnPropertyChanged(ByVal Propertyname As String)
If Not Propertyname.Contains("Changed") Then
Changed = True
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propertyname))
End Sub
Public Property Changed() As Boolean
Get
Return _changed
End Get
Set(ByVal value As Boolean)
If _changed <> value Then
_changed = value
OnPropertyChanged("Changed")
End If
End Set
End Property
Private _ModelYearID As Integer
<Column(Storage:="_ModelYearID", DbType:="int IDENTITY NOT NULL", IsPrimaryKey:=True, IsDbGenerated:=True, Name:="ModelYearID")> _
Public Property ModelYearID() As Integer
Get
Return Me._ModelYearID
End Get
Set(value As Integer)
Me._ModelYearID = value
End Set
End Property
Private _ModelID As Integer
<Column(Storage:="_ModelID", DbType:="int", Name:="ModelID")> _
Public Property ModelID() As Integer
Get
Return Me._ModelID
End Get
Set(value As Integer)
Me._ModelID = value
End Set
End Property
Private _ModelYear As String
<Column(Storage:="_ModelYear", DbType:="Varchar(50)", Name:="ModelYear")> _
Public Property ModelYear() As String
Get
Return Me._ModelYear
End Get
Set(value As String)
Me._ModelYear = value
End Set
End Property
End Class

Implementing INotifyPropertyChanged or using ObservableCollection wont notify you of changes in the database. You are going to have to execute the query every time you want to retrieve new data from the database.
Alternatively you could possibly use SqlDependency to set up a query notification dependency between your application and an instance of SQL Server.
EDIT:
From comments.
To hook up the PropertyChanged event of the items in the collection.
For Each item As var In itemsCollection
Dim notifyItem = TryCast(item, INotifyPropertyChanged)
If item IsNot Nothing Then
AddHandler notifyItem.PropertyChanged, AddressOf ItemChanged
End If
Next
Public Sub ItemChanged(sender As Object, e As PropertyChangedEventArgs)
'Handle event
End Sub

Related

How to assign typical NumericUpDown properties to custom NumericUpDown based control?

I'm trying to make a DataGridViewColumn that inherits all of the properties of a typical NumericUpDown control. I found an answer here on StackOverflow (https://stackoverflow.com/a/55788490/692250) and a MSDN article here (https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-host-controls-in-windows-forms-datagridview-cells?view=netframeworkdesktop-4.8&redirectedfrom=MSDN#code-snippet-1).
The code from the article works well, but... I cannot assign a Maximum nor Minimum value to the control (both are stuck at the "default" Minimum of 0 and Maximum of 100).
I tried to add in code from the SO answer (by adding in a Min and Max field) but doing so locks both in to 0 and unable to change.
The question in short: how do I add the necessary properties to allow to this work.
The custom control is question:
Public Class NumericUpDownColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New NumericUpDownCell())
End Sub
Public Overrides Property CellTemplate() As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set(ByVal value As DataGridViewCell)
' Ensure that the cell used for the template is a CalendarCell.
If Not (value Is Nothing) AndAlso Not value.GetType().IsAssignableFrom(GetType(NumericUpDownCell)) Then
Throw New InvalidCastException("Must be an Integer")
End If
MyBase.CellTemplate = value
End Set
End Property
End Class
Public Class NumericUpDownCell
Inherits DataGridViewTextBoxCell
Public Sub New()
' Number Format
Me.Style.Format = "0"
End Sub
Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, ByVal initialFormattedValue As Object, ByVal dataGridViewCellStyle As DataGridViewCellStyle)
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctl As NumericUpDownEditingControl = CType(DataGridView.EditingControl, NumericUpDownEditingControl)
ctl.Value = CType(Me.Value, Decimal)
End Sub
Public Overrides ReadOnly Property EditType() As Type
Get
' Return the type of the editing contol that CalendarCell uses.
Return GetType(NumericUpDownEditingControl)
End Get
End Property
Public Overrides ReadOnly Property ValueType() As Type
Get
' Return the type of the value that CalendarCell contains.
Return GetType(Decimal)
End Get
End Property
Public Overrides ReadOnly Property DefaultNewRowValue() As Object
Get
' Use the current date and time as the default value.
Return 0
End Get
End Property
End Class
Class NumericUpDownEditingControl
Inherits NumericUpDown
Implements IDataGridViewEditingControl
Private dataGridViewControl As DataGridView
Private valueIsChanged As Boolean = False
Private rowIndexNum As Integer
Public Sub New()
Me.DecimalPlaces = 0
End Sub
Public Property EditingControlFormattedValue() As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return Me.Value.ToString("#")
End Get
Set(ByVal value As Object)
If TypeOf value Is Decimal Then
Me.Value = Decimal.Parse(value.ToString)
End If
End Set
End Property
Public Function GetEditingControlFormattedValue(ByVal context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
Return Me.Value.ToString("#")
End Function
Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
Me.ForeColor = dataGridViewCellStyle.ForeColor
Me.BackColor = dataGridViewCellStyle.BackColor
End Sub
Public Property EditingControlRowIndex() As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex
Get
Return rowIndexNum
End Get
Set(ByVal value As Integer)
rowIndexNum = value
End Set
End Property
Public Function EditingControlWantsInputKey(ByVal key As Keys, ByVal dataGridViewWantsInputKey As Boolean) As Boolean Implements IDataGridViewEditingControl.EditingControlWantsInputKey
' Let the DateTimePicker handle the keys listed.
Select Case key And Keys.KeyCode
Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
Return True
Case Else
Return False
End Select
End Function
Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
' No preparation needs to be done.
End Sub
Public ReadOnly Property RepositionEditingControlOnValueChange() As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property
Public Property EditingControlDataGridView() As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView
Get
Return dataGridViewControl
End Get
Set(ByVal value As DataGridView)
dataGridViewControl = value
End Set
End Property
Public Property EditingControlValueChanged() As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
Get
Return valueIsChanged
End Get
Set(ByVal value As Boolean)
valueIsChanged = value
End Set
End Property
Public ReadOnly Property EditingControlCursor() As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property
Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)
' Notify the DataGridView that the contents of the cell have changed.
valueIsChanged = True
Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
MyBase.OnValueChanged(eventargs)
End Sub
End Class
Focusing on it a bit more, I found that I can assign values to the Maximum and Minimum fields as so:
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctl As NumericUpDownEditingControl = CType(DataGridView.EditingControl, NumericUpDownEditingControl)
ctl.Value = CType(Me.Value, Decimal)
ctl.Maximum = 180
ctl.Minimum = -1
End Sub
But I cannot seem to pass to this field via the usual means of .Maximum or .Minimum on the constructor. Any tips/advice?
This is an easy fix and very easy to overlook.
In your NumericUpDownCell class you need to override the Clone method to include the new Minimum and Maximum properties, for example:
First, add the new properties the NumericUpDownCell class and then override the Clone function
Public Class NumericUpDownCell
Inherits DataGridViewTextBoxCell
...
Public Property Minimum() As Integer
Public Property Maximum() As Integer
...
Public Overrides Function Clone() As Object
Dim retVal As NumericUpDownCell = CType(MyBase.Clone, NumericUpDownCell)
retVal.Minimum = Me.Minimum
retVal.Maximum = Me.Maximum
Return retVal
End Function
End Class
Second, inside the NumericUpDownCell classes InitializeEditingControl method, add the two lines:
ctl.Minimum = Me.Minimum
ctl.Maximum = Me.Maximum
When you setup the new column, get the CellTemplate to set the new properties, as per:
Dim upDownColumn As New NumericUpDownColumn
Dim cellTemplate As NumericUpDownCell = CType(upDownColumn .CellTemplate, NumericUpDownCell)
cellTemplate.Minimum = 10
cellTemplate.Maximum = 15
Or, following you preference to setup via the constructor, add a new constructor to the NumericUpdDownColumn class as per
Public Sub New(minValue As Integer, maxValue As Integer)
MyBase.New(New NumericUpDownCell())
Dim template As NumericUpDownCell = CType(CellTemplate, NumericUpDownCell)
template.Minimum = minValue
template.Maximum = maxValue
End Sub
and then use it like:
Dim upDownColumn As New NumericUpDownColumn(100, 150)
Dim cellTemplate As NumericUpDownCell = CType(upDownColumn.CellTemplate, NumericUpDownCell)
DataGridView1.Columns.Add(upDownColumn)

VB ToolStripControlHost remove Text property

I have a class that allows ToolStrip to have a NumericUpDown. I added a properties (Value, Minimum and Maximum). It is working but I want to remove the Text property since I added the Value property. Is there a way to do this?
Please see image where you can see the designer properties
Below is the class
<Windows.Forms.Design.ToolStripItemDesignerAvailabilityAttribute(Windows.Forms.Design.ToolStripItemDesignerAvailability.ToolStrip), DebuggerStepThrough()>
Public Class ToolStripNumericUpDown
Inherits Windows.Forms.ToolStripControlHost
Public Sub New()
MyBase.New(New System.Windows.Forms.NumericUpDown())
End Sub
Public ReadOnly Property ToolStripNumericUpDownControl() As Windows.Forms.NumericUpDown
Get
Return TryCast(Control, Windows.Forms.NumericUpDown)
End Get
End Property
Public Property Value() As Long
Get
Return ToolStripNumericUpDownControl.Value
End Get
Set(ByVal value As Long)
ToolStripNumericUpDownControl.Value = value
End Set
End Property
Public Property Minimum() As Long
Get
Return ToolStripNumericUpDownControl.Minimum
End Get
Set(ByVal value As Long)
ToolStripNumericUpDownControl.Minimum = value
End Set
End Property
Public Property Maximum() As Long
Get
Return ToolStripNumericUpDownControl.Maximum
End Get
Set(ByVal value As Long)
ToolStripNumericUpDownControl.Maximum = value
End Set
End Property
Protected Overrides Sub OnSubscribeControlEvents(ByVal c As Windows.Forms.Control)
MyBase.OnSubscribeControlEvents(c)
AddHandler DirectCast(c, Windows.Forms.NumericUpDown).ValueChanged, AddressOf OnValueChanged
End Sub
Protected Overrides Sub OnUnsubscribeControlEvents(ByVal c As Windows.Forms.Control)
MyBase.OnUnsubscribeControlEvents(c)
RemoveHandler DirectCast(c, Windows.Forms.NumericUpDown).ValueChanged, AddressOf OnValueChanged
End Sub
Public Event ValueChanged As EventHandler
Private Sub OnValueChanged(ByVal sender As Object, ByVal e As EventArgs)
RaiseEvent ValueChanged(Me, e)
End Sub
End Class

SubmitChanges does not update foreign key to the database

My problem occurs in a Windows Phone 8.1 Silverlight VB application. The CatDataContext defines a table Books with items Title and a foreign key _seriesID, with belongs to a table Series.
<Table()>
Public Class Series
Implements INotifyPropertyChanged, INotifyPropertyChanging
' Define ID: private field, public property, and database column.
Private _seriesID As Integer
<Column(IsPrimaryKey:=True, IsDbGenerated:=True, DbType:="INT NOT NULL Identity", CanBeNull:=False,
AutoSync:=AutoSync.OnInsert)>
Public Property SeriesID() As Integer
Get
Return _seriesID
End Get
Set(ByVal value As Integer)
If _seriesID <> value Then
NotifyPropertyChanging("SeriesID")
_seriesID = value
NotifyPropertyChanged("SeriesID")
End If
End Set
End Property
' Define name: private field, public property, and database column.
Private _Name As String
<Column()>
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
If _Name <> value Then
NotifyPropertyChanging("Name")
_Name = value
NotifyPropertyChanged("Name")
End If
End Set
End Property
#Region "INotifyPropertyChanged Members"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
' Used to notify that a property changed
Private Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
#End Region
#Region "INotifyPropertyChanging Members"
Public Event PropertyChanging As PropertyChangingEventHandler Implements INotifyPropertyChanging.PropertyChanging
' Used to notify that a property is about to change
Private Sub NotifyPropertyChanging(ByVal propertyName As String)
RaiseEvent PropertyChanging(Me, New PropertyChangingEventArgs(propertyName))
End Sub
#End Region
End Class
<Table()>
Public Class Book
Implements INotifyPropertyChanged, INotifyPropertyChanging
' Define ID: private field, public property, and database column.
Private _bookID As Integer
<Column(IsPrimaryKey:=True, IsDbGenerated:=True, DbType:="INT NOT NULL Identity", CanBeNull:=False,
AutoSync:=AutoSync.OnInsert)>
Public Property BookID() As Integer
Get
Return _bookID
End Get
Set(ByVal value As Integer)
If _bookID <> value Then
NotifyPropertyChanging("BookID")
_bookID = value
NotifyPropertyChanged("BookID")
End If
End Set
End Property
' Define title: private field, public property, and database column.
Private _title As String
<Column()>
Public Property Title() As String
Get
Return _title
End Get
Set(ByVal value As String)
If _title <> value Then
NotifyPropertyChanging("Title")
_title = value
NotifyPropertyChanged("Title")
End If
End Set
End Property
' Internal column for the associated series ID value.
<Column()>
Friend _seriesID As Integer
Private _series As EntityRef(Of Series)
<Association(Storage:="_series", ThisKey:="_seriesID", OtherKey:="SeriesID")>
Public Property BookSeries() As Series
Get
Return _series.Entity
End Get
Set(ByVal value As Series)
NotifyPropertyChanging("BookSeries")
_series.Entity = value
If value IsNot Nothing Then
_seriesID = value.SeriesID
End If
NotifyPropertyChanged("BookSeries")
End Set
End Property
#Region "INotifyPropertyChanged Members"
Public Event PropertyChanged As PropertyChangedEventHandler Implements NotifyPropertyChanged.PropertyChanged
' Used to notify that a property changed
Private Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
#End Region
#Region "INotifyPropertyChanging Members"
Public Event PropertyChanging As PropertyChangingEventHandler Implements INotifyPropertyChanging.PropertyChanging
' Used to notify that a property is about to change
Private Sub NotifyPropertyChanging(ByVal propertyName As String)
RaiseEvent PropertyChanging(Me, New PropertyChangingEventArgs(propertyName))
End Sub
#End Region
Updating of the field Title alone, or the fields Title and _seriesID works fine. However when I only change the _seriesID then no update of the underlying database is done. In this case .GetModifiedMembers shows no modifications.
A reference to a demo project showing this problem is given here: demo project.
Thanks for your attention.
_seriesID (in Book) is just a member variable. You can set it from outside Book because it is Friend, but further nothing happens.
Title, on the other hand, is a property that fires NotifyPropertyChanged. That means that the context is notified that the Book object has been modified if you change Title.
So if you change _seriesID and Title, the Book object is marked as modified and saved, along with the changed value of _seriesID. But if you change _seriesID alone, the object remains 'unchanged'.
I think this is generated code (LINQ-to-SQL? I don't really recognize it) and you shouldn't modify it manually. If you want _seriesID to be changed, you have to set BookSeries.

Databinding Not updating

I have been trying to get a label to databind to a readonly property. I have a much more complex project which I am implementing this in and it isn't working. I have been unsuccessful in getting help with this so I have created a much simpler version of the project and my databinding still isn't updating.
To replicate my issue you will need a form with a textbox, label and button, and then a class.
The code for the class is as follows
Imports System.ComponentModel
Public Class databinding
Implements INotifyPropertyChanged
Public Sub New()
numbers = New List(Of number)
End Sub
Public Property numbers As List(Of number)
Get
Return m_number
End Get
Set(value As List(Of number))
m_number = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("hnumber"))
End Set
End Property
Private m_number As List(Of number)
Public ReadOnly Property hnumber As Integer
Get
Dim list As IList(Of number) = (From t As number In numbers Select t Order By t.value Descending).ToList()
If (list.Count > 0) Then
If (IsNothing(list(0).value)) Then
Return "0"
Else
Return list(0).value
End If
End If
Return "0"
End Get
End Property
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class
Public Class number
Public Property value As Integer
Get
Return t_number
End Get
Set(value As Integer)
t_number = value
End Set
End Property
Private t_number As Integer
End Class
The code for the form is as follows:
Public Class Form1
Public numberlist As New databinding
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.DataBindings.Add(New Binding("text", numberlist, "hnumber"))
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim newnum As New number
newnum.value = TextBox1.Text
numberlist.numbers.Add(newnum)
End Sub
End Class
Now based on my understanding when you click the button a number from the textbox is added to this list, which happens, and the hnumber value updates, which using a breakpoint and a watch I can see also happens. From reading I need to implement inotifypropertychanged when I set the new number to get the label to re check the databind (which has been done).
However the label will stay at 0. If I run watch through Label1 I can see that under DataBindings > List > arrayList > (0) > System.Windows.Forms.Binding>DataSource>Databinding_test.databinding the details of the class (including the correct value for hnumber) is listed, so to me that shows that the Label does in fact know about the value it should be binding to.
Could someone please fill me in on what I am missing to make this all work, as it is almost causing me to pull out all of my hair.
Thanks,
mtg
I've tried to explain this to you before, and I will again.
The reason why your binding isn't updated is because you're adding the value to a list.
numberlist.numbers.Add(newnum)
However, if you "change" the list, this will trigger the propertychanged event.
numberlist.numbers.Add(newnum)
numberlist.numbers = numberlist.numbers '<--
Instead of using an IList<T> you should use the ObservableCollection<T> which allows you to track the changes made.
Public Class databinding
Implements INotifyPropertyChanged
Public Sub New()
Me.numbers = New ObservableCollection(Of number)
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property numbers As ObservableCollection(Of number)
Get
Return m_number
End Get
Set(value As ObservableCollection(Of number))
If (Not m_number Is value) Then
Unhook(m_number)
Hook(value)
m_number = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("hnumber"))
End If
End Set
End Property
Public ReadOnly Property hnumber As Integer
Get
If (Not numbers Is Nothing) Then
Dim list As IList(Of number) = (From t As number In numbers Select t Order By t.value Descending).ToList()
If (list.Count > 0) Then
If (IsNothing(list(0).value)) Then
Return 0
Else
Return list(0).value
End If
End If
End If
Return 0
End Get
End Property
Private Sub Hook(collection As ObservableCollection(Of number))
If (Not collection Is Nothing) Then
AddHandler collection.CollectionChanged, AddressOf Me.OnNumbersChanged
End If
End Sub
Private Sub OnNumbersChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("hnumber"))
End Sub
Private Sub Unhook(collection As ObservableCollection(Of number))
If (Not collection Is Nothing) Then
RemoveHandler collection.CollectionChanged, AddressOf Me.OnNumbersChanged
End If
End Sub
Private m_number As ObservableCollection(Of number)
End Class

VB.NET Databound Label not updating

I am having issues with a label with a databound text value updating. I have a Class that calculates the best throw of an athlete and this is bound to a label in my form. The class is as follows
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Public Class Competition
Public Sub New()
Competitors = New List(Of Competitor)()
End Sub
Public Property Competitors() As List(Of Competitor)
Get
Return m_Competitors
End Get
Set(value As List(Of Competitor))
m_Competitors = value
End Set
End Property
Private m_Competitors As List(Of Competitor)
Public ReadOnly Property CurrentPlacings() As List(Of Competitor)
Get
Return Competitors.OrderByDescending(Function(c) c.BestThrow).ToList()
End Get
End Property
End Class
Public Class Competitor
Implements System.ComponentModel.INotifyPropertyChanged
Public Event PropertyChanged(sender As Object,
e As System.ComponentModel.PropertyChangedEventArgs) _
Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Public Sub New()
Throws = New List(Of [Throw])()
End Sub
Public Property FirstName() As String
Get
Return m_FirstName
End Get
Set(value As String)
m_FirstName = value
End Set
End Property
Private m_FirstName As String
Public Property LastName() As String
Get
Return m_LastName
End Get
Set(value As String)
m_LastName = value
End Set
End Property
Private m_LastName As String
Public Property compNumber() As String
Get
Return m_compNumb
End Get
Set(value As String)
m_compNumb = value
End Set
End Property
Private m_compNumb As String
Public Property club() As String
Get
Return m_club
End Get
Set(value As String)
m_club = value
End Set
End Property
Private m_club As String
Public Property Throws() As List(Of [Throw])
Get
Return m_Throws
End Get
Set(value As List(Of [Throw]))
m_Throws = value
End Set
End Property
Private m_Throws As List(Of [Throw])
Public ReadOnly Property BestThrow() As Object
Get
Dim bt = Throws.Where(Function(t) t.Status = ThrowStatus.Valid).OrderByDescending(Function(t) t.Distance).First()
If (IsNothing(bt.Distance)) Then
bt.Distance = "0"
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("BestThrow"))
Return bt
End Get
End Property
Public ReadOnly Property getLabel
Get
Return compNumber & " " & LastName & ", " & FirstName & vbCrLf & club
End Get
End Property
End Class
Public Enum ThrowStatus
Valid
Pass
Foul
End Enum
Public Class [Throw]
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property Status() As ThrowStatus
Get
Return m_Status
End Get
Set(value As ThrowStatus)
m_Status = value
End Set
End Property
Private m_Status As ThrowStatus
Public Property Distance() As String
Get
If Status = ThrowStatus.Valid Then
If (m_Distance > 0) Then
Return m_Distance
Else
Return Nothing
End If
ElseIf Status = ThrowStatus.Pass Then
Return "PASS"
ElseIf Status = ThrowStatus.Foul Then
Return "FOUL"
Else
Return Nothing
End If
End Get
Set(value As String)
If (value > 0) Then
If (IsNumeric(value)) Then
m_Distance = value
Status = ThrowStatus.Valid
ElseIf (value = "foul") Then
Status = ThrowStatus.Foul
ElseIf (value = "pass") Then
Status = ThrowStatus.Pass
Else
Status = ThrowStatus.Valid
m_Distance = Nothing
End If
Else
m_Distance = Nothing
Status = ThrowStatus.Valid
End If
OnPropertyChanged("Distance")
End Set
End Property
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
Private m_Distance As Decimal
Public Property attempt() As Integer
Get
Return m_attempt
End Get
Set(value As Integer)
m_attempt = value
End Set
End Property
Private m_attempt As Integer
End Class
The line that databinds for the Label is as follows:
best.DataBindings.Add(New Binding("text", athlete.BestThrow, "distance", False, DataSourceUpdateMode.OnPropertyChanged))
I can tell that the Property BestThrow is defintely being updated using Watch however for some reason the label seems to only reflect Throw(0).Distance and not BestThrow.Distance
I can change Throw(0) and the label will change, if I add a bigger number to any of the other 5 attempts and Watch the value I can see that the BestThrow is being updated.
Thanks in advance for your assistance.
Mark
I think you have a wrong prespective, property BestThrow is never updated because it has no setter. When you check BestThrow value using watch, it 'recalculated' because you request the value. What you need is to find a way to raise Property Changed event for BestThrow whenever item added or removed from Throws because BestThrow value depends on Throws. After event raised, UI will call BestThrow's getter again looking for updated value.
'You need to execute this code to notify UI that `BestThrow` value has changed
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("BestThrow"))
Raisng the event can be done manually after code for adding/removing item to/from Throws property, or you can make Throws an ObservableCollection of Throw then listen to Collection Changed event. Then in the event handler you can raise BestThrow property changed event.