Search code examples
.netvb.netwinformsdata-bindingdevexpress-windows-ui

DevExpress GridControl Bound to Object with a BindingList isn't updating controls


I have a form with a few controls on it; some text boxes that are databound to a private form object's properties (Financial) and a DevExpress GridControl that is databound to a BindingList(of Fee) (Fees) property on the same Financial object. One of the properties on Financial is a readonly property that calculates some data based on other properties of the Financial and Fees (MonthlyCosts). Both Financial and Fee implement INotifyPropertyChanged.

The problem that I am having is that the textbox that is bound to that MonthlyCosts property does not update when changes are made to the GridControl. If I change the cost of a fee in the GridControl, then change a textbox value (Margin) that is also used in that calculation, the textbox with the calculated value will only update after I change the Margin.

Some of the related code is shown below:

Public Class Financial
    Inherits BindableBase ' helper for INotifyPropertyChanged

    Public Property Margin As Decimal
        Get
            return _margin
        End Get
        Set
            SetProperty() ' INotifyPropertyChanged stuff
        End Set
    End Property

    Public ReadOnly Property Fees As BindingList(Of Fee)

    Public ReadOnly Property Total as Decimal
        Get
            return Fees.Sum(Function(fee) fee.Amount) / (1 - Margin)
        End Get
    End Property
End Class

Public Class Fee
    Inherits BindableBase ' helper for INotifyPropertyChanged

    Public Property Amount as Decimal
End Class

In the form:

' Setup the databindings
Margin.DataBindings.Add("EditValue", Financial, NameOf(Financial.Margin))
FeeGrid.DataBindings.Add("DataSource", Financial, NameOf(Financial.Fees))
Total.DataBindings.Add("EditValue", Financial, NameOf(Financial.Total))

The databindings all seem to work fine, except in the case of changing the Fees doesn't change the Total textbox. If I put a button that pops up the Total property in a MessageBox, it reports the correct Total, but the textbox is not updating. It seems like the NotifyPropertyChanged on the Fee object isn't getting propagated up through the BindingList to the Form to tell it to refresh the Total textbox.


Solution

  • The databindings all seem to work fine, except in the case of changing the Fees doesn't change the Total textbox.

    There is nothing shown in your code that would raise the PropertyChanged Event for property Total. With Total being a computed value dependent on the properties Fees and Margin, changes to those properties should also raise a change notification for Total.

    As Fees is declared as a BindingList(Of Fee), subscribing to its ListChanged Event will provide a means of notifying a change to Total due to changes in Fees.

    The following is a working WinForm example similar to what you posted, but it uses only stock controls (TextBox, DataGridView, and Label).

    Public Class BindableBase : Implements INotifyPropertyChanged
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    
        Protected Sub RaisePropertyChanged(<CallerMemberName> Optional PropName As String = Nothing)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropName))
        End Sub
    End Class
    
    Public Class Financial : Inherits BindableBase
    
        Public _margin As Decimal
    
        Public Sub New()
            Fees = New BindingList(Of Fee)
            AddHandler Fees.ListChanged, AddressOf Fees_Changed
        End Sub
    
        Private Sub Fees_Changed(sender As Object, e As ListChangedEventArgs)
            NotifyTotalChanged()
        End Sub
    
        Private Sub NotifyTotalChanged()
            RaisePropertyChanged(NameOf(Me.Total))
        End Sub
    
        Public Property Margin As Decimal
            Get
                Return _margin
            End Get
            Set(ByVal value As Decimal)
                If value <> _margin Then
                    _margin = value
                    RaisePropertyChanged()
                    NotifyTotalChanged() ' Margin affects Total
                End If
            End Set
        End Property
    
        Public ReadOnly Property Fees As BindingList(Of Fee)
    
        Public ReadOnly Property Total As Decimal
            Get
                Return Fees.Sum(Function(fee) fee.Amount) / (1 - Margin)
            End Get
        End Property
    
    End Class
    
    Public Class Fee : Inherits BindableBase
    
        Private _Amount As Decimal
        Public Property Amount As Decimal
            Get
                Return _Amount
            End Get
            Set(value As Decimal)
                If value <> _Amount Then
                    _Amount = value
                    RaisePropertyChanged()
                End If
            End Set
        End Property
    End Class
    

    Example usage:

    Public Class Form1
        Private Financial As New Financial
        Protected Overrides Sub OnLoad(e As EventArgs)
            MyBase.OnLoad(e)
            SetFinancialBindings()
        End Sub
    
        Private Sub SetFinancialBindings()
            Margin.DataBindings.Add("Text", Me.Financial, NameOf(Me.Margin))
            FeeGrid.DataBindings.Add("DataSource", Me.Financial, NameOf(Me.Financial.Fees))
            Total.DataBindings.Add("Text", Me.Financial, NameOf(Me.Financial.Total))
        End Sub
    End Class