在我的Windows Phone 8应用程序中,我具有包含平面(图钉)集合的地图控件。
像这样在xaml中定义地图:
<maps:Map x:Name="Map">
<maptk:MapExtensions.Children>
<maptk:MapItemsControl Name="Planes" ItemTemplate="{StaticResource PlaneTemplate}"/>
</maptk:MapExtensions.Children>
</maps:Map>
平面MapItemsControl绑定到视图模型中的对象的集合(ObservableCollection),如下所示:
private ObservableCollection<IPlane> _planes;
public ObservableCollection<IPlane> Planes
{
get { return _planes; }
set
{
_planes = value;
NotifyOfPropertyChange(() => Planes);
}
}
public async void GetPlanes()
{
IReadOnlyCollection<IPlane> planes = await realtimePlanes.GetAllPlanesAsync();
foreach (IPlane newPlane in planes)
{
this.Vehicles.Add(newPlane);
}
}
当我调用GetPlaes()时,要从Web api获取飞机并更新集合,整个应用程序将释放几秒钟。通常,“飞机”集合机中大约有150-300件物品。
在更新Planes集合时,应该如何改善它以使用户体验更好,消除滞后?
我可以以某种方式强制在后台线程(?)中进行此集合更新,还是问题图控件性能良好?它不能一口气绘制所有项目吗?
这是更多代码,说明我的实际应用程序如何工作。
只需构建快速而肮脏的测试类/服务即可生成测试平面:
public class TestRealtimePlaneService : IRealtimePlaneService
{
private Random random;
public async Task<IEnumerable<IRealtimePlane>> GetAllPlanesAsync()
{
this.random = new Random();
int count = 350;
double x0 = 24.9375;
double y0 = 60.170833;
int radius = 8000;
List<TestRealtimePlane> planes = new List<TestRealtimePlane>();
for (int i = 0; i < count; i++)
{
planes.Add(new TestRealtimePlane() { Location = getLocation(x0, y0, radius), Bearing = 1 });
}
await Task.Delay(5000); // Just to simulate webservice call
return planes;
}
// Taken from http://gis.stackexchange.com/questions/25877/how-to-generate-random-locations-nearby-my-location
public GeoCoordinate getLocation(double x0, double y0, int radius)
{
double radiusInDegrees = radius / 111000f;
double u = this.random.NextDouble();
double v = this.random.NextDouble();
double w = radiusInDegrees * Math.Sqrt(u);
double t = 2 * Math.PI * v;
double x = w * Math.Cos(t);
double y = w * Math.Sin(t);
double new_x = x / Math.Cos(y0);
double foundLongitude = new_x + x0;
double foundLatitude = y + y0;
return new GeoCoordinate(foundLatitude, foundLongitude);
}
}
这是实际的地图组件
<maps:Map x:Name="Map">
<maptk:MapExtensions.Children>
<maptk:MapItemsControl Name="Planes">
<maptk:MapItemsControl.ItemTemplate>
<DataTemplate>
<maptk:Pushpin GeoCoordinate="{Binding Location}" PositionOrigin="0.5,0.5">
<maptk:Pushpin.Template>
<ControlTemplate TargetType="maptk:Pushpin">
<Grid Width="45" Height="45" Background="Transparent">
<Polygon Fill="Yellow" Points="22,0 34,13, 12,13" Width="45" Height="45" RenderTransformOrigin="0.5,0.5">
<Polygon.RenderTransform>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="{Binding Bearing}"/>
</Polygon.RenderTransform>
</Polygon>
<Ellipse Fill="Yellow" Stroke="Black" HorizontalAlignment="Center" VerticalAlignment="Center" Width="15" Height="15" StrokeThickness="2" />
</Grid>
</ControlTemplate>
</maptk:Pushpin.Template>
</maptk:Pushpin>
</DataTemplate>
</maptk:MapItemsControl.ItemTemplate>
</maptk:MapItemsControl>
</maptk:MapExtensions.Children>
</maps:Map>
似乎图钉模板也对性能有很大影响。如果我从图钉中删除自己的控制模板,则其工作速度会加快一点。
当您使用WPToolkit的MapExtension显示图钉时,您正在寻找麻烦。您希望Binding
ItemsSource能够ObservableCollection<T>
为您提供简便的方法来更新地图,并提供流畅的用户体验。
一定不行!我引用(我自己):
WPToolkit的MapExtensions糟透了驴子的屁股。这种组件使您质疑为什么首先要进行编程。
您可以绑定ItemsSource
到集合,但不能在XAML中这样做。不,您必须自己挖掘ItemsSource,并在代码隐藏中明确设置它。
var control = MapExtensions.GetChildren(MyMap).OfType<MapItemsControl>().FirstOrDefault();
还不错,但是等等,还有更多!如果collection中有项目,则不能仅将其替换为新的项目,否则最终会出现异常。不,相反,您必须清除这些项目,然后逐个添加新项目。是的,这是对的。一对一!
var planes = (await PlaneRepository.GetAllPlanesAsync()).ToList();
if (Planes.Any())
Planes.Clear();
foreach (var plane in planes)
Planes.Add(plane);
当您更新集合时,UI将被阻止。没有Dispatcher
,BackgroundWorker
也Task<T>
无法解决这个问题。
如果有人有任何疑问,请务必指出我错了。我设置了一个公共GitHub存储库(master分支)作为分支。
为了使地图不被阻塞,您必须做出一些折衷并完全放弃MapExtensions。创建new时MapOverlay
,将其中一个Pushpin
作为内容,然后将其手动添加到地图中,UI会保持响应状态。
但是也有一个陷阱。如果您一次添加很多MapOverlay,那么您仍然会短暂冻结。如果您故意拖延添加每个项目(例如75毫秒),那么图钉会一一出现在地图上并且UI保持响应状态会产生很好的效果。
Task.Factory.StartNew(() =>
{
if (message.Delay != null)
{
foreach (var plane in message.Planes)
{
AddPins(new[] {plane});
Thread.Sleep(message.Delay.Value);
}
}
});
private void AddPins(IEnumerable<IPlane> planes)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
foreach (var plane in planes)
{
var pushpin = new Pushpin
{
Style = Resources["PlaneStyle"] as Style
};
var pushpinOverlay = new MapOverlay
{
GeoCoordinate = plane.Location,
Content = pushpin
};
_pushpinLayer.Add(pushpinOverlay);
}
});
}
使用MapOverlays的响应式地图示例位于同一GitHub存储库中,但位于no-map-extensions分支中。
这就是提高Windows Phone 8 Map控件性能的方法:)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句