我有一个标签可以数据绑定到Class中的Property,它返回另一个类中的最大数字。这些数字从6个文本字段填充到另一个类中。
当使用参数“BestThrow.Distance”修改时,数字的6个属性都会引发PropertyChanged事件,如下所示:
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.Length > 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
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("BestThrow.Distance"))
End Set
End Property
标签所绑定的属性如下:
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
Return bt
End Get
End Property
为了提供所有信息,标签的数据绑定如下
best.DataBindings.Add(New Binding("text", athlete, "BestThrow.Distance", False, DataSourceUpdateMode.OnPropertyChanged))
当我添加第一个距离时,标签会更新,并按预期显示距离(0)。距离,但是当我更改其他5个值时,标签将不会更新。但是,如果我再次修改第一个值,它将重新计算BestThrow并显示正确的距离。
我还尝试使用表单上的按钮手动提升事件,该按钮也无效。
从使用观察值我可以看到,当请求BestThrow属性吐出正确的值时,看起来标签只对第一次投掷的事件感兴趣。
提前感谢你的帮助。
当前代码:
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
Public Sub New()
Throws = New List(Of [Throw])()
End Sub
Public Property athletenum() As Integer
Get
Return m_athnum
End Get
Set(value As Integer)
m_athnum = value
End Set
End Property
Private m_Athnum As Integer
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 String
Get
Dim list As IList(Of [Throw]) = (From t As [Throw] In Throws Select t Where t.Status = ThrowStatus.Valid Order By t.Distance Descending).ToList()
If (list.Count > 0) Then
If (IsNothing(list(0).Distance)) Then
Return "0"
Else
Return list(0).Distance
End If
End If
Return "0"
End Get
End Property
Public ReadOnly Property getLabel()
Get
Return compNumber & " " & LastName & ", " & FirstName & vbCrLf & club
End Get
End Property
Public Property currentPlace
Get
Return m_curplace
End Get
Set(value)
m_curplace = value
End Set
End Property
Private m_curplace As Integer
End Class
Public Enum ThrowStatus
Valid
Pass
Foul
End Enum
Public Class [Throw]
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
'Throw Status
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
'Throw Distance
Public Property Distance() As String
Get
Dim value As String = Me.m_Distance
If Status = ThrowStatus.Valid Then
If (m_Distance > 0) Then
value = m_Distance
Else
value = Nothing
End If
ElseIf Status = ThrowStatus.Pass Then
value = "PASS"
ElseIf Status = ThrowStatus.Foul Then
value = "FOUL"
Else
value = Nothing
End If
If (value Me.m_Distance) Then
Me.Distance = value
End If
Return value
End Get
Set(value As String)
If (value.Length > 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
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("BestThrow"))
End Set
End Property
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
当前绑定:
best.DataBindings.Add(New Binding("text", athlete, "BestThrow", False, DataSourceUpdateMode.OnPropertyChanged))
答案 0 :(得分:0)
首先,我不喜欢看到计算返回值的属性。我认为这应该在一个方法中完成。 (在只读属性中可能没问题。)只是我的意见。我觉得很奇怪的是Distance
的吸气剂取决于Status
,并且设定器更像是Status
的设定器。
您的绑定需要PropertyDescriptor
名为Distance
。您返回String
,如您所知,字符串类没有名为Distance
的属性。
best.DataBindings.Add(New Binding("text", athlete, "BestThrow.Distance", False, DataSourceUpdateMode.OnPropertyChanged))
选项1
为此,你需要这样做:
Public ReadOnly Property BestThrow() As TYPE_OF_YOUR_CLASS
Get
'IMPORTANT:
'This approach requires at least ONE matching object and
'ONE item returned. If not, the binding will fail.
Return (From t As TYPE_OF_YOUR_CLASS In throws Select t Where t.Status = ThrowStatus.Valid Order By t.Distance Ascending).First()
End Get
End Property
选项2
另一种选择是绑定到BestThrow
属性:
best.DataBindings.Add(New Binding("text", athlete, "BestThrow", False, DataSourceUpdateMode.OnPropertyChanged))
将您的财产更改为:
Public ReadOnly Property BestThrow() As String
Get
'NOTE:
'Do not use First() as this will fail if no matching items are found.
'We use ToList() and check the count property.
Dim list As IList(Of TYPE_OF_YOUR_CLASS) = (From t As TYPE_OF_YOUR_CLASS In throws Select t Where t.Status = ThrowStatus.Valid Order By t.Distance Ascending).ToList()
If (list.Count > 0) Then
Return list(0).Distance
End If
Return "0"
End Get
End Property
修改强>
老实说,我认为这是糟糕的设计。请详细了解这种方法:
Public Class Athlete
'TODO: Implements INotifyPropertyChanged
Public Sub New(athleteNumber As Integer, Optional firstName As String = Nothing, Optional ByVal lastName As String = Nothing, Optional ByVal club As String = Nothing)
Me.m_athleteNumber = athleteNumber
Me.m_firstName = If((firstName Is Nothing), String.Empty, firstName)
Me.m_lastName = If((lastName Is Nothing), String.Empty, lastName)
Me.m_club = If((club Is Nothing), String.Empty, club)
End Sub
Public ReadOnly Property AthleteNumber() As Integer
Get
Return Me.m_athleteNumber
End Get
End Property
Public Property Club() As String
Get
Return Me.m_club
End Get
Set(value As String)
Me.m_club = value
'TODO: If changed raise property changed.
End Set
End Property
Public Property FirstName() As String
Get
Return Me.m_firstName
End Get
Set(value As String)
Me.m_firstName = value
'TODO: If changed raise property changed.
End Set
End Property
Public Property LastName() As String
Get
Return Me.m_lastName
End Get
Set(value As String)
Me.m_lastName = value
'TODO: If changed raise property changed.
End Set
End Property
Public Overrides Function ToString() As String
Return String.Concat(Me.m_firstName, " ", Me.m_lastName).Trim()
End Function
Private m_athleteNumber As Integer
Private m_club As String
Private m_firstName As String
Private m_lastName As String
End Class
Public Class Competitor
Implements INotifyPropertyChanged
Public Sub New(athlete As Athlete, Optional competitorNumber As String = Nothing)
If (athlete Is Nothing) Then
Throw New ArgumentNullException("athlete")
End If
Me.m_athlete = athlete
Me.m_bestThrow = 0D
Me.m_competitorNumber = If((competitorNumber Is Nothing), String.Empty, competitorNumber)
Me.m_curplace = 0
Me.m_throws = New ObjectCollection(Of [Throw])
AddHandler Me.m_throws.CollectionChanged, New CollectionChangeEventHandler(AddressOf Me.OnThrowCollectionChanged)
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public ReadOnly Property Athlete() As Athlete
Get
Return Me.m_athlete
End Get
End Property
Public ReadOnly Property BestThrow() As Decimal
Get
Return Me.m_bestThrow
End Get
End Property
Public Property CompetitorNumber() As String
Get
Return Me.m_competitorNumber
End Get
Set(value As String)
If (value <> Me.m_competitorNumber) Then
Me.m_competitorNumber = value
Me.RaisePropertyChanged("CompetitorNumber")
End If
End Set
End Property
Public Property CurrentPlace() As Integer
Get
Return Me.m_curplace
End Get
Set(value As Integer)
If (value <> Me.m_curplace) Then
Me.m_curplace = value
Me.RaisePropertyChanged("CurrentPlace")
End If
End Set
End Property
Public ReadOnly Property Throws() As ObjectCollection(Of [Throw])
Get
Return Me.m_throws
End Get
End Property
Protected Sub OnThrowCollectionChanged(sender As Object, e As CollectionChangeEventArgs)
Dim list As IList(Of [Throw]) = (From t As [Throw] In Me.m_throws Select t Where t.Status = ThrowStatus.Valid Order By t.Distance Descending).ToList()
Dim bestThrow As Decimal = If((list.Count > 0), list(0).Distance, 0D)
If (Me.m_bestThrow <> bestThrow) Then
Me.m_bestThrow = bestThrow
Me.RaisePropertyChanged("BestThrow")
End If
End Sub
Private Sub RaisePropertyChanged(propertyName As String)
If (Not Me.PropertyChangedEvent Is Nothing) Then
Me.PropertyChangedEvent.Invoke(Me, New PropertyChangedEventArgs(propertyName))
End If
End Sub
Private m_athlete As Athlete
Private m_bestThrow As Decimal
Private m_competitorNumber As String
Private m_curplace As Integer
Private m_throws As ObjectCollection(Of [Throw])
End Class
Public Class [Throw]
Public Sub New(Optional ByVal status As ThrowStatus = ThrowStatus.Valid, Optional distance As Decimal = 0D)
Me.m_status = status
Me.m_distance = Math.Max(distance, 0D)
End Sub
Public ReadOnly Property Status() As ThrowStatus
Get
Return Me.m_status
End Get
End Property
Public ReadOnly Property Distance() As Decimal
Get
Return Me.m_distance
End Get
End Property
Public Overrides Function ToString() As String
Select Case Me.m_status
Case ThrowStatus.Valid
Return Me.m_distance.ToString("N2")
Case ThrowStatus.Pass
Return "PASS"
Case ThrowStatus.Foul
Return "FOUL"
Case Else
Return 0D.ToString("N2")
End Select
End Function
Private m_attempt As Integer
Private m_distance As Decimal
Private m_status As ThrowStatus
End Class
Public Class ObjectCollection(Of T)
Inherits Collections.ObjectModel.Collection(Of T)
Public Event CollectionChanged As CollectionChangeEventHandler
Protected Overrides Sub ClearItems()
MyBase.ClearItems()
Me.RaiseCollectionChanged(CollectionChangeAction.Refresh, Nothing)
End Sub
Protected Overrides Sub InsertItem(index As Integer, item As T)
If (item Is Nothing) Then
Throw New ArgumentNullException("item")
ElseIf (Me.Contains(item)) Then
Throw New ArgumentException("Item duplicate.", "item")
End If
MyBase.InsertItem(index, item)
Me.RaiseCollectionChanged(CollectionChangeAction.Add, item)
End Sub
Protected Overridable Sub OnCollectionChanged(e As CollectionChangeEventArgs)
If (Not Me.CollectionChangedEvent Is Nothing) Then
Me.CollectionChangedEvent.Invoke(Me, e)
End If
End Sub
Private Sub RaiseCollectionChanged(action As CollectionChangeAction, item As T)
Me.OnCollectionChanged(New CollectionChangeEventArgs(action, item))
End Sub
Protected Overrides Sub RemoveItem(index As Integer)
Dim item As T = Me.Item(index)
MyBase.RemoveItem(index)
Me.RaiseCollectionChanged(CollectionChangeAction.Remove, item)
End Sub
Protected Overrides Sub SetItem(index As Integer, item As T)
Throw New NotSupportedException()
End Sub
End Class
Public Enum ThrowStatus As Integer
Valid = 0 '<- This is the default value.
Pass = 1
Foul = 2
End Enum
以下代码经过测试并可以使用。将Button
和TextBox
放到Form
上并附加:
Public Class Form1
Public Sub New()
Me.InitializeComponent()
Me.competitor = New Competitor(New Athlete(1, "John", "Smith", "Team JS"), "CP_1")
Me.competitor.Throws.Add(New [Throw](distance:=123.3472D))
Me.competitor.Throws.Add(New [Throw](distance:=424.1234D))
Me.competitor.Throws.Add(New [Throw](distance:=242.1234D))
Dim b As New Binding("Text", Me.competitor, "BestThrow", True, DataSourceUpdateMode.Never)
AddHandler b.Format, New ConvertEventHandler(AddressOf Me._FormatBinding)
Me.TextBox1.DataBindings.Add(b)
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Me.competitor.Throws.Add(New [Throw](distance:=845.4365D))
End Sub
Private Sub _FormatBinding(sender As Object, e As ConvertEventArgs)
If ((TypeOf e.Value Is Decimal) AndAlso (e.DesiredType Is GetType(String))) Then
e.Value = CDec(e.Value).ToString("N2")
End If
End Sub
Private competitor As Competitor
End Class