c# - 141 是可以同时设置动画的 WPF 面板项的最大数量吗?

标签 c# wpf animation panel

我用 C# 设计了几个动画 Panel,它们都继承自一个基类,该基类在 Panel.Children 上执行实际动画。直到最近,它们都运行良好……但最近,我在使用一个包含大量项目的集合时,我开始遇到一些奇怪的视觉错误。

当应用程序首次加载项目时,前 x 个项目正确显示,然后其余项目全部显示在第一个位置(彼此重叠)。如果我调整窗口大小或选择一个项目,它们都会重新排列以正确显示。 (使用下面的示例代码,您必须调整窗口大小才能看到它们自行更正。)

经过大量 的摸索和调试之后,我发现正确的值仍在传递给每个项目的动画代码,但最后的 x 个项目根本没有被动画化。每个项目肯定会通过动画代码,但最后 x 个项目停留在动画 Panel.ArrangeOverride 方法中设置的位置。

为了尝试在我的代码中找到问题,我创建了一个新的 UserControl 和动画 Panel 类,其中包含重新创建问题所需的最少代码。这样做之后,我很惊讶问题仍然可以重现。在我的示例代码中有 141 个项目时,没有显示问题,但是如果更多,就会出现问题。

请原谅冗长的代码行,但要显示的内容太多了。

AnimatedStackPanel.cs 类

public class AnimatedStackPanel : Panel
{
    protected override Size MeasureOverride(Size availableSize)
    {
        double x = 0, y = 0;
        foreach (UIElement child in Children)
        {
            child.Measure(availableSize);
            x = Math.Max(x, availableSize.Width == double.PositiveInfinity ? child.DesiredSize.Width : availableSize.Width);
            y += child.DesiredSize.Height;
        }
        return new Size(availableSize.Width == double.PositiveInfinity ? x : availableSize.Width, availableSize.Height == double.PositiveInfinity ? y : availableSize.Height);
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        if (this.Children == null || this.Children.Count == 0) return finalSize;
        Size previousChildSize = new Size();
        Point endPosition = new Point(0, 0);
        foreach (UIElement child in Children)
        {
            endPosition.Y += previousChildSize.Height;
            double childWidth = finalSize.Width;
            child.Arrange(new Rect(0, 0, childWidth, child.DesiredSize.Height));
            Point startPosition = new Point(endPosition.X, endPosition.Y + finalSize.Height);
            AnimatePosition(child, startPosition, endPosition, new TimeSpan(0, 0, 1));
            previousChildSize = child.DesiredSize;
        }
        return finalSize;
    }

    private void AnimatePosition(UIElement child, Point startPosition, Point endPosition, TimeSpan animationDuration)
    {
        DoubleAnimation xAnimation = new DoubleAnimation(startPosition.X, endPosition.X, animationDuration);
        DoubleAnimation yAnimation = new DoubleAnimation(startPosition.Y, endPosition.Y, animationDuration);
        TransformGroup transformGroup = child.RenderTransform as TransformGroup;
        if (transformGroup == null)
        {
            TranslateTransform translatationTransform = new TranslateTransform();
            transformGroup = new TransformGroup();
            transformGroup.Children.Add(translatationTransform);
            child.RenderTransform = transformGroup;
            child.RenderTransformOrigin = new Point(0, 0);
        }
        TranslateTransform translateTransform = (TranslateTransform)transformGroup.Children[0];
        translateTransform.BeginAnimation(TranslateTransform.XProperty, xAnimation, HandoffBehavior.Compose);
        translateTransform.BeginAnimation(TranslateTransform.YProperty, yAnimation, HandoffBehavior.Compose);
        //child.Arrange(new Rect(endPosition.X, endPosition.Y, child.DesiredSize.Width, child.DesiredSize.Height));
    }
}

AnimatedListBox.xaml 用户控件

<UserControl x:Class="WpfTest.Views.AnimatedListBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Controls="clr-namespace:WpfTest.Views.Controls">
    <UserControl.Resources>
        <ItemsPanelTemplate x:Key="AnimatedStackPanel">
            <Controls:AnimatedStackPanel />
        </ItemsPanelTemplate>
    </UserControl.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding Data}" ItemsPanel="{StaticResource AnimatedStackPanel}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
    </Grid>
