Xamarin ListView 不更新项目 属性
Xamarin ListView does not update item property
我在 Xamarin.Forms 上遇到 ListView(以及我也尝试过的 CollectionView)的问题。我在表演方面遇到了一些麻烦(XF 论坛上的帖子),但我仍然有一个问题:当 属性 发生变化时,项目不会在 "real time" 中更新。
我有一个 ObservableCollection of Category(它是 Score 的集合)和另一个 ObservableCollection,其分数的 IsFavorite 属性 设置为 true。 Score class 实现了 INotifyPropertyChanged(显然设置 IsFavorite 引发了事件)。我在 2 个 ListViews 中显示它们,在 2 个不同的选项卡中(使用 TabbedPage):每个单元格都有一些文本,以及一个带有文本绑定到 Score.IsFavorite.
的按钮
问题:当我按下这个按钮时,FavoritesList 被正确修改,主 ListView 中的单元格也被修改...但是我必须向下和向上滚动以使相应的 ViewCell 消失然后重新出现, 看到变化。否则,Button.Text 会一直显示旧值!
一些代码:
分数和类别 classes :
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 个页面之间共享...)
有人有想法吗?
谢谢,
奥利维尔
嗯,
目前我必须使用一个技巧:OnFavButtonClicked 方法可以做到这一点:
Button button = sender as Button;
button.Text = ((Score)button.BindingContext).IsFavorite.ToString();
这行得通,但它看起来很糟糕而且不完整,因为我实际上在 2 个选项卡中有 2 个 ListView,它们共享相同的对象 (Score),所以我希望对一个 ListView 进行更改以更新另一个。 ..
有什么想法吗?
嗯,一些更新...
我在我的 ViewModelBase 中实现 INotifyPropertyChanged 时犯了一个非常非常大的错误...我不知道为什么我之前没有注意到这一点,因为它在其他页面中不起作用...
我使用的是引发 PropertyChanged 事件的方法,然后 returns 属性 的值。这样做,我可以写:
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 模板 !!
所以...解决了,抱歉!
奥利维尔
这个问题可能会用一个新的来解决。
当我尝试其他解决方案时,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);
}
我在 Xamarin.Forms 上遇到 ListView(以及我也尝试过的 CollectionView)的问题。我在表演方面遇到了一些麻烦(XF 论坛上的帖子),但我仍然有一个问题:当 属性 发生变化时,项目不会在 "real time" 中更新。
我有一个 ObservableCollection of Category(它是 Score 的集合)和另一个 ObservableCollection,其分数的 IsFavorite 属性 设置为 true。 Score class 实现了 INotifyPropertyChanged(显然设置 IsFavorite 引发了事件)。我在 2 个 ListViews 中显示它们,在 2 个不同的选项卡中(使用 TabbedPage):每个单元格都有一些文本,以及一个带有文本绑定到 Score.IsFavorite.
的按钮问题:当我按下这个按钮时,FavoritesList 被正确修改,主 ListView 中的单元格也被修改...但是我必须向下和向上滚动以使相应的 ViewCell 消失然后重新出现, 看到变化。否则,Button.Text 会一直显示旧值!
一些代码:
分数和类别 classes :
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 个页面之间共享...)
有人有想法吗?
谢谢, 奥利维尔
嗯,
目前我必须使用一个技巧:OnFavButtonClicked 方法可以做到这一点:
Button button = sender as Button;
button.Text = ((Score)button.BindingContext).IsFavorite.ToString();
这行得通,但它看起来很糟糕而且不完整,因为我实际上在 2 个选项卡中有 2 个 ListView,它们共享相同的对象 (Score),所以我希望对一个 ListView 进行更改以更新另一个。 ..
有什么想法吗?
嗯,一些更新...
我在我的 ViewModelBase 中实现 INotifyPropertyChanged 时犯了一个非常非常大的错误...我不知道为什么我之前没有注意到这一点,因为它在其他页面中不起作用...
我使用的是引发 PropertyChanged 事件的方法,然后 returns 属性 的值。这样做,我可以写:
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 模板 !!
所以...解决了,抱歉! 奥利维尔
这个问题可能会用一个新的来解决。 当我尝试其他解决方案时,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);
}