c# - 如果集合发生变化,Listview 中的 Xamarin.Forms 动画将停止工作

标签 c# listview xamarin animation xamarin.forms

我很难弄清楚我的动画发生了什么。

Viewmodel 由一个 ObservableCollection 组成,每个项目都包含一个子 ObservableCollection

Parents 集合绑定(bind)到 BindableLayout。该布局的 ItemTemplate 包含一个 Listview 以显示子元素。

<StackLayout BindableLayout.ItemsSource ="{Binding Parents}">
    <BindableLayout.ItemTemplate>
        <DataTemplate x:DataType="models:ParentRows">
            <StackLayout>      
                <Grid BackgroundColor="White" >
                     <!-- Some bindable content there -->
                </Grid>
            
               <ListView ItemsSource="{Binding Childs}" CachingStrategy="RecycleElementAndDataTemplate" RowHeight="50">
                    <ListView.ItemTemplate>
                        <DataTemplate x:DataType="models:ChildRows">
                            <ViewCell>
                                <StackLayout BackgroundColor="White">
                                    <controls:AnimatedGrid Refresh="{Binding Animation}"
                                          <!-- Some bindable content there -->
                                    </controls:AnimatedGrid>
                                </StackLayout>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </StackLayout>
        </DataTemplate>
    </BindableLayout.ItemTemplate>
</StackLayout>

我在子 ListView 上使用 AnimatedGrid,此控件继承自 Grid。它有一个名为 Refresh 的额外 BindableProperty 和一个只要 Refresh 属性发生变化就会调用的动画代码

        private async void AnimateItem()
        {
            await Device.InvokeOnMainThreadAsync(() =>
            {
                this.RotateTo(360,500);
            });
        }

一切正常,直到我开始过滤列表。 过滤列表后,后续调用 AnimateItem 将无效。 更准确地说,如果父项从列表中删除,然后再次添加,则此父项的子项将永远不会再设置动画。 过滤列表包括删除/插入可观察集合的父项(myCollection.Remove(item),myCollection.Insert(index,item),使用框架中的集合方法)

这似乎不是一个可观察到的集合绑定(bind)问题,因为父集合和子集合中的值仍然可以完美地更新。 更改 CachingStrategy 对问题也没有影响。

我发现,如果我用 CollectionView 替换 ListView 控件,问题就会消失。但是,我真的很想找到一个解决方案,允许我保留 ListView 控件,因为切换到 CollectionView 会带来许多其他不良影响。

编辑 22/02/2022 : 我做了一个sample project to reproduce the issue在 github 上。

  • 基本上,您可以多次点击“随机旋转”来制作 随机 child 旋转。
  • 单击“删除并添加 2 个父项”后,您 可以看到移除/重新插入的项目不再旋转。

编辑 15/03/2022 : 我仍然无法弄清楚出了什么问题。 但是,出于测试目的,我在控件构造函数中添加了一个 task.delay,然后是一个动画调用,这个调用正在过滤项目上工作。这超出了我的理解范围。

最佳答案

解决方案

在您的 AnimatedGrid 类中,添加一个 isAttached 标志,并将以下行添加到您的 OnPropertyChanged 覆盖:

bool isAttached = false;

protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    base.OnPropertyChanged(propertyName);

    if (propertyName == "Renderer")
    {
        isAttached = !isAttached;
        if (!isAttached) this.RemoveBinding(RefreshProperty);
    }

    if (propertyName == nameof(Refresh))
    {
    ...

原因

免责声明:我花了很长时间才弄清楚这里发生了什么,虽然我能够解决问题,但我不会声称我完全理解它。我相信这是 Xamarin 中的一个错误,对于您的情况,我会在 Github 中提交一个问题(尽管 MAUI 可能会更正此问题...)

ChildItem 被移除(做 Filter)时,旧的 ChildItem-AnimatedGridRefresh 属性仍然绑定(bind)到 ChildItemAnimationInt 属性。

当再次添加 ChildItem(删除过滤器)时,会创建一个新 View 。

现在问题很明显:当 ChildItemAnimationInt 属性改变时(点击 ROTATE RANDOM 按钮)旧的 ChildItem-AnimatedGridRefresh 得到通知,然后旧的View 旋转,但是新的ramains 不变(它不旋转)。


一旦理解了问题,我们就需要弄清楚如何在 View 分离时删除旧 View 的绑定(bind):好吧,为了做到这一点,我使用了 VisualElementRenderer 属性在附加元素时设置/修改,在元素分离时再次设置/修改:第一次调用它时我设置了 isAttached 标志为 true。第二次调用它时,我将标志设置为 false,并删除了绑定(bind)。去掉旧View的绑定(bind),可以让新View正确绑定(bind)。

关于c# - 如果集合发生变化,Listview 中的 Xamarin.Forms 动画将停止工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71190208/

相关文章:

c# - 调用 dll 时从未命中断点

c# - Repeater、Custom Paging、PagedDataSource,可能吗?

c# - 多点连接 : iOS and Android

android - Xamarin Android.App.DialogFragment 已过时

ios - 如何在 xamarin IOS 中使用弹出导航

c# - StreamWriter 不创建文本文件

c# - "Copy Local = false"引用的项目显示 "Could not load file or assembly"

更改光标后Android ListView不刷新

android - 使用新数据从 simpleAdapter 更新 listview

android - 在android中显示动态变化的列表项的最佳方式