【发布时间】:2013-09-19 00:01:00
【问题描述】:
这个问题与a question I recently posted直接相关,但我觉得方向已经发生了足够的变化,需要一个新的问题。我试图找出在画布上实时移动大量图像的最佳方法。我的 XAML 目前看起来像这样:
<UserControl.Resources>
<DataTemplate DataType="{x:Type local:Entity}">
<Canvas>
<Image Canvas.Left="{Binding Location.X}"
Canvas.Top="{Binding Location.Y}"
Width="{Binding Width}"
Height="{Binding Height}"
Source="{Binding Image}" />
</Canvas>
</DataTemplate>
</UserControl.Resources>
<Canvas x:Name="content"
Width="2000"
Height="2000"
Background="LightGreen">
<ItemsControl Canvas.ZIndex="2" ItemsSource="{Binding Entities}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
实体类:
[Magic]
public class Entity : ObservableObject
{
public Entity()
{
Height = 16;
Width = 16;
Location = new Vector(Global.rand.Next(800), Global.rand.Next(800));
Image = Global.LoadBitmap("Resources/Thing1.png");
}
public int Height { get; set; }
public int Width { get; set; }
public Vector Location { get; set; }
public WriteableBitmap Image { get; set; }
}
移动对象:
private Action<Entity> action = (Entity entity) =>
{
entity.Location = new Vector(entity.Location.X + 1, entity.Location.Y);
};
void Timer_Tick(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
foreach (var entity in Locator.Container.Entities)
{
action(entity);
}
});
}
如果我在Entities 集合中的条目少于大约 400 个,则移动是平稳的,但我希望能够将这个数字增加相当多。如果我超过 400,运动会变得越来越不稳定。起初我认为这是运动逻辑的问题(在这一点上这并不是什么大问题),但我发现这不是问题。我添加了另一个包含 10,000 个条目的集合,并将该集合添加到与第一个集合相同的计时器循环中,但未将其包含在 XAML 中,并且 UI 的反应没有任何不同。然而,我觉得奇怪的是,如果我在集合中添加 400 个条目,然后在 Image 设置为 null 的情况下再添加 400 个条目,即使有一半的项目没有被绘制,移动也会变得不稳定。
那么,如果有的话,我该怎么做才能在画布上绘制和平滑移动更多图像?这是我可能想回避 WPF 和 XAML 的情况吗?如果您需要更多代码,我很乐意发布。
更新:根据 Clemens 的建议,我的 Entity DataTemplate 现在看起来像这样:
<DataTemplate DataType="{x:Type local:Entity}">
<Image Width="{Binding Width}"
Height="{Binding Height}"
Source="{Binding Image}">
<Image.RenderTransform>
<TranslateTransform X="{Binding Location.X}" Y="{Binding Location.Y}" />
</Image.RenderTransform>
</Image>
</DataTemplate>
使用它可能会提高性能,但如果有的话,它是非常微妙的。另外,我注意到如果我使用 DispatcherTimer 循环并将其设置为:
private DispatcherTimer dTimer = new DispatcherTimer();
public Loop()
{
dTimer.Interval = TimeSpan.FromMilliseconds(30);
dTimer.Tick += Timer_Tick;
dTimer.Start();
}
void Timer_Tick(object sender, EventArgs e)
{
foreach (var entity in Locator.Container.Entities)
{
action(entity);
}
}
... 即使有几千个项目,移动也很流畅,但是很慢,无论间隔如何。如果使用 DispatcherTimer 并且 Timer_Tick 看起来像这样:
void Timer_Tick(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
foreach (var entity in Locator.Container.Entities)
{
action(entity);
}
});
}
... 动作非常不稳定。我觉得奇怪的是Stopwatch 表明如果有 5,000 个条目,Task.Factory 需要 1000 到 1400 个滴答来迭代集合。标准的foreach 循环需要超过 3,000 个滴答声。为什么Task.Factory 的速度是原来的两倍?是否有不同的方法来遍历集合和/或不同的计时方法,可以允许平稳移动而不会出现任何重大减速?
更新:如果有人可以帮助我提高画布上对象实时移动的性能,或者可以在 WPF 中提出另一种实现类似结果的方法,100 赏金等待着。
【问题讨论】:
-
如果你的图片绑定为空,你会得到绑定错误。在调试器中运行时,这些错误会大大降低整体性能。确保在
DataTemplate中添加DataTrigger以避免这种情况。 -
即使没有将图像绑定为 null,在集合中的大约 400 个项目之后,我仍然开始受到明显的性能影响。从我的代码来看,我可以做些什么来增加屏幕上的动态图像数量,同时保持一切顺利运行?
-
也许这是疯话,但你为什么要移动图像而不是容器?图像是否单独移动?
-
最终,我打算让集合中的每个项目能够执行不同的任务。我现在只有非常简单的逻辑,不要把任何事情复杂化。
-
您是否有正在运行的示例重现我们可以解决的问题?