2016-06-30 1 views
1

J'utilise une branche orientée MVVM de la bibliothèque GreatMaps.NET (https://greatmaps.codeplex.com/), mais ma question se résume à un problème de base avec l'utilisation d'un Canvas personnalisé en tant que ItemsPanel pour un ItemsControl. Essentiellement, la carte est un ItemsControl, avec un MapCanvas plaçant ses enfants comme suit:ArrangeOverride n'est pas appelé sur les enfants?

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

Cela fonctionne parce que mon ItemContainerStyle se lie à Latitude et Longitude dans le ViewModel pour chaque élément de la carte comme suit (avec ZIndex, que je ont mis à 99 comme un espace réservé):

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

J'utilise DataTemplates avec des champs de DataType pour ajuster la façon dont chaque élément s'affiche, et cette partie fonctionne correctement.

Il y a des éléments (tels que des lignes pour les routes sur la carte) que je n'ai pas besoin d'éléments ponctuels mais de séquences de sous-éléments. Pour ce faire, j'ai simplement suivi le modèle, et fait un DataTemplate où j'utilise un autre MapCanvas lié au même MapControl que son propriétaire (ici, MapOverlay est juste une copie de MapCanvas qui substitue OnRender pour tracer des lignes entre ses enfants):

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

pour garder les liaisons de travail, le RouteViewModel a encore sa propre Lat/Lng, qui semblent fonctionner que lorsque je leur position au point initial de la liste des points le long de la route:

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

En résumé:

  • J'ai une commande ItemsControl GMapControl utilisant un MapCanvas pour positionner des éléments géospatialement. DataTypes sont utilisés pour sélectionner quel DataTemplate est utilisé pour chaque élément. Cela fonctionne pour les éléments basés sur des points tels que les marqueurs.
  • L'un des DataTemplates est un ItemsControl utilisant encore une autre variante de MapCanvas (MapOverlay) en tant que ItemsPanel pour tracer des lignes entre les sous-éléments. Cela fonctionne au lancement, lorsque son ArrangeOverride s'exécute.
  • L'affichage de la carte fonctionne, mais ArrangeOverride n'est pas appelé dans MapOverlay.

Le problème: alors que mes éléments apparaissent initialement positionnés correctement, zoom sur la carte (ce qui est le déclenchement d'une InvalidateVisual et UpdateLayout via le contrôle de la carte) ne provoque pas ArrangeOverride à appeler sur le MapOverlay imbriquée. Cela ne permet plus de positionner la route correctement - elle ne change pas d'échelle.

Avez-vous des suggestions sur les raisons pour lesquelles l'invalidation d'organisation ne se répercute pas sur MapOverlay imbriqué et/ou sur des astuces pour résoudre ce problème? La route a été placée correctement lorsque la carte a été initialement positionnée sur son premier élément - la latitude/longitude de la route dans son ensemble est une question distincte sur laquelle j'accueillerais des idées.

+0

Si vous êtes intéressé, je pourrais vous devriez comment afficher facilement un itinéraire (c'est-à-dire des cercles connectés avec des lignes) avec mon [XAML MapControl] (http://xamlmapcontrol.codeplex.com/). MVVM bien sûr. – Clemens

+0

J'ai vu votre projet mentionné - ce serait très utile! Je vous remercie! – Methalous

+0

Comment bourdonnez-vous? J'utiliserais un TransformGroup dans RenderTransform pour la racine de la carte qui a un ScaleTransform et TranslateTransform qui contrôle le zoom et le panoramique. Comme cela serait fait pendant la passe de rendu, cela n'affecterait pas du tout la mise en page. En outre, il existe un certain nombre de commandes de zoom disponibles à partir de différentes sources que vous pouvez utiliser pour contrôler cet aspect sans vous soucier de la mise en page dans votre carte. – Will

Répondre

0

En fait, ce n'est pas une réponse à votre question, mais cela peut vous donner une idée générale de la manière d'afficher un itinéraire, c'est-à-dire une collection de cercles reliés par des lignes.

L'exemple suivant utilise la bibliothèque XAML MapControl, mais peut très bien être implémenté en fonction de toute autre bibliothèque de mappes qui a une sorte de MapItemsControl (par exemple MS Bing Maps).

L'idée est d'avoir une collection de Location instances qui composent votre itinéraire. Vous auriez un MapPolyline et lierez sa propriété Locations à la collection de points de route. Puis mettez un MapItemsControl par dessus et liez le ItemsSource à la même collection. Notez que le ItemTemplate utilise des contrôles de chemin avec EllipseGeometries au lieu de Ellipses, car ils sont centrés à l'emplacement du point, alors que les ellipses sont alignés haut/gauche.

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

Si les points de la route ne sont pas Location s, vous pouvez utiliser des convertisseurs de liaison dans les deux liaisons pour convertir votre type de point de route vers Location.

+0

Salut Clemens, j'utilise maintenant votre XAMLMapControl et je trouve qu'il est assez facile de travailler avec. Cependant, j'ai plusieurs itinéraires sur ma carte - comment pourrais-je utiliser ce que vous venez de définir pour chaque élément d'une collection? – Methalous

+0

Je ne l'ai jamais essayé, mais vous pouvez probablement placer tout MapPanel à partir de ce post dans le ItemTemplate d'un autre MapItemsControl externe. – Clemens