Nazeerah Banu
Nazeerah Banu

Reputation: 43

WPF ListView not updating at runtime

I have a listview's itemsource binded to a Observable collection of Animal class.

When the window loads up, listview displays all the items correctly.

But I have a button which deletes an item from the observablecollection which did not update the listview.

Expected Behaviour: Button click should delete first item in observable collection and update the UI

Observed Behaviour: Button click should deletes first item in observable collection but did not update the UI

public class Animal
{
    public int Num { get; set; }
    public string Name { get; set; }
}


 public class ViewModel:INotifyPropertyChanged
{
    private ObservableCollection<Animal> animals;

    public ObservableCollection<Animal> Animals
    {
        get { return animals; }
        set { animals = value; OnPropertyChanged("Animals"); }
    }

    public ViewModel()
    {
        Animals = new ObservableCollection<Animal>()
        {
            new Animal(){ Name="ASD", Num = 1},
            new Animal(){ Name="XYZ", Num = 2},
        };
    }
    public void Run()
    {
        Animals.RemoveAt(0);
    }
    public event PropertyChangedEventHandler PropertyChanged;
    // Create the OnPropertyChanged method to raise the event
    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}



<Grid DataContext="{Binding Source={StaticResource ViewModelDataSource}}">
    <Button Content="Button" HorizontalAlignment="Left" Height="20" Margin="70,285,0,0" VerticalAlignment="Top" Width="100" Click="Button_Click"/>
    <ListView x:Name="mylistview" HorizontalAlignment="Left" Height="212" Margin="42,47,0,0" VerticalAlignment="Top" Width="446" ItemsSource="{Binding Animals}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Label Content="{Binding Num}"/>
                    <Label Content="{Binding Name}"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

</Grid>



public partial class MainWindow : Window
        {
            private ViewModel vm;
            public MainWindow()
            {
                InitializeComponent();
                vm = new ViewModel();
            }

            private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
            {
                vm.Run();
            }
        }

Upvotes: 1

Views: 738

Answers (1)

ASh
ASh

Reputation: 35681

ListView uses DataContext="{Binding Source={StaticResource ViewModelDataSource}}.

In a Window you create another instance of a view model (vm = new ViewModel();). After that you have 2 different instances and collections. vm.Run(); removes item from collection which is not connected to view.

You need to work with one instance, so try to find the same resource, which is used in the view:

public MainWindow()
{
    InitializeComponent();
    vm = (ViewModel)this.FindResource("ViewModelDataSource");
}

Also DataContext setter can be simplified:

`DataContext="{StaticResource ViewModelDataSource}"`


it is preferable to follow MVVM aproach and get rid of code behind:

1] declare command property in a viewmodel

public ICommand RunCmd { get; private set; }

2] use some ready-made ICommand implementation, e.g. RelayCommand or DelegateCommand and initialize RunCmd property from viewmodel constructor:

RunCmd = new RelayCommand(Run);

3] bind Button to that command:

<Button Content="Button" 
        HorizontalAlignment="Left" 
        Height="20" Width="100" Margin="70,285,0,0" 
        VerticalAlignment="Top" 
        Command="{Binding RunCmd}"/>

note, that Click handler is removed

Upvotes: 1

Related Questions