WPF bing贴图控制折线/多边形在第一次添加到集合时不绘制

时间:2012-06-08 14:07:06

标签: c# wpf xaml bing-maps polyline

我正在研究这个表面项目,我们有一个bing贴图控件,我们希望通过数据绑定在地图上绘制折线。

发生的奇怪行为是,当我点击“添加”按钮时,地图上没有任何反应。如果我稍微移动地图,则会在地图上绘制折线。另一种有效的方案是单击添加按钮一次,没有任何反应,再次单击它会绘制两条折线。 (在我的手册集中,我有4个LocationCollections),因此第3次点击和第4次点击同样会发生两条线的绘制。

我完全不知道在哪里可以解决这个问题。我已经尝试订阅Layoutupdated事件,这两种情况都会发生。还向observablecollection添加了一个collectionchanged事件,以查看是否触发了add,并且是触发了它。我尝试的另一件事是将折线更改为图钉并从管道视图模型中的位置集合中获取第一个位置,而不是预期的工作。

如果您想了解自己发生了什么,我已经上传了sample project

真的希望有人能指出我正确的方向,因为我不再有线索了。

下面你会找到我写的代码:

我有以下viewmodels:

MainViewModel

public class MainViewModel
{
    private ObservableCollection<PipelineViewModel> _pipelines;

    public ObservableCollection<PipelineViewModel> Pipes
    {
        get { return _pipelines; }
    }

    public MainViewModel()
    {
        _pipelines = new ObservableCollection<PipelineViewModel>();
    }
}

PipelineViewModel,它包含实现INotifyPropertyChanged的Locations集合:

PipelineViewModel

public class PipelineViewModel : ViewModelBase
{
    private LocationCollection _locations;

    public string Geometry { get; set; }
    public string Label { get; set; }
    public LocationCollection Locations
    {
        get { return _locations; }
        set
        {
            _locations = value;
            RaisePropertyChanged("Locations");
        }
    }
}

我的XAML如下所示:

<s:SurfaceWindow x:Class="SurfaceApplication3.SurfaceWindow1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl.WPF;assembly=Microsoft.Maps.MapControl.WPF" 
    Title="SurfaceApplication3">
    <s:SurfaceWindow.Resources>
        <DataTemplate x:Key="Poly">
            <m:MapPolyline Locations="{Binding Locations}" Stroke="Black" StrokeThickness="5" />
        </DataTemplate>
    </s:SurfaceWindow.Resources>
  <Grid>
        <m:Map ZoomLevel="8" Center="52.332074,5.542302" Name="Map">
            <m:MapItemsControl Name="x" ItemsSource="{Binding Pipes}" ItemTemplate="{StaticResource Poly}" />
        </m:Map>
        <Button Name="add" Width="100" Height="50" Content="Add" Click="add_Click"></Button>
    </Grid>
</s:SurfaceWindow>

在我们的代码隐藏中,我们正在设置绑定和click事件,如下所示:

private int _counter = 0;
private string[] geoLines;

private MainViewModel _mainViewModel = new MainViewModel();

/// <summary>
/// Default constructor.
/// </summary>
public SurfaceWindow1()
{
    InitializeComponent();

    // Add handlers for window availability events
    AddWindowAvailabilityHandlers();

    this.DataContext = _mainViewModel;

    geoLines = new string[4]{ "52.588032,5.979309; 52.491143,6.020508; 52.397391,5.929871; 52.269838,5.957336; 52.224435,5.696411; 52.071065,5.740356",
                                "52.539614,4.902649; 52.429222,4.801025; 52.308479,4.86145; 52.246301,4.669189; 52.217704,4.836731; 52.313516,5.048218",
                                "51.840869,4.394531; 51.8731,4.866943; 51.99841,5.122375; 52.178985,5.438232; 51.8731,5.701904; 52.071065,6.421509",
                                "51.633362,4.111633; 51.923943,6.193542; 52.561325,5.28717; 52.561325,6.25946; 51.524125,5.427246; 51.937492,5.28717" };
}

private void add_Click(object sender, RoutedEventArgs e)
{
    PipelineViewModel plv = new PipelineViewModel();
    plv.Locations = AddLinestring(geoLines[_counter]);
    plv.Geometry = geoLines[_counter];

    _mainViewModel.Pipes.Add(plv);

    _counter++;
}

private LocationCollection AddLinestring(string shapegeo)
{
    LocationCollection shapeCollection = new LocationCollection();

    string[] lines = Regex.Split(shapegeo, ";");
    foreach (string line in lines)
    {
        string[] pts = Regex.Split(line, ",");

        double lon = double.Parse(pts[1], new CultureInfo("en-GB"));
        double lat = double.Parse(pts[0], new CultureInfo("en-GB"));
        shapeCollection.Add(new Location(lat, lon));
    }

    return shapeCollection;
}

1 个答案:

答案 0 :(得分:15)

我对这个问题进行了一些挖掘,发现Map实现中存在一个错误。我也为它做了一个解决方法,可以像这样使用