</UserControl>

AnimatedListBox.xaml.cs UserControl 代码隐藏

public partial class AnimatedListBox : UserControl
{
    public AnimatedListBox()
    {
        InitializeComponent();
        Data = new ObservableCollection<int>();
        // change this 150 below to anything less than 142 and the problem disappears!!
        for (int count = 1; count <= 150; count++) Data.Add(count);
        DataContext = this;
    }

    public static DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(ObservableCollection<int>), typeof(AnimatedListBox));

    public ObservableCollection<int> Data
    {
        get { return (ObservableCollection<int>)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }
}

在实验过程中,我发现了一些看起来很奇怪的东西。如果我将 Panel.ArrangeOverride 方法中项目的位置更改为以下内容,则会出现更多意想不到的结果。

child.Arrange(new Rect(endPosition.X, endPosition.Y, childWidth, child.DesiredSize.Height));

不知何故,这改变了实际(动画)的结束位置,即。项目最终被定位的地方。它引入了某种双倍间距的项目。在开始动画之前将项目安排在其结束位置如何改变其最终结束位置?我是否遗漏了一些明显的东西?

此外,如果您取消注释 AnimatedStackPanel.AnimatePosition 方法中的注释行并注释掉上面的两行(即剪切动画并将项目直接移动到与动画会将它们移动到)然后你会看到问题再次消失......无论集合中有多少项目,这都适用。这就是让我认为问题与动画相关的原因。

我发现的最后一件事是无论是否使用 DataTemplate 作为项目数据类型都存在问题...我很确定它与动画相关。如果有人能看到我做错了什么,或者找到解决方案,我将非常感激,因为这个让我感到困惑。谁能用示例代码重现这个问题?非常感谢。

最佳答案

Sheridan,我稍微研究了一下您的代码示例,发现何时您应用 RenderTransform 显然很重要。我没有在 AnimatePosition 中分配它,即在 ArrangeOverride 的第一次运行期间,而是将代码移动到 MeasureOverride:

protected override Size MeasureOverride(Size availableSize)
{
    double x = 0, y = 0;
    foreach (UIElement child in Children)
    {
        TransformGroup transformGroup = child.RenderTransform as TransformGroup;
        if (transformGroup == null)
        {
            TranslateTransform translatationTransform = new TranslateTransform();
            transformGroup = new TransformGroup();
            transformGroup.Children.Add(translatationTransform);
            child.RenderTransform = transformGroup;
            child.RenderTransformOrigin = new Point(0, 0);
        }

        child.Measure(availableSize);
        x = Math.Max(x, availableSize.Width == double.PositiveInfinity ? child.DesiredSize.Width : availableSize.Width);
        y += child.DesiredSize.Height;
    }
    return new Size(
        availableSize.Width == double.PositiveInfinity ? x : availableSize.Width,
        availableSize.Height == double.PositiveInfinity ? y : availableSize.Height);
}

现在,即使在第一次调用 ArrangeOverride 时,动画也会按预期运行。似乎最后几个 RenderTransforms 尚未附加到控件,但已经设置了动画。

顺便说一句,我在运行 32 位 Windows 7 的两个不同系统(双核和四核)上的最大数量是 144。

关于c# - 141 是可以同时设置动画的 WPF 面板项的最大数量吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9049610/

相关文章:

c# - 如何在特定位置覆盖 WPF 控件

swift - 使节点以相同的速度从不同的起点移动到终点

javascript - Div 出现在不同 div 的悬停上

android - 自定义动画不适用于 SingleInstance Activity

c# - 两个用户/两个 session 同时从一台 PC 到一个站点?

c# - ServiceStack.Text 和 ISODate ("")

c# - 全局命名空间 C# 中的命名空间

c# - Unity : Need to reset a pooled object on return to pool. 也许使用 ScriptableObject?

wpf - 使用XAML完成Storyboard时将元素的可见性设置为“折叠”

c# - WPF 应用程序的内容调整大小事件