【问题标题】:ArrangeOverride not being called on children?没有在孩子身上调用 ArrangeOverride?
【发布时间】:2016-06-30 14:27:18
【问题描述】:

我正在使用 GreatMaps.NET 库 (https://greatmaps.codeplex.com/) 的面向 MVVM 的分支,但我的问题归结为一个基本问题,即使用自定义 Canvas 作为 ItemsPanel 的 ItemsControl。本质上,地图是一个 ItemsControl,其中一个 MapCanvas 将其子项放置如下:

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

之所以有效,是因为我的 ItemContainerStyle 为每个地图项绑定到 ViewModel 中的纬度和经度,如下所示(连同 ZIndex,我已将其设置为 99 作为占位符):

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

我正在使用带有 DataType 字段的 DataTemplates 来调整每个元素的显示方式,并且该部分工作正常。

有些元素(例如地图上的路线线)我不需要作为点元素,而是作为子元素序列。为此,我只是按照模式制作了一个 DataTemplate,在其中我使用另一个 MapCanvas 绑定到与其所有者相同的 MapControl(这里,MapOverlay 只是 MapCanvas 的一个副本,它覆盖 OnRender 以在其子项之间绘制线条):

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

为了保持绑定正常工作,RouteViewModel 仍然有自己的 Lat/Lng,这似乎仅在我将它们定位在沿路线的点列表的初始点时才有效:

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

总结:

  • 我有一个 GMapControl ItemsControl,它使用 MapCanvas 对元素进行地理空间定位。 DataTypes 用于选择每个元素使用哪个 DataTemplate。这适用于标记等基于点的元素。
  • 其中一个 DataTemplates 是 ItemsControl,它使用另一个 MapCanvas 变体 (MapOverlay) 作为其 ItemsPanel,以在子元素之间绘制线条。这在启动时起作用,当它的 ArrangeOverride 执行时。
  • 虽然在 MapOverlay 中没有调用 ArrangeOverride,但可以平移地图。

问题:虽然我的元素最初显示正确定位,但缩放地图(通过地图控件触发 InvalidateVisual 和 UpdateLayout)不会导致在嵌套 MapOverlay 上调用 ArrangeOverride。这会导致路线不再正确定位 - 它无法缩放。

关于为什么排列失效没有渗透到嵌套的 MapOverlay 和/或有关如何修复它的提示的任何建议?

附录: 仅当地图最初位于其第一个元素时,路线才被正确放置 - 整个路线的纬度/经度是一个单独的问题,我欢迎提出意见.

【问题讨论】:

  • 如果您有兴趣,我可以告诉您如何使用我的XAML MapControl 轻松显示路线(即与线相连的圆圈)。当然是 MVVM。
  • 我已经看到您提到的项目——这将非常有帮助!谢谢!
  • 你如何缩放?我会在 RenderTransform 中使用 TransformGroup 作为地图的根,它具有控制缩放和平移的 ScaleTransform 和 TranslateTransform。就像在渲染过程中完成的那样,它根本不会影响布局。此外,您可以使用来自不同来源的许多缩放控件来控制该方面,而无需担心地图中的布局。

标签: wpf canvas mvvm itemscontrol gmap.net


【解决方案1】:

实际上并不是您问题的答案,但它可能会让您大致了解如何显示路线,即由线连接的圆圈集合。

以下示例使用 XAML MapControl 库,但也可以基于具有某种 MapItemsControl 的任何其他地图库(例如 MS Bing 地图)来实现。

我们的想法是收集构成您的路由的Location 实例。您将拥有一个 MapPolyline 并将其 Locations 属性绑定到路由点集合。然后在它上面放一个MapItemsControl 并将它的ItemsSource 绑定到同一个集合。请注意,ItemTemplate 使用带有 EllipseGeometries 而不是 Ellipses 的 Path 控件,因为它们以点位置为中心,而 Ellipses 顶部/左侧对齐。

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

如果路由点不是Locations,您可以在两个绑定中使用绑定转换器将您的路由点类型转换为Location

【讨论】:

  • 嗨 Clemens,我现在正在使用您的 XAMLMapControl,发现它很容易使用。但是,我的地图上有多条路线——我将如何使用您刚刚为集合的每个元素定义的路线?
  • 我从未尝试过,但您可能可以将这篇文章中的整个 MapPanel 放在另一个外部 MapItemsControl 的 ItemTemplate 中。
猜你喜欢
  • 2012-02-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-17
  • 2014-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-30
相关资源
最近更新 更多