繁体   English   中英

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

[英]WPF bing maps control polylines/polygons not draw on first add to collection

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

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

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

我已经上传了一个示例项目 ,如果你想看看自己发生了什么。

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

您可以在下面找到我编写的代码:

我有以下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;
}

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

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

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


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

在此输入图像描述

当您向集合中添加新项时, Polygon最初会获得错误的Points 取而代之的是像0.0009, 0.00044这样的值59, 29它会得到像0.0009, 0.00044

这些点在MapShapeBase中的MeasureOverride中计算,执行计算的部分如下所示

MapMath.TryLocationToViewportPoint(ref this._NormalizedMercatorToViewport, location, out point2);

最初, _NormalizedMercatorToViewport将具有其默认值(一切都设置为0),因此计算完全错误。 _NormalizedMercatorToViewport在方法SetView设置,该方法在MapLayerMeasureOverride调用。

MapLayer中的MeasureOverride具有以下两个if语句。

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 当将新的ContentPresenter添加到MapItemsControl但在ContentPresenter具有可视子项之后,必须执行此操作。

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

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

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

MapFixBehavior

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;
    }
}

MapLayerExtensions

public static class MapLayerExtensions
{
    private static DependencyProperty ForceMeasureProperty =
        DependencyProperty.RegisterAttached("ForceMeasure",
                                            typeof(int),
                                            typeof(MapLayerExtensions),
                                            new FrameworkPropertyMetadata(0,
                                                FrameworkPropertyMetadataOptions.AffectsRender |
                                                FrameworkPropertyMetadataOptions.AffectsArrange |
                                                FrameworkPropertyMetadataOptions.AffectsMeasure));

    private static int GetForceMeasure(DependencyObject mapLayer)
    {
        return (int)mapLayer.GetValue(ForceMeasureProperty);
    }
    private static void SetForceMeasure(DependencyObject mapLayer, int value)
    {
        mapLayer.SetValue(ForceMeasureProperty, value);
    }

    public static void ForceMeasure(this MapLayer mapLayer)
    {
        SetForceMeasure(mapLayer, GetForceMeasure(mapLayer) + 1);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM