我在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
<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
s作为项目数据类型,问题都存在......我很确定它与动画有关。如果有人能够看到我做错了什么,或者找到解决方案,我会非常感激,因为这个让我感到困惑。有人甚至可以用示例代码重现这个问题吗?非常感谢提前。
答案 0 :(得分:4)
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。