InotifyPropertyChanged : expression recursively calls the containing property - wpf

On Set Property if certain condition match then i want to change the value of Set Property.
E.g. Item Gross Wt. = 5.55 Item Sales Rate = 2.892 Sales Value = 16.05 (Sales Value should rounded to 2 decimal) But actually (Sales Value / Gross Wt) <> 2.892. It should be 2.89189189 (Rate should be round upto 8 Decimal)
So whenever i enter rate 2.892 and it will set the Rate property and will set the Sales Value Property and again from there it will set the Rate Property.
By doing above property side there is no issue but my UI is not updating with 2.89189189 in Sales Rate TextBox.
See the code what i want to do this is not related to above example but having same issue.
Imports System.ComponentModel
Public Class TextViewModel
Implements INotifyPropertyChanged
Dim _View As MainWindow
Public Sub New(ByVal View As MainWindow)
_View = View
End Sub
Private Property _MyValue As String
Public Property MyValue As String
Get
Return _MyValue
End Get
Set(value As String)
_MyValue = value
NotifyPropertyChanged("MyValue")
If value = "ABC" Then
MyValue = "PQR"
End If
End Set
End Property
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Protected Sub NotifyPropertyChanged(info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
End Class
As described if i write ABC and tab then PQR should display in same textbox.
Is it possible?

But there is no need to call the Set recursively
Extra calls to NotifyPropertyChanged is just extra traffic
Set(value As String)
If _MyValue = value then
return
End If
_MyValue = value
If _MyValue = "ABC" Then
_MyValue = "PQR"
End If
NotifyPropertyChanged("MyValue")
End Set

You are Notifying of the property change too early. You first notify that it has changed, then change it. You should change it, then notify. Hope that helps!
My comment was too long so I'm adding it here: I think I understand what you are saying; in your CODE EXAMPLE, you are recursively calling the setter from the setter. But in your WRITTEN COMMENT, you are setting the properties back and forth (likely an infinite loop). THESE ARE TWO DIFFERENT PROBLEMS. In your CODE EXAMPLE, if you move the NotifyPropertyChanged event to the very end of the setter, it will work as you want. It will NotifyPropertyChanged TWICE though AFTER everything has changed (once for the "child" setter call and once for the "parent" setter call). In your WRITTEN example, first, update _ItemRate, then set ItemValue. Within ItemValue, update _ItemValue, then update _ItemRate (NOT ItemRate), and NotifyPropertyChanged for both ItemValue AND ItemRate WITHIN THE SETTER OF ItemValue. This will get you out of your infinite loop.

Related

ListView with grouping and sorting not refresh while INotifyPropertyChanged used

I have my own class which uses INotifyPropertyChanged correctly (Raising updates events), but when a property of type DateTime updated and called (View.Run) the listView not updating untill another property changing (not this property)
Now with the code:
Public Class EntryInfo
Implements INotifyPropertyChanged
ReadOnly Property DateAccessed As Date
Get
.......
Return _Access
End Get
End Property
Readonly Property Property1 as object
Get
.......
Return _Property1
End Get
End Property
Friend Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
RaiseEvent ApropertyHasChanged()
End Sub
Then when I need to Change the "DateAccessProperty" I use this code:
Friend Sub SetAccessTime(Dt As Date)
_Access = Dt
NotifyPropertyChanged("DateAccessed")
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''
After this I have a ListView named "LV1"
Dim Coll as new observableCollection(Of EntryInfo)
....... filing "Coll" with items (EntryInfo)
Lv1.ItemsSource =Coll
Then I do the following:
Do some sort and group operations.
Changing "DateAccessed" value. so that the "ApropertyHasChanged" event fired and at this point I used the following code
Private Sub RefreshViewNow()
Dim _view As ListCollectionView = TryCast(CollectionViewSource.GetDefaultView(LV1.ItemsSource), ListCollectionView)
If _view IsNot Nothing Then _view.Refresh()
'\\\ Items.Refresh()
End Sub
But _view not refreshed.
But if the property "Property1" changed the _View refreshed.
Any help?
The solution is by set the following "_view" properties:
_view.IsLiveFiltering = True
_view.IsLiveGrouping = True
_view.IsLiveSorting = True
or at least one of them if you want one of them only to be activated.

WPF Textbox Binding. VB.Net 2017

Scenario:
A structure is associated with an indexed random access file.
Data is loaded from the file in the constructor at boot time , and the structure is automatically assigned the data values from the file, and the textboxes in the WPF window is automatically populated.
The structure is also updated if the user enter new values in a textbox. So far so good.
The problem arise when the user loads a different dataset from the file when the program is running. This happen in a Button Click event. The structure is updated, but this change is not reflected in the textboxes.
Can someone hopefully shed some light on this.
Pretty new to both WPF and VB.Net
Public Class MainWindow
Shared myStruct As aStruct
Public Structure aStruct
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public year As Integer
Public Property YearP As Integer
Get
Return myStruct.year
End Get
Set(value As Integer)
myStruct.year = value
End Set
End Property
Public salary as Integer
Public Property SalaryP As Integer
Get
Return myStruct.salary
End Get
Set(value As Integer)
myStruct.salary = value
End Set
End Property
.......
.......
End Structure
Public Sub New()
Me.New(".\indxfile.ptx", ".\datafile.dat")
' This call is required by the designer.
'InitializeComponent() ' Moved
' Add any initialization after the InitializeComponent() call.
End Sub
Public Sub New(Optional _IndexFileName As String = "", Optional _DataFileName As String = "")
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.DataContext = myStruct
Dim Retval As Integer
Retval = GetRecord(2018, DataFileName, Len(myStruct), myStruct, IndexFilename)
If Retval <> 0 Then
MsgBox("Error")
End If
End Class
<TextBox x:Name="Year" Text = "{Binding YearP, Mode = TwoWay}"
Your 'Set' properties need to invoke the PropertyChanged event so that the data binding updates.

Correctly bind object instance property to a Label in code

This is supposed to be easy, yet I can't get it to work.
This is a simplified example so I can illustrate my problem. I have a List(Of Object) and I want to bind the Content of a Label to some property of an object in the list. I want to do this in code (Because those labels will be generated in runtime).
I create my object that will hold the value for the label and the list, that will hold those objects:
' The List to hold objects
Public Class BList
Public Shared listy As New List(Of BindTest)
End Class
' Object to hold label text
Public Class BindTest
Public Property Namy As String
End Class
Then I try to create the object and add it to the list. And try to set the binding for the label (for sake of simplicity, lets say I want bind to the first list item).
Dim bb As New BindTest
bb.Namy = "FirstName"
BList.listy.Add(bb)
B_label.SetBinding(Label.ContentProperty, "Namy")
B_label.DataContext = BList.listy.Item(0)
So far it works fine and label shows "FirstName" as expected. But then if I try to change the value of that first item like this:
BList.listy.Item(0).Namy = "Something else"
nothing happens and the label is not updated.
Thanks to Mike Eason.
I needed to implement INotifyPropertyChanged. I somehow thought, that it would be implemented automatically with all that WPF default-way of databinding everything. Oh well, one needs implement this manually.
So for the code to work, this part:
' Object to hold label text
Public Class BindTest
Public Property Namy As String
End Class
..must be changed to this:
' Object to hold label text
Public Class BindTest
Implements INotifyPropertyChanged
Private _Namy As String
Public Property Namy
Set(value)
_Namy = value
_PropertyChanged("Namy")
End Set
Get
Return _Namy
End Get
End Property
Private Sub _PropertyChanged(Optional ByVal PropertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
End Sub
Private Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class

WPF INotifyPropertyChanged not allowing null value

Using WPF and EF and new to both. I may use the wrong terminology.
Using the EF wizard and code generators, I set up an EF using several tables, one of which is a lookup table.
I set up a form with a datagrid for the user to edit items in this lookup table. An issue I'm having is that the user can edit a cell, then close the form, but the editing of the cell will not update the underlying SQL database. I researched INotifyPropertyChanged and it seemed to be what I needed.
I implemented INotifyPropertyChanged to class bound to the datagrid. Before the implementation, the datagrid displayed all null values. After implementation, a message displays when the first null value is read that a nullable object must have a value.
Code:
Public Property ProcedureName As String
Get
Return _ProcedureName
End Get
Set(value As String)
_ProcedureName = value
RaisePropertyChanged("ProcedureName")
End Set
End Property
Private _ProcedureName As String
The exception occurs at "_ProcedureName = value".
Entire class:
Imports System
Imports System.Collections.ObjectModel
Partial Public Class tlkpProcedures_PartB
Inherits PropertyChangedBase
Public Property CPT As String
Get
Return _CPT
End Get
Set(value As String)
_CPT = value
RaisePropertyChanged("CPT")
End Set
End Property
Private _CPT As String
Public Property ProcedureName As String
Get
Return _ProcedureName
End Get
Set(value As String)
_ProcedureName = value
RaisePropertyChanged("ProcedureName")
End Set
End Property
Private _ProcedureName As String
Public Property BillingAmount As Nullable(Of Decimal)
Get
Return _BillingAmount
End Get
Set(value As Nullable(Of Decimal))
_BillingAmount = value
RaisePropertyChanged("BillingAmount")
End Set
End Property
Private _BillingAmount As Decimal
Public Property UniqueID As Integer
Public Overridable Property tblBilling_PartB As ObservableCollection(Of tblBilling_PartB) = New ObservableCollection(Of tblBilling_PartB)
End Class
Any help or advice is appreciated.
I'm not very good at VB but it seems like you try to assign a Nullable ( value As Nullable(Of Decimal) ) to a non-nullable (Private _BillingAmount As Decimal).
You should either cast value as Decimal or define _BillingAmount as Nullable(Of Decimal).

Layered INotifyPropertyChanged

I have a class that implements INotifyPropertyChanged for a property.
I have a control that is bound to that property.
I have another class that listens to the propertychanged event. In the event handler of that class I change the value of the property in code.
The problem I have is that I don't want to do any logic in the event handler for the next time it will fire due to the change of the property due to code.
However if the user changes the value of the property in the mean time (via async gui input) I still want the logic to fire. I also need to make sure that the control gets updated (this is twoway binding).
What is the best way to do this without this becoming a complete mess?
One way to accomplish this would be to refactor the setter on your property so that it called a method taking a parameter indicating whether or not to raise the event. Here is a simple code sample:
Imports System.ComponentModel
Public Class Class1
Implements INotifyPropertyChanged
Public Property MyData() As String
Get
Return _myData
End Get
Set(ByVal value As String)
SetMyData(value, True)
End Set
End Property
Private Sub SetMyData(ByVal value As String, ByVal triggerPropertyChanged As Boolean)
_myData = value
If triggerPropertyChanged Then
OnPropertyChanged("MyData")
End If
End Sub
Private _myData As String
Private Sub OnPropertyChanged(ByVal propertyName As String)
SetMyData("new value", False)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Your question is a bit vague, but you can check to see if the value has actually changed to determine if you should do your logic.
It would be better if you include some code to be more specific in your question.

Resources