Xamarin ListView не обновляет свойство элемента - PullRequest
0 голосов
/ 06 марта 2020

У меня проблема с Xamarin.Forms с ListView (и CollectionView, который я тоже пробовал). У меня были некоторые проблемы с производительностью (ветка на форуме XF), которые были решены, но у меня есть еще одна проблема: элементы не обновляются в «реальном времени» при изменении их свойств.

У меня есть ObservableCollection of Category ( который является коллекцией Score) и другой ObservableCollection с оценками, для свойства IsFavorite которых установлено значение true. Класс Score реализует INotifyPropertyChanged (и, очевидно, установка IsFavorite вызывает событие). Я отображаю их в 2 ListViews, на 2 разных вкладках (используя TabbedPage): каждая ячейка имеет некоторый текст и кнопку с текстом, привязанным к Score.IsFavorite.

Проблема: когда я пу sh это Кнопка, список FavoritesList корректно изменяется, и ячейка в основном ListView тоже изменяется ... НО мне нужно прокрутить вниз и вверх, чтобы соответствующий ViewCell исчез, а затем снова появился, чтобы увидеть изменения. Иначе, Button.Text продолжает показывать старое значение!

Некоторый код:

Классы Score и Category:

public class Score : HasPropertyChanging, IComparable<Score>
{
    // Other properties...
    // The HasPropertyChanging abstract class implements INotifyPropertyChanged

    private bool _isFavorite;
    public bool IsFavorite { get => _isFavorite; set => _isFavorite = SetFieldValueAndNotify(value); }
}

public class Category : ObservableCollection<Score>
{
    public string Name { get; set; }
}

ViewModel:

public class ScoreListViewModel : ViewModelBase
{
    // Lists of categories and scores : Categories<Score> (not ordered) / all scores (order AZ) / favorites (order AZ)
    public ObservableCollection<Category> Categories { get; set; } = new ObservableCollection<Category>();
    public ObservableCollection<Score> Scores { get; set; } = new ObservableCollection<Score>();
    public ObservableCollection<Score> FavoriteScores { get; set; } = new ObservableCollection<Score>();

    public ScoreListViewModel()
    {
        this.InitializeScoreList();
    }

    // Toggle favorite status (raised from ICommand in XAML)
    public void ToggleFavorites(Score score)
    {
        score.IsFavorite = !score.IsFavorite;

        if (score.IsFavorite)
            this.FavoriteScores.AddSorted(score);

        else
            this.FavoriteScores.Remove(score);
    }
}

И View:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:viewmodels="clr-namespace:CardioCALC"
    x:Class="CardioCALC.ScoreListPage"
    x:Name="ThisPage"
    x:DataType="viewmodels:ScoreListViewModel"
    BindingContext="{x:Static viewmodels:ScoreListViewModel.Instance}">

    <ListView
        ItemsSource="{Binding Categories}"
        IsGroupingEnabled="True"
        GroupDisplayBinding="{Binding Name}"
        HasUnevenRows="True"
        CachingStrategy="RecycleElement">

        <ListView.ItemTemplate>
            <DataTemplate x:DataType="viewmodels:Score">
                <ViewCell Height="70">
                    <Grid BackgroundColor="White" Padding="20, 5, 10, 5">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*" />
                            <RowDefinition Height="1.5*" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="50" />
                        </Grid.ColumnDefinitions>

                        <Label Text="{Binding DisplayName}" FontSize="17" Grid.Column="0" Grid.Row="0" />
                        <Label Text="{Binding Detail}" FontSize="13" Opacity="0.6" Grid.Column="0" Grid.Row="1" />
                        <Button Text="{Binding IsFavorite, Converter={StaticResource FavoritesDisplayValueConverter}}" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2"
                                Clicked="OnFavButtonClicked"
                                Command="{Binding Source={x:Reference ThisPage}, Path=ToogleFavorites}" CommandParameter="{Binding .}" />
                    </Grid>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

(Примечание: я немного упростил ViewModel, который фактически использует одиночный экземпляр для совместного использования между двумя страницами ...)

У кого-нибудь есть идея?

Спасибо, Оливье

Ответы [ 2 ]

0 голосов
/ 14 марта 2020

Ну, какое-то обновление ...

Я сделал очень большую ошибку при реализации INotifyPropertyChanged в моей ViewModelBase ... Я не знаю, почему я раньше этого не замечал, потому что это не было функционально на других страницах ...

Я использовал метод, который вызывает событие PropertyChanged, а затем возвращает значение для свойства. Делая это, я мог написать:

protected T SetPropertyValueAndNotify(T value, [CallerMemberName] string propertyName = null)
{
     PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
     return value;
}

private bool _myProperty;
public bool MyProperty
{
     get => _myProperty;
     set => _myProperty = SetPropertyValueAndNotify(value);
}

НО! Сделав это, я уведомил шаблон XAML только при двойном изменении свойства !!

Итак ... решено, и извините! Оливье

0 голосов
/ 08 марта 2020

Ну,

На данный момент я должен использовать трюк: метод OnFavButtonClicked делает это:

Button button = sender as Button;
button.Text = ((Score)button.BindingContext).IsFavorite.ToString();

Это работает, но выглядит очень плохо, и это неполно, как у меня фактически 2 ListView на 2 вкладках, которые имеют одни и те же объекты (Score), поэтому я бы хотел, чтобы изменения в одном ListView обновили другой ...

Есть идеи?

...