14

我正在处理这个表面项目,我们有一个 bing 地图控件,并且我们希望通过使用数据绑定在地图上绘制折线。

发生的奇怪行为是,当我单击“添加”按钮时,地图上什么也没有发生。如果我稍微移动地图,多段线就会绘制在地图上。另一种可行的方案是单击一次添加按钮,没有任何反应,再次单击它,两条折线都被绘制了。(在我的手动收藏中,我有 4 个 LocationCollections)所以第三次点击和第四次点击再次绘制两条线时也会发生同样的情况。

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

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

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

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

我有以下视图模型:

主视图模型

public class MainViewModel
{
    private ObservableCollection<PipelineViewModel> _pipelines;

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

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

以及具有实现 INotifyPropertyChanged 的​​位置集合的 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>

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

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

1 回答 1

17

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

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

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


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

在此处输入图像描述

当您将新项目添加到集合中时,最初Polygon会出错。Points而不是像它这样的值59, 29,而是像0.0009, 0.00044.

点在MeasureOverridein中计算,进行计算MapShapeBase的部分如下所示

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

最初,_NormalizedMercatorToViewport将有其默认值(一切都设置为 0),因此计算完全错误。在从in调用_NormalizedMercatorToViewport的方法中设置。SetViewMeasureOverrideMapLayer

MeasureOverrideinMapLayer有以下两个 if 语句。

if ((element is ContentPresenter) && (VisualTreeHelper.GetChildrenCount(element) > 0))
{
    child.SetView(...)
}

这是false因为ContentPresenter还没有视觉孩子,它仍在生成中。这就是问题所在

第二个看起来像这样

IProjectable projectable2 = element as IProjectable;
if (projectable2 != null)
{
    projectable2.SetView(...);
}

false也是因为元素 aContentPresenter没有实现IProjectable. 这是由孩子实现的,MapShapeBase而且这个孩子还没有生成。

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


解决方法

要解决此问题,我们需要强制重新测量MapLayer. 这必须在新ContentPresenter添加到时MapItemsControl但在ContentPresenter具有视觉孩子之后完成。

强制更新的一种方法是创建一个具有 metadata-flags 的附加属性AffectsRenderAffectsArrange并将AffectsMeasure其设置为 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);
    }
}
于 2012-06-17T10:06:06.680 回答