Ответ 1
Я немного искал эту проблему и обнаружил, что в реализации Map
есть ошибка. Я также сделал обходной путь для него, который можно использовать как это
<m:Map ...>
<m:MapItemsControl Name="x"
behaviors:MapFixBehavior.FixUpdate="True"/>
</m:Map>
Я включил это исправление в ваше примерное приложение и загрузил его здесь: SurfaceApplication3.zip
Визуальное дерево для каждого ContentPresenter
выглядит следующим образом:
Когда вы добавляете новый элемент в коллекцию, 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
в 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
никогда не будет вызван, а _NormalizedMercatorToViewport
в MapShapeBase
будет иметь свои значения по умолчанию, и вычисления будут ошибочными в первый раз при добавлении нового элемента.
Обход
Чтобы обойти эту проблему, нам нужно заставить повторить измерение MapLayer
. Это необходимо сделать, когда новый ContentPresenter
добавлен в MapItemsControl
, но после того, как ContentPresenter
имеет визуальный дочерний элемент.
Одним из способов принудительного обновления является создание вложенного свойства, в котором флаги метаданных AffectsRender
, AffectsArrange
и 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);
}
}