问题描述
下面的this示例为CollectionView创建分组,我注意到没有一个属性是INotifyPropertyChanged,基类也不是ObservableCollection。
通过将List更改为ObservableCollection可以轻松解决后者:
public class AnimalGroup : ObservableCollection<Animal>
{
public string Name { get; private set; }
public AnimalGroup(string name,ObservableCollection<Animal> animals) : base(animals)
{
Name = name;
}
private string _someOtherPropertyIWanttochangeAtRuntime = "hey";
public string SomeOtherPropertyIWanttochangeAtRuntime { get => _someOtherPropertyIWanttochangeAtRuntime,set => SetProperty(ref _someOtherPropertyIWanttochangeAtRuntime,value); }
}
目前尚不清楚如何创建Name或任何其他属性(例如SomeOtherPropertyIWanttochangeAtRuntime),我想将该组与INotifyPropertyChanged关联。通过将接口添加到base来将其视为普通类会导致以下警告:
基本接口'INotifyPropertyChanged'是多余的,因为AnimalGroup继承了'ObservableCollection'
但是,没有什么可以让setter调用的,例如SetProperty(ref _name,Value),现有的PropertyChanged对象仅用于监视组的集合更改。它不能调用,只能处理。
如果我忽略警告并仍然实现INotifyPropertyChanged(并命名我的事件PropChanged以避免与ObservableCollection.PropertyChanged冲突),
protected bool SetProperty<T>(ref T backingStore,T value,[CallerMemberName]string propertyName = "",Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore,value))
return false;
backingStore = value;
onChanged?.Invoke();
PropChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
return true;
}
public event PropertyChangedEventHandler PropChanged;
并让我的viewmodel管理SomeOtherPropertyIWanttochangeAtRuntime的值,绑定的<Label>
永远不会看到任何更改。
<CollectionView ItemsSource="{Binding AnimalGroups}" HorizontalOptions="FillAndExpand">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label
Text="{Binding Name}"
HorizontalOptions="Start"
FontSize="24.44"
TextColor="Black"
FontAttributes="Bold"
Margin="0,10"/>
<Label
Text="{Binding SomeOtherPropertyIWanttochangeAtRuntime}" FontSize="15"
TextColor="Black"
Margin="0,0">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.FindGroupAndChangeTextCommand,Source{x:Reference thisPageName}" CommandParameter="{Binding Name}"/>
</Label.GestureRecognizers>
</Label>
...
viewmodel:
public ObservableCollection<AnimalGroup> AnimalGroups {get; private set;}
public ICommand FindGroupAndChangeTextCommand {get; private set;}
public void FindGroupAndChangeText(string name)
{
var group = AnimalGroups.FirstOrDefault(t => t.Name == name);
if (group != null)
group.someOtherPropertyIWanttochangeAtRuntime = DateTime.Now.ToString();
}
viewmodel()
{
AnimalGroups = LoadData(); // not shown
FindGroupAndChangeTextCommand = new Command(FindGroupAndChangeText);
}
结果是,标签保持为“嘿”(这是默认值),并且即使我看到上面的命令已触发并且代码找到了该组并设置了文本,也从未更改。
解决方法
同意Jason,ObservableCollection
已继承了INotifyPropertyChanged
界面,因此您将收到警告
基本接口'INotifyPropertyChanged'是多余的,因为AnimalGroup继承了'ObservableCollection'
并且请查看以下有关ObservableCollection<T>
的屏幕截图。
如果您想像这样的GIF在运行时更改项目。
根据您的代码。我在Animal
类中添加了两个属性。为了在运行时实现属性文本的更改,我们可以实现INotifyPropertyChanged
类中的Animal
。这是AnimalGroup.cs
public class AnimalGroup : ObservableCollection<Animal>
{
public string Name { get; private set; }
public AnimalGroup(string name,ObservableCollection<Animal> animals) : base(animals)
{
Name = name;
}
}
public class Animal : INotifyPropertyChanged
{
string animalName;
public string AnimalName
{
set
{
if (animalName != value)
{
animalName = value;
OnPropertyChanged("AnimalName");
}
}
get
{
return animalName;
}
}
string animalArea;
public string AnimalArea
{
set
{
if (animalArea != value)
{
animalArea = value;
OnPropertyChanged("AnimalArea");
}
}
get
{
return animalArea;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
}
}
要进行测试,请点击以下命令,即可达到MyAnimalViewModel.cs
,如以下代码所示。
public class MyAnimalViewModel
{
public ObservableCollection<AnimalGroup> AnimalGroups { get; private set; } = new ObservableCollection<AnimalGroup>();
public ICommand FindGroupAndChangeTextCommand { protected set; get; }
public MyAnimalViewModel()
{
ObservableCollection<Animal> ts = new ObservableCollection<Animal>();
ts.Add(new Animal() { AnimalArea = "Asia",AnimalName = "cat" });
ts.Add(new Animal() { AnimalArea = "Asia",AnimalName = "dog" });
ObservableCollection<Animal> ts2 = new ObservableCollection<Animal>();
ts2.Add(new Animal() { AnimalArea = "Eourp",AnimalName = "keep" });
ts2.Add(new Animal() { AnimalArea = "Eourp",AnimalName = "gggg" });
AnimalGroups.Add(new AnimalGroup("Animal1",ts));
AnimalGroups.Add(new AnimalGroup("Animal2",ts2));
FindGroupAndChangeTextCommand = new Command<Animal>((key) =>
{
key.AnimalName = "testggggg";
});
}
}
我注意到您想为CollectionView实现分组。这是我编辑过的布局。
<ContentPage.Content>
<CollectionView x:Name="MyCollectionView" ItemsSource="{Binding AnimalGroups}" IsGrouped="True" HorizontalOptions="FillAndExpand">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.GroupHeaderTemplate>
<DataTemplate>
<Label Text="{Binding Name}"
BackgroundColor="LightGray"
FontSize="Large"
FontAttributes="Bold" >
</Label>
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label
Text="{Binding AnimalArea}"
HorizontalOptions="Start"
FontSize="24.44"
TextColor="Black"
FontAttributes="Bold"
Margin="0,10"/>
<Label
Text="{Binding AnimalName}" FontSize="15"
TextColor="Black"
Margin="0,0">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1"
Command="{ Binding BindingContext.FindGroupAndChangeTextCommand,Source={x:Reference Name=MyCollectionView} }" CommandParameter="{Binding .}"
/>
</Label.GestureRecognizers>
</Label>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage.Content>
这是布局背景代码。
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
this.BindingContext = new MyAnimalViewModel();
}
}