Doomed Seeker
Doomed Seeker

Reputation: 23

VB Net ObservableCollection not updating ListView correctly

Just learning WPF so updating an old but useful program. I do programming as a hobby. Most of the learning I do is from the internet and I have searched for the answer to this for some days, so grateful for any help.

I'm using an Observablecollection to see how it works. My issue is that the list view that the Observablecollection is bound to doesn't show the correct data. I added a message box in to debug. After I did that, it worked perfectly, although I had to hit OK every time. After extensive reading, I was expecting the listview to update on every add (no Addrange method) but all it does is show the right number of entries but every row contains the last add.

I've changed the XAML so many times and the code behind but cannot seem to understand what I have done incorrectly or more importantly why it works with the MsgBox. The code below is the final incarnation of all the changes, not that pretty!

'convert from datatable to observable collection
    For x1 = 0 To DBdtPW.Rows.Count - 1
        PWResultHolder.PWNominalDia = DBdtPW.Rows(x1).Item(0)
        PWResultHolder.PWInternalDia = DBdtPW.Rows(x1).Item(1).ToString
        PWResultHolder.PWInternalArea = DBdtPW.Rows(x1).Item(2).ToString
        PWResultHolder.PWWithin = DBdtPW.Rows(x1).Item(3).ToString
        PWResultHolder.PWVelocity = DBdtPW.Rows(x1).Item(4).ToString
        PWResultHolder.PWPressureDropPM = DBdtPW.Rows(x1).Item(5).ToString
        PWResults.Add(PWResultHolder)
        'MsgBox(PWResultHolder.PWNominalDiaValue, MsgBoxStyle.OkOnly, "Invalid Input")
    Next

Again, thanks for any help.

Public Class ClsPWSizing

Implements INotifyPropertyChanged

Public PWPipeType As String 'Pipework material
Public PWPipeTypeList As New ObservableCollection(Of String) 'For populating pipe type combo box
Public PWFluidTemperatureList As New ObservableCollection(Of String) 'For populating temperature combo box
Public PWGlycolPerCentList As New ObservableCollection(Of String) 'For populating glycol combo box
Public PWResults As New ObservableCollection(Of clsPWFluidResult) 'For populating result listview
Public PWMinVelvalue As String 'Pipework minimum velocity
Public PWMaxVelvalue As String 'Pipework maximum velocity
Public PWMinPDvalue As String 'Pipework minimum pressure drop
Public PWMaxPDvalue As String 'Pipework maximum pressure drop
Public PWFluidTemperature As String ' Fluid temperature in the pipe
Public PWGlycolPerCent As String 'Percentage of glycol in fluid
Public PWDensity As String 'Density of fluid
Public PWDynVisc As String 'Dynamic viscosity of fluid
Public PWMassFlowRateValue As String 'Mass flow rate of fluid
Public GrdPWSizingVisible As System.Windows.Visibility 'Is the grid visible?

Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

    Public Property PWResultListView()
    Get
        Return PWResults
    End Get
    Set(ByVal value)
        PWResults = value
    End Set
End Property

XAML:

 <ListView x:Name="lstVwPWResults" HorizontalAlignment="Left" Height="405" Margin="612,53,0,0" VerticalAlignment="Top" Width="637" ItemsSource="{Binding PWResultListView, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="100" 
                            Header="Nom Dia" 
                            DisplayMemberBinding="{Binding PWNominalDia}"/>
                    <GridViewColumn Width="100" 
                            Header="Int Dia" 
                            DisplayMemberBinding="{Binding PWInternalDia}"/>
                    <GridViewColumn Width="100" 
                            Header="Area" 
                            DisplayMemberBinding="{Binding PWInternalArea}"/>
                    <GridViewColumn Width="100" 
                            Header="Within" 
                            DisplayMemberBinding="{Binding PWWithin}"/>
                    <GridViewColumn Width="100" 
                            Header="Velocity" 
                            DisplayMemberBinding="{Binding PWVelocity}"/>
                    <GridViewColumn Width="100" 
                            Header="Pressure Drop" 
                            DisplayMemberBinding="{Binding PWPressureDropPM}"/>
                </GridView>
            </ListView.View>
        </ListView>

