【问题标题】:Xamarin ListView does not update item propertyXamarin ListView 不更新项目属性
【发布时间】:2020-06-19 15:03:01
【问题描述】:

我在使用 ListView(以及我也尝试过的 CollectionView)的 Xamarin.Forms 上遇到问题。我在性能方面遇到了一些麻烦(XF 论坛上的帖子),但我仍然有一个问题:当它们的属性发生变化时,项目不会“实时”更新。

我有一个类别的 ObservableCollection(它是一个分数的集合)和另一个带有 IsFavorite 属性设置为 true 的分数的 ObservableCollection。 Score 类实现了 INotifyPropertyChanged(显然设置 IsFavorite 会引发事件)。我将它们显示在 2 个 ListView 中,在 2 个不同的选项卡中(使用 TabbedPage):每个单元格都有一些文本,以及一个带有绑定到 Score.IsFavorite 的文本的 Button。

问题:当我按下此按钮时,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; }
}

视图模型:

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);
    }
}

还有观点:

<?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 个页面之间共享...)

有人有想法吗?

谢谢, 奥利维尔

【问题讨论】:

  • 尝试修改缓存策略
  • 一开始我使用的是 RetainElement(默认情况下),但我遇到了同样的问题,性能非常差......

标签: listview xamarin inotifypropertychanged


【解决方案1】:

嗯,

目前我必须使用一个技巧:OnFavButtonClicked 方法可以做到这一点:

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

这可行,但它看起来很糟糕而且不完整,因为我实际上在 2 个选项卡中有 2 个 ListView,它们共享相同的对象(分数),所以我希望一个 ListView 上的更改能够更新另一个...

有什么想法吗?

【讨论】:

  • 感谢您的分享,请标记您的答案。它将帮助其他有类似问题的人。
【解决方案2】:

嗯,一些更新...

我在 ViewModelBase 中实现 INotifyPropertyChanged 时犯了一个非常非常大的错误...我不知道为什么我之前没有注意到这一点,因为它在其他页面中不起作用...

我使用的方法会引发 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 模板!!

所以...解决了,抱歉! 奥利维尔

【讨论】:

    【解决方案3】:

    这个问题可以通过 NEW 来解决。 当我尝试其他解决方案时,OnPropertyChanged() 似乎没有反应。

        public ObservableCollection<Item> Items { get; set; }
        
        public ListViewPage1()
        {
            InitializeComponent();
            Items = new ObservableCollection<Item>();
            Listview.ItemsSource = Items;
        }
    
        private void Listview_ItemTapped(object sender, ItemTappedEventArgs e)
        {
            Item item = new Item(Items.ElementAt(e.ItemIndex));
    
            switch (item.Source as FileImageSource)
            {
                case "green.png":
                    item.Source = "red.png";
                    break;
                default:
                    item.Source = "green.png";
                    break;
            }
    
            Items.RemoveAt(e.ItemIndex);
            Items.Insert(e.ItemIndex, item);
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-07
      • 1970-01-01
      相关资源
      最近更新 更多