locked
Update listview after object modification RRS feed

  • Question

  • User378546 posted

    Hi,

    I need your help for updating my listview after background tasks... I have a ListView binded to my viewmodel ObservableCollection named Results where Article is a simple object (with ID, Price etc) and an ImagePath (ImageSource type). In my viewmodel, when I update the Results collection with an Article's ImagePath (after a download), the UI don't shows the images...

    Have you an idea to help me ?

    Thanks

    Tuesday, October 16, 2018 10:01 AM

All replies

  • User369978 posted

    Please attach your code.

    Wednesday, October 17, 2018 9:01 AM
  • User378546 posted

    This is my xaml:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage
        x:Class="EvoPrixArt.Portable.SearchPage.SearchPage"
    ....
        xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
        xmlns:local="clr-namespace:EvoPrixArt.Portable.SearchPage"
        xmlns:telerikDataControls="clr-namespace:Telerik.XamarinForms.DataControls;assembly=Telerik.XamarinForms.DataControls"
        xmlns:telerikInput="clr-namespace:Telerik.XamarinForms.Input;assembly=Telerik.XamarinForms.Input"
        xmlns:telerikListView="clr-namespace:Telerik.XamarinForms.DataControls.ListView;assembly=Telerik.XamarinForms.DataControls"
        Title="Recherche"
        ios:Page.UseSafeArea="true"
        Icon="search_icon.png">
        <ContentPage.BindingContext>
            <local:ViewModelSearchPage />
        </ContentPage.BindingContext>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="40" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <telerikInput:RadEntry
                x:Name="SearchBar"
                Grid.Row="0"
                Completed="SearchBar_Completed"
                WatermarkText="Rechercher un article" />
            <telerikDataControls:RadListView
                x:Name="ListSearchResults"
                Grid.Row="1"
                IsItemsReorderEnabled="True"
                IsLoadOnDemandEnabled="True"
                IsVisible="False"
                ItemsSource="{Binding Results, Mode=TwoWay}"
                LoadOnDemand="ListViewResults_LoadOnDemand"
                LoadOnDemandMode="Manual">
                <telerikDataControls:RadListView.ItemTemplate>
                    <DataTemplate>
                        <telerikListView:ListViewTemplateCell>
                            <telerikListView:ListViewTemplateCell.View>
                                <Grid HeightRequest="200">
                                    <Grid>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="2*" />
                                            <RowDefinition Height="*" />
                                        </Grid.RowDefinitions>
                                        <Image
                                            Grid.Row="0"
                                            Grid.RowSpan="2"
                                            Grid.Column="0"
                                            Aspect="AspectFill"
                                            Source="{Binding ImgPath, Mode=TwoWay}" />
                                        <BoxView Grid.Row="1" BackgroundColor="#EAEAEC" />
                                        <Grid Grid.Row="1" Margin="10">
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="Auto" />
                                            </Grid.RowDefinitions>
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="2*" />
                                                <ColumnDefinition Width="1*" />
                                            </Grid.ColumnDefinitions>
                                            <Label
                                                Grid.Row="0"
                                                Grid.Column="0"
                                                FontSize="Medium"
                                                Opacity="1"
                                                Text="{Binding DesiAutoAd, Mode=TwoWay}"
                                                VerticalTextAlignment="Center" />
                                            <StackLayout
                                                Grid.Row="0"
                                                Grid.Column="1"
                                                HorizontalOptions="End"
                                                Orientation="Vertical"
                                                VerticalOptions="Center">
                                                <Label
                                                    FontAttributes="Bold"
                                                    FontSize="Small"
                                                    HorizontalTextAlignment="End"
                                                    Text="{Binding PrixNet, Mode=TwoWay, StringFormat='\{0\}€'}" />
                                                <Label
                                                    FontAttributes="Italic"
                                                    FontSize="Micro"
                                                    HorizontalTextAlignment="End"
                                                    Opacity="0.5"
                                                    Text="{Binding StockDisponible, Mode=TwoWay}"
                                                    TextColor="Green" />
                                            </StackLayout>
                                        </Grid>
                                    </Grid>
                                </Grid>
                            </telerikListView:ListViewTemplateCell.View>
                        </telerikListView:ListViewTemplateCell>
                    </DataTemplate>
                </telerikDataControls:RadListView.ItemTemplate>
                <telerikDataControls:RadListView.LayoutDefinition>
                    <telerikListView:ListViewGridLayout
                        HorizontalItemSpacing="6"
                        ItemLength="200"
                        SpanCount="2"
                        VerticalItemSpacing="6" />
                </telerikDataControls:RadListView.LayoutDefinition>
            </telerikDataControls:RadListView>
        </Grid>
    </ContentPage>
    

    My CS:

    using System;
    using System.Threading.Tasks;
    using Xamarin.Forms;
    
    namespace EvoPrixArt.Portable.SearchPage
    {
        public partial class SearchPage : ContentPage
        {
    
            public SearchPage()
            {
                InitializeComponent();
            }
    
            private async void SearchBar_Completed(object sender, System.EventArgs e)
            {
                await Task.Run(() =>
                {
                    (this.BindingContext as ViewModelSearchPage).SearchArt(SearchBar.Text);
                });
                await Task.Run(() =>
                {
                    (this.BindingContext as ViewModelSearchPage).LoadVignettes();
                });
            }
    
            private async void ListViewResults_LoadOnDemand(object sender, EventArgs e)
            {
            //SOME CODE
            }
        }
    }
    

    And my ViewModel:

    using EvoPrixArt.Portable.Models;
    using EvoPrixArt.Portable.Services;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using Telerik.XamarinForms.Common;
    using Xamarin.Forms;
    
    namespace EvoPrixArt.Portable.SearchPage
    {
        public class ViewModelSearchPage : NotifyPropertyChangedBase
        {
            public ObservableCollection<Article> Results { get; set; } = new ObservableCollection<Article>();
    
            public ViewModelSearchPage() { }
    
            public void LoadMoreItems()
            {
                //SOME CODE
            }
    
            public void SearchArt(string research)
            {
                //SOME CODE
            }
    
            public void LoadVignettes()
            {
                foreach (Article art in this.Results)
                {
                    string img = AppelsWebServices.GetVignetteArticle(art.IdArt);
                    art.ImgPath = ImageSource.FromFile(img);
                }
            }
        }
    }
    

    So my LoadVignettes function in the ViewModel fill the Results collection correctly but my UI doesn't update with the images... The SearchArt function works well : my listview is updated for each item added in the Results collection.

    I think the list is only update when items are added or removed from the ObservableCollection but not on items modifications...

    Thanks

    Wednesday, October 17, 2018 9:59 AM
  • User93655 posted

    Hello / Salut :) (AppelsWebServices me fait penser que tu es fr ^^)

    First, either you are sure about your type of VM and if it has been set and then you do a cast like this
    var vm = (ViewModelSearchPage)BindingContext; Otherwise you should prefer var vm = (this.BindingContext as ViewModelSearchPage); But don't forget to test if vm is null with something like vm?.SearchArt(SearchBar.Text); because it can be null

    Try this ViewModelSearchPage vm = (ViewModelSearchPage)this.BindingContext;
    Task.Run(() => vm.SearchArt(SearchBar.Text)).ContinueWith(previousTask => vm.LoadVignettes(), TaskScheduler.FromCurrentSynchronizationContext());

    My guess is you are not performing UI things on the UI thread. You also can try a

    RaisePropertyChanged(nameof(Results));

    Wednesday, October 17, 2018 11:27 AM
  • User378546 posted

    Salut :) Je vais donc parler français puisque je suis démasqué :D

    J'ai modifié mon code pour correspondre à tes conseils, mais ce n'est pas mieux... Ma liste s'affiche bien mais sans les images...

    Wednesday, October 17, 2018 11:50 AM
  • User93655 posted

    (héhé :D par contre je te conseil de rester en anglais si tu veux maximiser tes chances d'obtenir de l'aide ;) )

    Can you show how you filter items in SearchArt ?

    Can you show how what does string img = AppelsWebServices.GetVignetteArticle(art.IdArt); return ?

    Wednesday, October 17, 2018 12:02 PM
  • User93655 posted

    <Image> <Image.Source> <UriImageSource Uri="{Binding ImgPath}"/> </Image.Source> </Image>

    Wednesday, October 17, 2018 12:15 PM
  • User378546 posted

    The SearchArt function just call a webservice and return me a List of "Article" that I add in my Results collection:

    public void SearchArt(string research)
            {
                List<Article> results = AppelsWebServices.GetArticlesFromResearch(research);
    
                Device.BeginInvokeOnMainThread(() =>
                {
                    this.Results.Clear();
                    foreach (Article art in results)
                    {
                        this.Results.Add(art);
                    }
                });
            }
    

    And this function works well.

    The AppelsWebServices.GetVignetteArticle() function download the image and save it in the phone ("/storage/emulated/0/" for Android) and return the complete path to this image (for exemple "/storage/emulated/0/63.jpeg" for the Article with the ID 63).

    Wednesday, October 17, 2018 12:19 PM
  • User378546 posted

    @NicolasKrier said: <Image> <Image.Source> <UriImageSource Uri="{Binding ImgPath}"/> </Image.Source> </Image>

    It doesn't work (white images)...

    Wednesday, October 17, 2018 12:24 PM
  • User93655 posted

    Arg I post the last answer without refreshing the page and did not see your answer. Are all the images embedded in the app ? How do you store them ?

    I think the path might be wrong.

    https://forums.xamarin.com/discussion/54807/display-an-image-stored-locally

    Wednesday, October 17, 2018 12:33 PM
  • User378546 posted

    The images are not embedded in the app because they are downloaded from a webservice and saved in the root folder of Android in their corresponding format (PNG, JPEG...)

    The path is right because if I call the AppelWebServices.GetVignetteArticle() function in the SearchArt function the images are visible in the UI (but it's long and it's block the UI during the download):

    public void SearchArt(string research)
            {
                List<Article> results = AppelsWebServices.GetArticlesFromResearch(research);
    
                Device.BeginInvokeOnMainThread(async () =>
                {
                    this.Results.Clear();
                    foreach (Article art in results.Item1)
                    {
                        string img= AppelsWebServices.GetVignetteArticle(art.IdArt);
                        this.Results.Add(art);
                        art.ImgPath = ImageSource.FromFile(img);
                    }
                });
            }
    

    But it's not what I want to do because here images are downloaded at the same time the Article is added to the list and not after all Articles are added to the list...

    Wednesday, October 17, 2018 12:45 PM