Upvotes: 2

Views: 2361

Answers (2)

emoacht
emoacht

Reputation: 3591

Your code still misses necessary parts to reproduce the problem but okay, I can guess. I removed all non-essential parts and add fake method to populate the ObservableCollection.

Item class for ObservableCollection

Public Class clsPWFluidResult
    Public Property PWNominalDia As String
    Public Property PWInternalDia As String
    Public Property PWInternalArea As String
    Public Property PWWithin As String
    Public Property PWVelocity As String
    Public Property PWPressureDropPM As String
End Class

ViewModel class which owns the ObservableCollection.

Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Public Class clsPWSizing
    Implements INotifyPropertyChanged

    Public PWResults As New ObservableCollection(Of clsPWFluidResult) 'For populating result listview

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Public Property PWResultListView()
        Get
            Return PWResults
        End Get
        Set(ByVal value)
            PWResults = value
        End Set
    End Property

    Public Sub Pupulate()
        For Each indexString In Enumerable.Range(0, 10).Select(Function(x) x.ToString())
            Dim PWResultHolder As New clsPWFluidResult()

            PWResultHolder.PWNominalDia = "NominalDia" & indexString
            PWResultHolder.PWInternalDia = "InternalDia" & indexString
            PWResultHolder.PWInternalArea = "InternalArea" & indexString
            PWResultHolder.PWWithin = "Within" & indexString
            PWResultHolder.PWVelocity = "Velocity" & indexString
            PWResultHolder.PWPressureDropPM = "PressureDropPM" & indexString

            PWResults.Add(PWResultHolder)
        Next
    End Sub
End Class

Code behind of View (MainWindow.xaml.vb)

Class MainWindow
    Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
        Dim vm = New clsPWSizing()
        Me.DataContext = vm
        vm.Pupulate()
    End Sub
End Class

XAML of View (MainWindow.xaml)

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="500"
        Loaded="Window_Loaded">
    <Grid>
        <ListView ItemsSource="{Binding PWResultListView}">
            <ListView.View>
                <GridView >
                    <GridViewColumn Width="100"
                            Header="Nom Dia" 
                            DisplayMemberBinding="{Binding PWNominalDia}"/>
                    <GridViewColumn Width="100" 
                            Header="Int Dia" 
                            DisplayMemberBinding="{Binding PWInternalDia}"/>
                    <GridViewColumn Width="100" 
                            Header="Area" 
                            DisplayMemberBinding="{Binding PWInternalArea}"/>
                    <GridViewColumn Width="100" 
                            Header="Within" 
                            DisplayMemberBinding="{Binding PWWithin}"/>
                    <GridViewColumn Width="100" 
                            Header="Velocity" 
                            DisplayMemberBinding="{Binding PWVelocity}"/>
                    <GridViewColumn Width="100" 
                            Header="Pressure Drop" 
                            DisplayMemberBinding="{Binding PWPressureDropPM}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

This code works as expected. So there should be a problem somewhere you have not posted. Please check the difference between this code and your actual code to narrow down the cause of the problem.

Upvotes: 2

rohHit naik
rohHit naik

Reputation: 11

If you're using ViewModel(MVVM), implement INotifyPropertyChanged

use this

public event PropertyChangedEventHandler PropertyChanged;

private void OnPropertyChanged(string property)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(property));
}

In your Collection's(ItemsSource of ListView) Setter, do this

private ObservableCollection<someType> _pWResults;
public ObservableCollection<someType> PWResults
{
   get{ return _pWResults }
   set
   {
      _pWResults = value;
       OnPropertyChanged("PWResults");
   }
 }

Sorry for providing the eg code in c#

Upvotes: 0

Related Questions