【问题标题】:CollectionView Grouping with Observables使用 Observable 进行 CollectionView 分组
【发布时间】:2020-12-29 10:10:32
【问题描述】:

按照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 的值,绑定的 &lt;Label&gt; 永远不会看到任何变化。

<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,0,0,10"/>

                    <Label 
                        Text="{Binding SomeOtherPropertyIWantToChangeAtRuntime}" FontSize="15" 
                        TextColor="Black" 
                        Margin="0,0,0,0">
                        <Label.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding BindingContext.FindGroupAndChangeTextCommand, Source{x:Reference thisPageName}" CommandParameter="{Binding Name}"/>
                        </Label.GestureRecognizers>
                    </Label>
...

视图模型:

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

结果是标签保持“嘿”(这是默认值)并且永远不会改变,即使我可以看到上面的命令触发并且代码找到了组并设置了文本。

【问题讨论】:

  • ObservableCollection 有一个 OnPropertyChanged 方法
  • 我不认为你理解我的问题。 ObservableCollection 确实具有 PropertyChanged,但它仅在添加和删除项目时触发。这与我需要什么无关。
  • 我明白,你应该可以在你的属性设置器中调用 OnPropertyChanged
  • 现在我明白了。设置器的工作方式如下: set {_propname= value; OnPropertyChanged(newPropertyChangedEventArgs(nameof(Propname)));}

标签: xamarin.forms grouping observablecollection inotifypropertychanged collectionview


【解决方案1】:

同意Jason,ObservableCollection继承了INotifyPropertyChanged接口,所以你会得到警告

基本接口 'INotifyPropertyChanged' 是多余的,因为 AnimalGroup 继承了 'ObservableCollection'

请看下面关于ObservableCollection&lt;T&gt;的截图。

如果你想像这个 GIF 一样在运行时更改项目。

根据您的代码。我在Animal 类中添加了两个属性。为了实现在运行时更改属性的文本,我们可以在Animal类中实现INotifyPropertyChanged。这里是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,0,0,10"/>

                            <Label 
                                Text="{Binding AnimalName}" FontSize="15" 
                                TextColor="Black" 
                                 Margin="0,0,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();
        }
}

【讨论】:

  • 如果以上回答对您有帮助,请采纳为回答(点击此回答左上角的“✔”),对有类似问题的其他人有帮助
  • 您的示例更新组内的项目。我想更新与组本身关联的项目。例如“Animal1 - a corvid subtype”,然后点击它并将其更改为“Animal1 - a hominid subtype”
  • 你没有CollectionView.GroupHeaderTemplate, Header 和data 在同一个item?你能给我一张关于你的数据结构的截图吗?
  • 确实如此。我的子项目是水平渲染的,我无法使用常规的分组隐喻来做到这一点。所以我的组是由一个垂直的 CollectionView 处理的,而项目是前者的 ItemTemplate 中的一个水平的 CollectionView。 Jason 帮我解决了我发布的问题。
  • 顺便说一句,我不确定如何标记这个回答。
猜你喜欢
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-27
  • 2018-11-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多