2016-06-30 19 views
1

Ich verwende einen MVVM-orientierten Fork der GreatMaps.NET-Bibliothek (https://greatmaps.codeplex.com/), aber meine Frage geht auf ein grundlegendes Problem mit einem benutzerdefinierten Canvas als ItemsPanel für ein ItemsControl. Im Wesentlichen ist die Karte ein Itemscontrol, mit einem MapCanvas seine Kinder setzen sich wie folgt:ArrangeOverride wird nicht an Kinder gerufen?

protected override Size ArrangeOverride(Size arrangeSize) 
{ 
    foreach (UIElement child in Children) 
    { 
     PointLatLng position = new PointLatLng(GetTop(child), GetLeft(child)); 

     GMapControl map = Owner as GMapControl; 
     if (map != null) 
     { 
      GPoint p = map.FromLatLngToLocal(position); 
      p.Offset(-(long)(map.MapTranslateTransform.X + child.DesiredSize.Width * 0.5), -(long)(map.MapTranslateTransform.Y + child.DesiredSize.Height * 0.5)); 

      Rect rect = new Rect(p.X, p.Y, child.DesiredSize.Width, child.DesiredSize.Height); 
      child.Arrange(rect); 
     } 
    } 
    return arrangeSize; 
} 

Das funktioniert, weil mein ItemContainerStyle im Ansichtsmodell für jedes Kartenelement Breiten- und Längen bindet wie folgt (zusammen mit ZIndex, die ich haben bis 99 als Platzhalter eingestellt):

<Setter Property="Canvas.Left" Value="{Binding Longitude}" /> 
<Setter Property="Canvas.Top" Value="{Binding Latitude}" /> 
<Setter Property="Canvas.ZIndex" Value="{Binding ZIndex}" /> 

I Datatemplates mit Feldern des Datentyps anzupassen bin mit, wie jedes Element angezeigt wird, und dass ein Teil richtig funktioniert.

Es gibt Elemente (z. B. Linien für Routen auf der Karte), die ich nicht als Punktelemente, sondern als Folgen von Unterelementen haben muss. Dazu folgte ich einfach dem Muster und erstellte ein DataTemplate, in dem ich ein anderes MapCanvas verwendete, das an dasselbe MapControl gebunden war wie der Besitzer (MapOverlay ist nur eine Kopie von MapCanvas, die OnRender überschreibt, um Linien zwischen den untergeordneten Elementen zu zeichnen):

<DataTemplate DataType="{x:Type viewModels:RouteViewModel}"> 
    <ItemsControl ItemsSource="{Binding Locations}"> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <wpf:MapOverlay Name="MapOverlay" Owner="{Binding Path=., RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type wpf:GMapControl}}}"/> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <ItemsControl.ItemContainerStyle> 
      <Style> 
       <Setter Property="Canvas.Left" Value="{Binding Longitude}" /> 
       <Setter Property="Canvas.Top" Value="{Binding Latitude}" /> 
       <Setter Property="Canvas.ZIndex" Value="{Binding ZIndex}" /> 
      </Style> 
     </ItemsControl.ItemContainerStyle> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <Ellipse Fill="Blue" Stroke="Black" Width="10" Height="10"></Ellipse> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</DataTemplate> 

um die Bindungen weiter arbeitet, der RouteViewModel noch seine eigenen Lat/Lng hat, das nur zu arbeiten scheint, wenn ich sie in dem Anfangspunkt der Liste der Punkte entlang der Route positionieren:

public double Latitude => Locations.Select(loc => loc.Latitude).FirstOrDefault(); 
public double Longitude => Locations.Select(loc => loc.Longitude).FirstOrDefault(); 

In Zusammenfassung:

  • Ich habe ein GMapControl ItemsControl mit einem MapCanvas, um Elemente geografisch zu positionieren. DataTypes werden verwendet, um auszuwählen, welches DataTemplate für jedes Element verwendet wird. Dies funktioniert für punktbasierte Elemente wie Marker.
  • Eines der DataTemplates ist ein ItemsControl, das eine weitere MapCanvas-Variante (MapOverlay) als ItemsPanel zum Zeichnen von Linien zwischen Unterelementen verwendet. Dies funktioniert beim Start, wenn ArrangeOverride ausgeführt wird.
  • Das Verschieben der Karte funktioniert, obwohl ArrangeOverride nicht in MapOverlay aufgerufen wird.

Das Problem: während meine Elemente zunächst richtig positioniert erscheinen, Zoomen der Karte (die eine InvalidateVisual und UpdateLayout über die Karte Steuerung auslöst) verursacht keine ArrangeOverride auf der verschachtelten MapOverlay aufgerufen werden. Dadurch wird die Route nicht mehr richtig positioniert - sie wird nicht skaliert.

Irgendwelche Vorschläge, warum die Ungültigkeit der Anordnung nicht auf das verschachtelte MapOverlay hinunterrieselt und/oder Tipps, wie man es beheben kann?

Nachtrag: Die Route wurde nur korrekt platziert, wenn die Karte anfänglich auf ihrem ersten Element positioniert wurde - die Breite/Länge der Route als Ganzes ist ein separates Problem, das ich gerne begrüßen würde.

+0

Wenn Sie interessiert sind, könnte ich Sie sollten eine Route (d. H. Kreise mit Linien verbunden) mit meinem [XAML MapControl] (http://xamlmapcontrol.codeplex.com/) leicht anzuzeigen. MVVM natürlich. – Clemens

+0

Ich habe Ihr Projekt erwähnt - das wäre sehr hilfreich! Vielen Dank! – Methalous

+0

Wie zoomen Sie? Ich würde eine TransformGroup in der RenderTransform für den Stamm der Map verwenden, die über eine ScaleTransform- und TranslateTransform-Funktion verfügt, die das Zoomen und Verschieben steuert. Wie beim Render-Durchlauf würde dies das Layout überhaupt nicht beeinflussen. Außerdem stehen verschiedene Zoomsteuerungen aus verschiedenen Quellen zur Verfügung, mit denen Sie diesen Aspekt steuern können, ohne sich um das Layout in Ihrer Karte kümmern zu müssen. – Will

Antwort

0

Nicht eigentlich eine Antwort auf Ihre Frage, aber es kann Ihnen eine allgemeine Idee, wie man eine Route, d. H. Eine Sammlung von Kreisen durch Linien verbunden zeigen.

Im folgenden Beispiel wird die XAML MapControl-Bibliothek verwendet, sie kann jedoch basierend auf einer anderen Kartenbibliothek mit einer Art MapItemsControl (z. B. MS Bing Maps) implementiert werden.

Die Idee ist eine Sammlung von Location Instanzen, die Ihre Route ausmachen. Sie hätten eine MapPolyline und binden ihre Locations-Eigenschaft an die Routenpunkte-Sammlung. Dann legen Sie eine MapItemsControl darüber und binden Sie seine ItemsSource an die gleiche Sammlung. Beachten Sie, dass ItemTemplate Path-Steuerelemente mit EllipseGeometries anstelle von Ellipsen verwendet, da sie an der Punktposition zentriert sind, während Ellipsen oben/links ausgerichtet sind.

<map:MapPanel> 

    <map:MapPolyline Locations="{Binding RouteLocations}" 
        Stroke="DarkBlue" StrokeThickness="3"/> 

    <map:MapItemsControl ItemsSource="{Binding RouteLocations}"> 
     <map:MapItemsControl.ItemContainerStyle> 
      <Style TargetType="map:MapItem"> 
       <Setter Property="map:MapPanel.Location" Value="{Binding}"/> 
      </Style> 
     </map:MapItemsControl.ItemContainerStyle> 
     <map:MapItemsControl.ItemTemplate> 
      <DataTemplate> 
       <Path Fill="Red"> 
        <Path.Data> 
         <EllipseGeometry RadiusX="5" RadiusY="5"/> 
        </Path.Data> 
       </Path> 
      </DataTemplate> 
     </map:MapItemsControl.ItemTemplate> 
    </map:MapItemsControl> 

</map:MapPanel> 

Wenn die Routenpunkte sind nicht Location s können Sie verbindlich Wandler in beiden Bindungen verwenden, um von Ihrem Routenpunkt Typ Location zu konvertieren.

+0

Hallo Clemens, ich benutze jetzt dein XAMLMapControl und finde es sehr einfach damit zu arbeiten. Ich habe jedoch mehrere Routen auf meiner Karte - wie würde ich das verwenden, was Sie für jedes Element einer Sammlung definiert haben? – Methalous

+0

Ich habe es nie versucht, aber Sie können wahrscheinlich das gesamte MapPanel von diesem Beitrag in der ItemTemplate eines anderen, äußeren MapItemsControl setzen. – Clemens