2012-06-08 25 views
14

我正在處理這個表面項目,我們有一個bing地圖控件,並且我們想通過使用數據綁定在地圖上繪製多段線。WPF bing地圖控制首先添加到集合的多義線/多邊形

發生的奇怪行爲是當我點擊添加按鈕時,地圖上沒有任何反應。如果我稍微移動地圖,多段線就會繪製在地圖上。這種類型的另一種場景是單擊添加按鈕一次,沒有任何反應,再次單擊它就會繪製兩條折線。 (在我的手動集合中,我有4個LocationCollections),所以第三次單擊和第四次單擊都會發生同樣的情況,即再次繪製兩條線。

我完全不知道在哪裏再來解決這個問題。我嘗試訂閱Layoutupdated事件,這兩種情況都會發生。還將一個collectionchanged事件添加到observablecollection,以查看添加是否被觸發,並且是否觸發。我嘗試的另一件事是將折線更改爲圖釘,並從pipelineviewmodel中的位置集合中獲取第一個位置,而不是預期的工作。

我已經上傳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 :

Pipelin eViewModel

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

我不能幫你這個但測試你的樣本項目;做了一些試驗和錯誤的東西(無效,強制移動地圖),但也沒有線索爲什麼它不工作。你實施的一切看起來都很好。 但是,這裏有一些發現:從CodeBehind添加MapPolyline工作正常。如果您使用另一個像Pushpin的元素,它也可以正常工作。所以這個問題與所有從MapShapeBase繼承的東西有關。那就是MapPolyline和MapPolygon。我通過Reflector查看了它,並嘗試將Pushpin實現與MapPolyline實現進行比較。 – SvenG

+0

我不能投入更多時間,但如果我能夠調試反射代碼,並看看爲什麼Pushpin刷新正確,但MapPolyLine/MapPolygon不是。 – SvenG

+0

嗨SvenG,感謝您的時間看着它。是的,我已經看到,圖釘工作正常。我也在MapItemsControl上調用了方法UpdateLayout(),並向圖層添加了一個空的UIElement,它將顯示折線。仍然hav eno線索爲什麼它不工作:( – ChristiaanV

回答

14

我做了一些挖掘在這個問題上,發現在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計算在MapShapeBase並做計算的部分看起來像這樣

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

最初,_NormalizedMercatorToViewport將其默認值(一切都被設置爲0)這樣的計算雲都是錯的。 _NormalizedMercatorToViewport在方法SetView中設置,該方法從MeasureOverride中調用MapLayer

MeasureOverride in MapLayer有以下兩條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不會被調用,並在_NormalizedMercatorToViewportMapShapeBase有其默認值,並計算出了問題,當你添加新項目的第一次。


解決方法

要解決這個問題,我們需要強制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); 
    } 
} 
+0

@ChristiaanV:任何運氣與我建議的解決方法?如果您有任何關於「bug」的問題,我很高興以我的方式來看,問題不在於你的實現,而在於微軟實現了'Map',我認爲這是一個需要在他們的最終和唯一方法上解決的問題直到他們這樣做才能使用某種解決方法 –

+0

哇,這是一個很好的答案!真的很滿意! – ChristiaanV

+0

我一直在尋找解決這個問題一段時間,雖然有各種各樣的這是迄今爲止唯一可靠的工作,但有一件事,可能是我錯誤地構建了我的XAML,但是如果將MapItemsControl放置在MapLayer中,那麼修復程序不起作用。 lly需要多層爲我正在做的事情。 –

相關問題