我很难弄清楚我的动画发生了什么。
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-AnimatedGrid 的 Refresh
属性仍然绑定(bind)到 ChildItem
的 AnimationInt
属性。
当再次添加 ChildItem
(删除过滤器)时,会创建一个新 View 。
现在问题很明显:当 ChildItem
的 AnimationInt
属性改变时(点击 ROTATE RANDOM 按钮)旧的 ChildItem-AnimatedGrid 的Refresh
得到通知,然后旧的View 旋转,但是新的ramains 不变(它不旋转)。
一旦理解了问题,我们就需要弄清楚如何在 View 分离时删除旧 View 的绑定(bind):好吧,为了做到这一点,我使用了 VisualElement 的 Renderer
属性在附加元素时设置/修改,在元素分离时再次设置/修改:第一次调用它时我设置了 isAttached
标志为 true
。第二次调用它时,我将标志设置为 false
,并删除了绑定(bind)。去掉旧View的绑定(bind),可以让新View正确绑定(bind)。
关于c# - 如果集合发生变化,Listview 中的 Xamarin.Forms 动画将停止工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71190208/