StackPanel 上的 FluidMoveBehavior 在动画前闪烁

Posted

技术标签:

【中文标题】StackPanel 上的 FluidMoveBehavior 在动画前闪烁【英文标题】:FluidMoveBehavior On a StackPanel Flickers Before Animating 【发布时间】:2015-09-02 19:56:27 【问题描述】:

我有一个ItemsControl,上面附有FluidMoveBehavior,如下所示:

<ScrollViewer VerticalScrollBarVisibility="Auto">
    <ItemsControl ItemsSource="Binding RequirementsSource" ItemTemplateSelector="StaticResource RequirementTemplateSelector">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel IsItemsHost="True">
                    <i:Interaction.Behaviors>
                        <ic:FluidMoveBehavior AppliesTo="Children" Duration="0:0:00.5" Tag="DataContext">
                            <ic:FluidMoveBehavior.EaseX>
                                <BackEase EasingMode="EaseInOut" Amplitude="0.5"/>
                            </ic:FluidMoveBehavior.EaseX>
                            <ic:FluidMoveBehavior.EaseY>
                                <BackEase EasingMode="EaseInOut" Amplitude="0.5"/>
                            </ic:FluidMoveBehavior.EaseY>
                            </ic:FluidMoveBehavior>
                    </i:Interaction.Behaviors>
                </StackPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</ScrollViewer>

当项目更改其排序顺序时,它们会进行动画处理,但首先项目排序就像没有附加 FluidMoveBehavior 一样,然后重新排序。整个过程发生得足够快,以至于在动画开始之前它看起来就像一个闪烁。

ItemsControlItemsSource 如下所示:

RequirementsSource = new ListCollectionView(Requirements);
ICollectionViewLiveShaping live = (ICollectionViewLiveShaping)RequirementsSource;
live.IsLiveSorting = True;
live.IsLiveFiltering = True;
live.LiveSortingProperties.Add("Rank");
live.LiveFilteringProperties.Add("Owner");
RequirementsSource.SortDescriptions.Add(
    new SortDescription("Rank", ListSortDirection.Ascending)
);
RequirementsSource.Filter = _filterPred;

排序由以下方法触发:

private void SwapRanks(SwapArgs args)

    IPropertyRequirement first = args.Warning.Requirements
        .First(r => r.Rank == args.RankToSwap);
    IPropertyRequirement second = args.Warning.Requirements
        .First(r => r.Rank == args.SwapWith);

    int temp = first.Rank;
    first.Rank = second.Rank;
    second.Rank = temp;

    RequirementsSource.Refresh();

有谁知道是什么导致了这种“闪烁”以及我可以做些什么来阻止它?

【问题讨论】:

您能否提供任何代码来显示您的排序方式,因为您发布的内容看起来不错 - 除了没有可使用的模板 @Cadogi 我添加了显示 ItemsSource 的代码另外,实际代码使用模板,我只是将所有内容都放在示例中 我认为声明:RequirementsSource = New ListCollectionView(Requirements) 最初会刷新视图(通过有效地设置新的 ItemsSource),然后当您进行排序时,您使用 FluidMoveBehavior 看到它正在移动从它认为的初始位置。 (我可能会离开) @Cadogi 该代码仅在视图模型的构造函数中。不是每次对代码进行排序时都会调用它。我将导致排序触发的方法添加到问题中。 现在更有意义了。然后可能值得阻止数据源的刷新,例如RequirementsSource.DeferRefresh();在开始排序之前然后刷新,这样 ItemsPanel 只会被一次更新击中。 【参考方案1】:

从您的代码中删除 RequirementsSource.Refresh(); 行。

由于您使用的是生命排序,因此动画会在 Requirement.Rank 更改(触发 PropertyChanged 事件)时开始。但是当你调用 refresh 时,它会使用 Reset 参数引发 CollectionChanged 并强制列表框刷新。

编辑:

public class MainWindowViewModel 

    ObservableCollection<Requirement> requirements;
    public MainWindowViewModel()
    
        requirements = new ObservableCollection<Requirement>(Enumerable
            .Range(0, 10)
            .Select(i => new Requirement Rank = 10 - i, Name = "Item " + i));

        RequirementsSource = new ListCollectionView(requirements);
        var live = (ICollectionViewLiveShaping)RequirementsSource;
        live.IsLiveSorting = true;
        live.IsLiveFiltering = true;
        live.LiveSortingProperties.Add("Rank");
        RequirementsSource.SortDescriptions.Add(
            new SortDescription("Rank", ListSortDirection.Ascending)
        );
        ReorderCommand = new DelegateCommand(Reorder);
    

    public ListCollectionView RequirementsSource  get; set; 
    public DelegateCommand ReorderCommand  get; private set; 

    private void Reorder()
    
        for (int i = 0; i < requirements.Count - 1; i += 2)
        
            var temp = requirements[i].Rank;
            requirements[i].Rank = requirements[i + 1].Rank;
            requirements[i + 1].Rank = temp;
        
    

public class Requirement : BindableBase

    private double _rank;

    public double Rank
    
        get  return _rank; 
        set  SetProperty(ref _rank, value); 
    

    public string Name  get; set; 

【讨论】:

这最终是正确的答案(关键部分是刷新触发了 Reset 参数)。为了做到这一点,我需要提取我创建的 INotifyPropertyChangedRank 方法中的 Rank 属性而不是实际对象本身实现的 PropertyRequirementViewModels。我还按照 Cadogi 的建议将它们包裹在 DeferRefresh 中。 我不确定你为什么使用 DeferRefresh,然后从 SwapRank 调用 PropertyChanged。我添加了对我有用的示例代码 现在 propertychanged 事件会在这些对象的 Rank 属性更改时触发,它会触发两次:一次是在我第一次设置时,一次是在我设置第二次时。我添加了 DeferRefresh,这样只会导致一次刷新。我从方法中删除了显式刷新调用。 我相信使用 LiveShaping 绑定到 ListCollectionView 的 ItemsControl 数据已经过优化,不需要 DefferRefresh。简单来说就是每次循环调用PropertyChanged时ItemsControl都不会刷新

以上是关于StackPanel 上的 FluidMoveBehavior 在动画前闪烁的主要内容,如果未能解决你的问题,请参考以下文章

StackPanel 相关

是什么导致此StackPanel滚动问题?

在 StackPanel 上填充?

silverlight StackPanel设置边框线和颜色

ScrollViewer 不能与 StackPanel 一起使用

StackPanel与Grid交叉使用