如何改进Windows Phone 8地图控制性能

时间:2014-08-10 18:30:04

标签: c# performance windows-phone-8 asynchronous maps

在我的Windows Phone 8应用程序中,我有地图控件,其中包含一组平面(图钉)。

Map在xaml中的定义如下:

<maps:Map x:Name="Map">
    <maptk:MapExtensions.Children>

        <maptk:MapItemsControl Name="Planes" ItemTemplate="{StaticResource PlaneTemplate}"/>

    </maptk:MapExtensions.Children>
</maps:Map>

Planes 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获取飞机并更新集合,整个应用程序将释放几秒钟。通常情况下,Planes collectoin中会有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>

似乎图钉模板对性能影响很大。如果我从图钉中删除我自己的控件模板,这样可以获得更快的速度。

2 个答案:

答案 0 :(得分:2)

当您使用WPToolkit的MapExtension显示图钉时,您正在寻找麻烦。您希望Binding ItemsSource到ObservableCollection<T>为您提供更简便的方式来更新您的地图以及更好的&amp;流畅的用户体验。

不,不!我引用(我自己):

  

WPToolkit的MapExtensions糟透了驴子的屁股。这是一种   组件,让你质疑为什么你开始编程   第一名。

可以ItemsSource绑定到集合,但您无法在XAML中执行此操作。不,你必须自己挖掘ItemsSource并在代码隐藏中明确设置它。

var control = MapExtensions.GetChildren(MyMap).OfType<MapItemsControl>().FirstOrDefault();

没那么糟糕,但等等,还有更多!如果集合中有项目,则不能只用新项目替换它,否则最终会出现异常。不,您必须清除这些项目,然后逐个添加新项目。恩,那就对了。一个接一个!

var planes = (await PlaneRepository.GetAllPlanesAsync()).ToList();

if (Planes.Any())
    Planes.Clear();

foreach (var plane in planes)
    Planes.Add(plane);

更新集合后,UI将被阻止。没有DispatcherBackgroundWorkerTask<T>可以解决这个问题。

如果有人有任何疑问,请务必指出我哪里出错了。我设置了一个public GitHub repo主分支)来分叉。

为了使地图无阻塞,你必须做一些妥协并完全抛弃MapExtensions。当您创建新的MapOverlay,将一个Pushpin作为内容,并将其手动添加到地图时,UI仍会保持响应。

但也有一个问题。如果你一次添加很多MapOverlays,那么你仍然可以暂时冻结。如果您故意延迟添加每个项目(例如,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的响应式地图示例位于in the same GitHub repo,但位于 no-map-extensions 分支。

imgur

这就是改善Windows Phone 8地图控制性能的方法:)。

答案 1 :(得分:-2)

因为你正在等待主线程。您应该使用BackgroundWorker关闭并获取平面,或者只是异步执行。然后更新UI。回到主线程非常简单。

Deployment.Current.Dispatcher.BeginInvoke(() =>
{
    //Stuff to do in UI thread
});