<m:Map ...>
    <m:MapItemsControl Name="x"
                       behaviors:MapFixBehavior.FixUpdate="True"/>
</m:Map>

我在您的示例应用程序中包含了此修复程序,并在此处上传:SurfaceApplication3.zip


每个ContentPresenter的可视化树看起来像这样

enter image description here

当您向集合中添加新项目时,Polygon最初会得到错误的Points。而不是像59, 29这样的值,它会得到类似0.0009, 0.00044的内容。

这些点在MeasureOverride的{​​{1}}中计算,计算的部分如下所示

MapShapeBase

最初,MapMath.TryLocationToViewportPoint(ref this._NormalizedMercatorToViewport, location, out point2); 的默认值(一切都设置为0),因此计算完全错误。 _NormalizedMercatorToViewport设置在_NormalizedMercatorToViewport SetViewMeasureOverride调用的方法{/ 1}}。

MapLayer中的

MeasureOverride包含以下两个if语句。

MapLayer

这是if ((element is ContentPresenter) && (VisualTreeHelper.GetChildrenCount(element) > 0)) { child.SetView(...) } ,因为false还没有视觉孩子,它仍在生成。 这是问题

第二个看起来像这样

ContentPresenter

这也是IProjectable projectable2 = element as IProjectable; if (projectable2 != null) { projectable2.SetView(...); } ,因为false元素不会实现ContentPresenter。这是由孩子IProjectable实现的,而且这个孩子还没有生成。

因此,MapShapeBase永远不会被调用,SetView中的_NormalizedMercatorToViewport将具有其默认值,并且在您添加新项目时第一次计算出错。


解决方法

要解决此问题,我们需要强制重新衡量MapShapeBase。当MapLayer添加到ContentPresenterMapItemsControl有视觉孩子后

}时,必须执行此操作。

强制更新的一种方法是创建一个附加属性,其元数据标志ContentPresenterAffectsRenderAffectsArrange设置为true。然后我们每次只想更新时都会更改此属性的值。

这是执行此操作的附加行为。像这样使用它

AffectsMeasure

MapFixBehavior

<m:Map ...>
    <m:MapItemsControl Name="x"
                       behaviors:MapFixBehavior.FixUpdate="True"/>
</m:Map>

MapLayerExtensions

public class MapFixBehavior
{
    public static DependencyProperty FixUpdateProperty =
        DependencyProperty.RegisterAttached("FixUpdate",
                                            typeof(bool),
                                            typeof(MapFixBehavior),
                                            new FrameworkPropertyMetadata(false,
                                                                          OnFixUpdateChanged));

    public static bool GetFixUpdate(DependencyObject mapItemsControl)
    {
        return (bool)mapItemsControl.GetValue(FixUpdateProperty);
    }
    public static void SetFixUpdate(DependencyObject mapItemsControl, bool value)
    {
        mapItemsControl.SetValue(FixUpdateProperty, value);
    }

    private static void OnFixUpdateChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        MapItemsControl mapItemsControl = target as MapItemsControl;
        ItemsChangedEventHandler itemsChangedEventHandler = null;
        itemsChangedEventHandler = (object sender, ItemsChangedEventArgs ea) =>
        {
            if (ea.Action == NotifyCollectionChangedAction.Add)
            {
                EventHandler statusChanged = null;
                statusChanged = new EventHandler(delegate
                {
                    if (mapItemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                    {
                        mapItemsControl.ItemContainerGenerator.StatusChanged -= statusChanged;
                        int index = ea.Position.Index + ea.Position.Offset;
                        ContentPresenter contentPresenter =
                            mapItemsControl.ItemContainerGenerator.ContainerFromIndex(index) as ContentPresenter;
                        if (VisualTreeHelper.GetChildrenCount(contentPresenter) == 1)
                        {
                            MapLayer mapLayer = GetVisualParent<MapLayer>(mapItemsControl);
                            mapLayer.ForceMeasure();
                        }
                        else
                        {
                            EventHandler layoutUpdated = null;
                            layoutUpdated = new EventHandler(delegate
                            {
                                if (VisualTreeHelper.GetChildrenCount(contentPresenter) == 1)
                                {
                                    contentPresenter.LayoutUpdated -= layoutUpdated;
                                    MapLayer mapLayer = GetVisualParent<MapLayer>(mapItemsControl);
                                    mapLayer.ForceMeasure();
                                }
                            });
                            contentPresenter.LayoutUpdated += layoutUpdated;
                        }
                    }
                });
                mapItemsControl.ItemContainerGenerator.StatusChanged += statusChanged;
            }
        };
        mapItemsControl.ItemContainerGenerator.ItemsChanged += itemsChangedEventHandler;
    }

    private static T GetVisualParent<T>(object childObject) where T : Visual
    {
        DependencyObject child = childObject as DependencyObject;
        while ((child != null) && !(child is T))
        {
            child = VisualTreeHelper.GetParent(child);
        }
        return child as T;
    }
}