ravi kumar
ravi kumar

Reputation: 1620

Updating ListView on property change

I am having a strange problem in my UWP application. I have a ListView bound to an ObservableCollection<SomeInterface>. I am changing one property of all the items in the ObservableCollection. The problem is that because of the ObservableCollection does not take a class as the type argument, rather it takes an interface, I cannot figure out how to implement INotifyCollectionChanged on the interface.

So I went ahead and changed the property of all items in the collection ( without implementing INotifyPropertyChanged ). This is giving weird behavior. When I change the property ( I do this as soon as I navigate to the page containing the ListView ) It only affects only those ListView items that are not in view currently ie, if there are 100 items in the ListView and I can see the first 10 without scrolling down then those 10 items are unchanged but when I scroll down, I see that other (other than the first 10) ListViewItems are reflecting the changes that were made. And to add to this, when I scroll up again ( to the first 10 items ), I see that now they are also changed.

to summarize, only the items that are not currently in the view get updated.

here's my code to update the ObservableCollection:

class SomePage : Page
{
    private ObservabeCollection<SomeInterface> SomeObservableCollection { get; set; } = new ObservabeCollection<SomeInterface>();
    ...
    private async Task ModifyObservableCollection()
    {
        var response = await MakeApiCall();
        var facets = response.ListOfItems;

        foreach (var item in SomeObservableCollection.ToList())
        {
            var fromApi = facets.author.FirstOrDefault(i => i.key == item.key);
            if (fromApi == null) continue;

            var itemInList = SomeList.FirstOrDefault(i => i.key == fromApi.key);
            itemInList.read = fromApi.read;
            itemInList.num = fromApi.num;

            //here!!
            itemInList = SomeObservableCollection.FirstOrDefault(i => i.key == fromApi.key);
            itemInList.read = fromApi.read;
            itemInList.num = fromApi.num;
        }
    }
}

This is my ListView:

<ListView ItemsSource="{x:Bind AuthorFacets, Mode=OneWay}"
        ItemTemplate="{StaticResource ListViewDataTemplate}"                         
        SelectionMode="Multiple">

            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                </Style>
            </ListView.ItemContainerStyle>

</ListView>

My itemtemplate:

<DataTemplate x:Key="ListViewDataTemplate"
              x:DataType="local:ISomeInterface">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <TextBlock Text="{x:Bind read}"/>               
        <TextBlock Text="{x:Bind num}"/>             
    </Grid>
</DataTemplate>

Upvotes: 0

Views: 1528

Answers (1)

Johnny Westlake
Johnny Westlake

Reputation: 1460

Change

<TextBlock Text="{x:Bind read}"/>               
<TextBlock Text="{x:Bind num}"/>    

to

<TextBlock Text="{x:Bind read, Mode=OneWay}"/>               
<TextBlock Text="{x:Bind num, Mode=OneWay}"/>    

And implement INotifyPropertyChanged, and it should work. By default, x:Bind is OneTime unlike traditional binding (because performance / memory) so your items in view won't update.

Without INPC? You could possibly try calling Bindings.Update() on the parent view after changing the properties, though you may lose scroll position.

Upvotes: 2

Related Questions