diff --git a/MapControl/MapControl.csproj b/MapControl/MapControl.csproj
index b141ef95..7ee5737c 100644
--- a/MapControl/MapControl.csproj
+++ b/MapControl/MapControl.csproj
@@ -56,6 +56,8 @@
+
+
diff --git a/MapControl/MapGraticule.cs b/MapControl/MapGraticule.cs
index 2c6e3841..ee4aebad 100644
--- a/MapControl/MapGraticule.cs
+++ b/MapControl/MapGraticule.cs
@@ -5,107 +5,24 @@
using System;
using System.Linq;
using System.Windows;
-using System.Windows.Controls;
using System.Windows.Media;
-using System.Windows.Shapes;
namespace MapControl
{
///
- /// Draws a graticule overlay. The minimum spacing in pixels between adjacent
- /// graticule lines is specified by the MinLineSpacing property.
+ /// Draws a graticule overlay.
///
- public class MapGraticule : MapElement
+ public class MapGraticule : MapOverlay
{
- public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(
- typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).UpdateBrush()));
-
- public static readonly DependencyProperty FontSizeProperty = Control.FontSizeProperty.AddOwner(
- typeof(MapGraticule));
-
- public static readonly DependencyProperty FontFamilyProperty = Control.FontFamilyProperty.AddOwner(
- typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).typeface = null));
-
- public static readonly DependencyProperty FontStyleProperty = Control.FontStyleProperty.AddOwner(
- typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).typeface = null));
-
- public static readonly DependencyProperty FontWeightProperty = Control.FontWeightProperty.AddOwner(
- typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).typeface = null));
-
- public static readonly DependencyProperty FontStretchProperty = Control.FontStretchProperty.AddOwner(
- typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).typeface = null));
-
- public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
- typeof(MapGraticule), new FrameworkPropertyMetadata((o, e) => ((MapGraticule)o).UpdateBrush()));
-
- public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
- typeof(MapGraticule), new FrameworkPropertyMetadata(0.5, (o, e) => ((MapGraticule)o).pen.Thickness = (double)e.NewValue));
-
public static readonly DependencyProperty MinLineSpacingProperty = DependencyProperty.Register(
"MinLineSpacing", typeof(double), typeof(MapGraticule), new FrameworkPropertyMetadata(100d));
- public static double[] Spacings =
+ ///
+ /// Graticule line spacings in degrees.
+ ///
+ public static double[] LineSpacings =
new double[] { 1d / 60d, 1d / 30d, 1d / 12d, 1d / 6d, 1d / 4d, 1d / 3d, 1d / 2d, 1d, 2d, 5d, 10d, 15d, 20d, 30d, 45d };
- private readonly DrawingVisual visual = new DrawingVisual();
- private readonly Pen pen;
- private Typeface typeface;
-
- public MapGraticule()
- {
- pen = new Pen(null, StrokeThickness);
- IsHitTestVisible = false;
- AddVisualChild(visual);
- }
-
- public Brush Foreground
- {
- get { return (Brush)GetValue(ForegroundProperty); }
- set { SetValue(ForegroundProperty, value); }
- }
-
- public double FontSize
- {
- get { return (double)GetValue(FontSizeProperty); }
- set { SetValue(FontSizeProperty, value); }
- }
-
- public FontFamily FontFamily
- {
- get { return (FontFamily)GetValue(FontFamilyProperty); }
- set { SetValue(FontFamilyProperty, value); }
- }
-
- public FontStyle FontStyle
- {
- get { return (FontStyle)GetValue(FontStyleProperty); }
- set { SetValue(FontStyleProperty, value); }
- }
-
- public FontWeight FontWeight
- {
- get { return (FontWeight)GetValue(FontWeightProperty); }
- set { SetValue(FontWeightProperty, value); }
- }
-
- public FontStretch FontStretch
- {
- get { return (FontStretch)GetValue(FontStretchProperty); }
- set { SetValue(FontStretchProperty, value); }
- }
-
- public Brush Stroke
- {
- get { return (Brush)GetValue(StrokeProperty); }
- set { SetValue(StrokeProperty, value); }
- }
-
- public double StrokeThickness
- {
- get { return (double)GetValue(StrokeThicknessProperty); }
- set { SetValue(StrokeThicknessProperty, value); }
- }
-
///
/// Minimum spacing in pixels between adjacent graticule lines.
///
@@ -115,90 +32,61 @@ namespace MapControl
set { SetValue(MinLineSpacingProperty, value); }
}
- protected override int VisualChildrenCount
- {
- get { return 1; }
- }
-
- protected override Visual GetVisualChild(int index)
- {
- return visual;
- }
-
- protected override void OnViewportChanged()
+ protected override void OnRender(DrawingContext drawingContext)
{
MapBase parentMap = ParentMap;
Rect bounds = parentMap.ViewportTransform.Inverse.TransformBounds(new Rect(parentMap.RenderSize));
Location loc1 = parentMap.MapTransform.TransformBack(bounds.TopLeft);
Location loc2 = parentMap.MapTransform.TransformBack(bounds.BottomRight);
double minSpacing = MinLineSpacing * 360d / (Math.Pow(2d, parentMap.ZoomLevel) * 256d);
- double spacing = Spacings[Spacings.Length - 1];
+ double spacing = LineSpacings[LineSpacings.Length - 1];
if (spacing >= minSpacing)
{
- spacing = Spacings.FirstOrDefault(s => s >= minSpacing);
+ spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing);
}
double latitudeStart = Math.Ceiling(loc1.Latitude / spacing) * spacing;
double longitudeStart = Math.Ceiling(loc1.Longitude / spacing) * spacing;
- if (pen.Brush == null)
+ for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
{
- pen.Brush = Stroke != null ? Stroke : Foreground;
+ drawingContext.DrawLine(Pen,
+ parentMap.LocationToViewportPoint(new Location(lat, loc1.Longitude)),
+ parentMap.LocationToViewportPoint(new Location(lat, loc2.Longitude)));
}
- using (DrawingContext drawingContext = visual.RenderOpen())
+ for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
{
+ drawingContext.DrawLine(Pen,
+ parentMap.LocationToViewportPoint(new Location(loc1.Latitude, lon)),
+ parentMap.LocationToViewportPoint(new Location(loc2.Latitude, lon)));
+ }
+
+ if (Foreground != null && Foreground != Brushes.Transparent)
+ {
+ string format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
+
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
{
- drawingContext.DrawLine(pen,
- parentMap.LocationToViewportPoint(new Location(lat, loc1.Longitude)),
- parentMap.LocationToViewportPoint(new Location(lat, loc2.Longitude)));
- }
-
- for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
- {
- drawingContext.DrawLine(pen,
- parentMap.LocationToViewportPoint(new Location(loc1.Latitude, lon)),
- parentMap.LocationToViewportPoint(new Location(loc2.Latitude, lon)));
- }
-
- if (Foreground != null && Foreground != Brushes.Transparent)
- {
- string format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
-
- if (typeface == null)
+ for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
{
- typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
- }
+ double t = StrokeThickness / 2d;
+ Point p = parentMap.LocationToViewportPoint(new Location(lat, lon));
+ Point latPos = new Point(p.X + t + 2d, p.Y - t - FontSize / 4d);
+ Point lonPos = new Point(p.X + t + 2d, p.Y + t + FontSize);
+ string latString = CoordinateString(lat, format, "NS");
+ string lonString = CoordinateString(Location.NormalizeLongitude(lon), format, "EW");
- for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
- {
- for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
- {
- double t = StrokeThickness / 2d;
- Point p = parentMap.LocationToViewportPoint(new Location(lat, lon));
- Point latPos = new Point(p.X + t + 2d, p.Y - t - FontSize / 4d);
- Point lonPos = new Point(p.X + t + 2d, p.Y + t + FontSize);
- string latString = CoordinateString(lat, format, "NS");
- string lonString = CoordinateString(Location.NormalizeLongitude(lon), format, "EW");
-
- drawingContext.PushTransform(new RotateTransform(parentMap.Heading, p.X, p.Y));
- drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(latString, typeface, FontSize, latPos));
- drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(lonString, typeface, FontSize, lonPos));
- drawingContext.Pop();
- }
+ drawingContext.PushTransform(new RotateTransform(parentMap.Heading, p.X, p.Y));
+ drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(latString, Typeface, FontSize, latPos));
+ drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(lonString, Typeface, FontSize, lonPos));
+ drawingContext.Pop();
}
}
}
}
- private void UpdateBrush()
- {
- pen.Brush = null;
- OnViewportChanged();
- }
-
private static string CoordinateString(double value, string format, string hemispheres)
{
char hemisphere = hemispheres[0];
diff --git a/MapControl/MapOverlay.cs b/MapControl/MapOverlay.cs
new file mode 100644
index 00000000..750c6dfc
--- /dev/null
+++ b/MapControl/MapOverlay.cs
@@ -0,0 +1,224 @@
+// WPF MapControl - http://wpfmapcontrol.codeplex.com/
+// Copyright © 2012 Clemens Fischer
+// Licensed under the Microsoft Public License (Ms-PL)
+
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Shapes;
+
+namespace MapControl
+{
+ ///
+ /// Base class for map overlays with font, background, foreground and stroke properties.
+ ///
+ public class MapOverlay : MapElement
+ {
+ public static readonly DependencyProperty FontFamilyProperty = Control.FontFamilyProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
+
+ public static readonly DependencyProperty FontStyleProperty = Control.FontStyleProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
+
+ public static readonly DependencyProperty FontWeightProperty = Control.FontWeightProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
+
+ public static readonly DependencyProperty FontStretchProperty = Control.FontStretchProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
+
+ public static readonly DependencyProperty FontSizeProperty = Control.FontSizeProperty.AddOwner(
+ typeof(MapOverlay));
+
+ public static readonly DependencyProperty BackgroundProperty = Control.BackgroundProperty.AddOwner(
+ typeof(MapOverlay));
+
+ public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).ForegroundChanged((Brush)e.NewValue)));
+
+ public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.Brush = (Brush)e.NewValue));
+
+ public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata(0.5, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => ((MapOverlay)o).pen.Thickness = (double)e.NewValue));
+
+ public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapOverlay)o).StrokeDashOffset)));
+
+ public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashStyle = new DashStyle(((MapOverlay)o).StrokeDashArray, (double)e.NewValue)));
+
+ public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashCap = (PenLineCap)e.NewValue));
+
+ public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.StartLineCap = (PenLineCap)e.NewValue));
+
+ public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.EndLineCap = (PenLineCap)e.NewValue));
+
+ public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.LineJoin = (PenLineJoin)e.NewValue));
+
+ public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.MiterLimit = (double)e.NewValue));
+
+ private readonly Pen pen;
+ private Typeface typeface;
+
+ static MapOverlay()
+ {
+ UIElement.IsHitTestVisibleProperty.OverrideMetadata(
+ typeof(MapOverlay), new FrameworkPropertyMetadata(false));
+ }
+
+ public MapOverlay()
+ {
+ pen = new Pen
+ {
+ Brush = Stroke,
+ Thickness = StrokeThickness,
+ DashStyle = new DashStyle(StrokeDashArray, StrokeDashOffset),
+ DashCap = StrokeDashCap,
+ StartLineCap = StrokeStartLineCap,
+ EndLineCap = StrokeEndLineCap,
+ LineJoin = StrokeLineJoin,
+ MiterLimit = StrokeMiterLimit
+ };
+ }
+
+ public FontFamily FontFamily
+ {
+ get { return (FontFamily)GetValue(FontFamilyProperty); }
+ set { SetValue(FontFamilyProperty, value); }
+ }
+
+ public FontStyle FontStyle
+ {
+ get { return (FontStyle)GetValue(FontStyleProperty); }
+ set { SetValue(FontStyleProperty, value); }
+ }
+
+ public FontWeight FontWeight
+ {
+ get { return (FontWeight)GetValue(FontWeightProperty); }
+ set { SetValue(FontWeightProperty, value); }
+ }
+
+ public FontStretch FontStretch
+ {
+ get { return (FontStretch)GetValue(FontStretchProperty); }
+ set { SetValue(FontStretchProperty, value); }
+ }
+
+ public double FontSize
+ {
+ get { return (double)GetValue(FontSizeProperty); }
+ set { SetValue(FontSizeProperty, value); }
+ }
+
+ public Brush Background
+ {
+ get { return (Brush)GetValue(BackgroundProperty); }
+ set { SetValue(BackgroundProperty, value); }
+ }
+
+ public Brush Foreground
+ {
+ get { return (Brush)GetValue(ForegroundProperty); }
+ set { SetValue(ForegroundProperty, value); }
+ }
+
+ public Brush Stroke
+ {
+ get { return (Brush)GetValue(StrokeProperty); }
+ set { SetValue(StrokeProperty, value); }
+ }
+
+ public double StrokeThickness
+ {
+ get { return (double)GetValue(StrokeThicknessProperty); }
+ set { SetValue(StrokeThicknessProperty, value); }
+ }
+
+ public DoubleCollection StrokeDashArray
+ {
+ get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
+ set { SetValue(StrokeDashArrayProperty, value); }
+ }
+
+ public double StrokeDashOffset
+ {
+ get { return (double)GetValue(StrokeDashOffsetProperty); }
+ set { SetValue(StrokeDashOffsetProperty, value); }
+ }
+
+ public PenLineCap StrokeDashCap
+ {
+ get { return (PenLineCap)GetValue(StrokeDashCapProperty); }
+ set { SetValue(StrokeDashCapProperty, value); }
+ }
+
+ public PenLineCap StrokeStartLineCap
+ {
+ get { return (PenLineCap)GetValue(StrokeStartLineCapProperty); }
+ set { SetValue(StrokeStartLineCapProperty, value); }
+ }
+
+ public PenLineCap StrokeEndLineCap
+ {
+ get { return (PenLineCap)GetValue(StrokeEndLineCapProperty); }
+ set { SetValue(StrokeEndLineCapProperty, value); }
+ }
+
+ public PenLineJoin StrokeLineJoin
+ {
+ get { return (PenLineJoin)GetValue(StrokeLineJoinProperty); }
+ set { SetValue(StrokeLineJoinProperty, value); }
+ }
+
+ public double StrokeMiterLimit
+ {
+ get { return (double)GetValue(StrokeMiterLimitProperty); }
+ set { SetValue(StrokeMiterLimitProperty, value); }
+ }
+
+ protected Pen Pen
+ {
+ get
+ {
+ if (pen.Brush == null)
+ {
+ pen.Brush = Foreground;
+ }
+
+ return pen;
+ }
+ }
+
+ protected Typeface Typeface
+ {
+ get
+ {
+ if (typeface == null)
+ {
+ typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
+ }
+
+ return typeface;
+ }
+ }
+
+ protected override void OnViewportChanged()
+ {
+ InvalidateVisual();
+ }
+
+ private void ForegroundChanged(Brush foreground)
+ {
+ if (Stroke == null)
+ {
+ pen.Brush = foreground;
+ }
+ }
+ }
+}
diff --git a/MapControl/MapPolygon.cs b/MapControl/MapPolygon.cs
index 2c086cc4..ab855e0d 100644
--- a/MapControl/MapPolygon.cs
+++ b/MapControl/MapPolygon.cs
@@ -14,11 +14,11 @@ namespace MapControl
public class MapPolygon : MapPolyline
{
public static readonly DependencyProperty FillProperty = Shape.FillProperty.AddOwner(
- typeof(MapPolygon), new FrameworkPropertyMetadata((o, e) => ((MapPolygon)o).drawing.Brush = (Brush)e.NewValue));
+ typeof(MapPolygon), new FrameworkPropertyMetadata((o, e) => ((MapPolygon)o).Drawing.Brush = (Brush)e.NewValue));
public MapPolygon()
{
- drawing.Brush = Fill;
+ Drawing.Brush = Fill;
}
public Brush Fill
diff --git a/MapControl/MapPolyline.cs b/MapControl/MapPolyline.cs
index 34d5245c..6535cf15 100644
--- a/MapControl/MapPolyline.cs
+++ b/MapControl/MapPolyline.cs
@@ -3,8 +3,10 @@
// Licensed under the Microsoft Public License (Ms-PL)
using System;
+using System.Globalization;
using System.Linq;
using System.Windows;
+using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;
@@ -13,54 +15,68 @@ namespace MapControl
///
/// An open map polygon, defined by a collection of geographic locations in the Locations property.
///
- public class MapPolyline : MapElement
+ public class MapPolyline : FrameworkElement
{
public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
- typeof(MapPolyline), new FrameworkPropertyMetadata(Brushes.Black, (o, e) => ((MapPolyline)o).drawing.Pen.Brush = (Brush)e.NewValue));
-
- public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner(
- typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapPolyline)o).StrokeDashOffset)));
-
- public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner(
- typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.DashStyle = new DashStyle(((MapPolyline)o).StrokeDashArray, (double)e.NewValue)));
-
- public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner(
- typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.DashCap = (PenLineCap)e.NewValue));
-
- public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
- typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.StartLineCap = (PenLineCap)e.NewValue));
-
- public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
- typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.EndLineCap = (PenLineCap)e.NewValue));
-
- public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
- typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.LineJoin = (PenLineJoin)e.NewValue));
-
- public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
- typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).drawing.Pen.MiterLimit = (double)e.NewValue));
+ typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.Brush = (Brush)e.NewValue));
public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
- typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdatePenThickness()));
+ typeof(MapPolyline));
+
+ public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner(
+ typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapPolyline)o).StrokeDashOffset)));
+
+ public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner(
+ typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.DashStyle = new DashStyle(((MapPolyline)o).StrokeDashArray, (double)e.NewValue)));
+
+ public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner(
+ typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.DashCap = (PenLineCap)e.NewValue));
+
+ public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
+ typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.StartLineCap = (PenLineCap)e.NewValue));
+
+ public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
+ typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.EndLineCap = (PenLineCap)e.NewValue));
+
+ public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
+ typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.LineJoin = (PenLineJoin)e.NewValue));
+
+ public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
+ typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.MiterLimit = (double)e.NewValue));
public static readonly DependencyProperty TransformStrokeProperty = DependencyProperty.Register(
- "TransformStroke", typeof(bool), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdatePenThickness()));
+ "TransformStroke", typeof(bool), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).SetPenThicknessBinding()));
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
"Locations", typeof(LocationCollection), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdateGeometry()));
- protected readonly DrawingVisual visual = new DrawingVisual();
- protected readonly GeometryDrawing drawing = new GeometryDrawing();
+ protected readonly GeometryDrawing Drawing = new GeometryDrawing();
+
+ static MapPolyline()
+ {
+ MapPanel.ParentMapPropertyKey.OverrideMetadata(
+ typeof(MapPolyline),
+ new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged));
+ }
public MapPolyline()
{
- drawing.Pen = new Pen(Stroke, StrokeThickness);
-
- using (DrawingContext drawingContext = visual.RenderOpen())
+ Drawing.Pen = new Pen
{
- drawingContext.DrawDrawing(drawing);
- }
+ Brush = Stroke,
+ Thickness = StrokeThickness,
+ DashStyle = new DashStyle(StrokeDashArray, StrokeDashOffset),
+ DashCap = StrokeDashCap,
+ StartLineCap = StrokeStartLineCap,
+ EndLineCap = StrokeEndLineCap,
+ LineJoin = StrokeLineJoin,
+ MiterLimit = StrokeMiterLimit
+ };
+ }
- Loaded += (o, e) => UpdateGeometry();
+ public MapBase ParentMap
+ {
+ get { return MapPanel.GetParentMap(this); }
}
public Brush Stroke
@@ -69,6 +85,12 @@ namespace MapControl
set { SetValue(StrokeProperty, value); }
}
+ public double StrokeThickness
+ {
+ get { return (double)GetValue(StrokeThicknessProperty); }
+ set { SetValue(StrokeThicknessProperty, value); }
+ }
+
public DoubleCollection StrokeDashArray
{
get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
@@ -111,12 +133,6 @@ namespace MapControl
set { SetValue(StrokeMiterLimitProperty, value); }
}
- public double StrokeThickness
- {
- get { return (double)GetValue(StrokeThicknessProperty); }
- set { SetValue(StrokeThicknessProperty, value); }
- }
-
public bool TransformStroke
{
get { return (bool)GetValue(TransformStrokeProperty); }
@@ -129,43 +145,19 @@ namespace MapControl
set { SetValue(LocationsProperty, value); }
}
- public double TransformedStrokeThickness
- {
- get { return drawing.Pen.Thickness; }
- }
-
public PathGeometry TransformedGeometry
{
- get { return drawing.Geometry as PathGeometry; }
+ get { return Drawing.Geometry as PathGeometry; }
}
- protected override int VisualChildrenCount
+ public double TransformedStrokeThickness
{
- get { return 1; }
+ get { return Drawing.Pen.Thickness; }
}
- protected override Visual GetVisualChild(int index)
+ protected override void OnRender(DrawingContext drawingContext)
{
- return visual;
- }
-
- protected override void OnInitialized(EventArgs e)
- {
- base.OnInitialized(e);
-
- AddVisualChild(visual);
- }
-
- protected override void OnViewportChanged()
- {
- double scale = 1d;
-
- if (TransformStroke)
- {
- scale = ParentMap.CenterScale * MapBase.MeterPerDegree;
- }
-
- drawing.Pen.Thickness = scale * StrokeThickness;
+ drawingContext.DrawDrawing(Drawing);
}
protected virtual void UpdateGeometry()
@@ -177,20 +169,11 @@ namespace MapControl
{
if (ParentMap != null && Locations != null && Locations.Count > 0)
{
- drawing.Geometry = CreateGeometry(Locations, closed);
- OnViewportChanged();
+ Drawing.Geometry = CreateGeometry(Locations, closed);
}
else
{
- drawing.Geometry = null;
- }
- }
-
- private void UpdatePenThickness()
- {
- if (ParentMap != null)
- {
- OnViewportChanged();
+ Drawing.Geometry = null;
}
}
@@ -213,5 +196,44 @@ namespace MapControl
return geometry;
}
+
+ private void SetPenThicknessBinding()
+ {
+ BindingBase binding = new Binding { Source = this, Path = new PropertyPath(MapPolyline.StrokeThicknessProperty)};
+
+ if (TransformStroke && ParentMap != null)
+ {
+ MultiBinding multiBinding = new MultiBinding { Converter = new PenThicknessConverter() };
+ multiBinding.Bindings.Add(binding);
+ multiBinding.Bindings.Add(new Binding { Source = ParentMap, Path = new PropertyPath(MapBase.CenterScaleProperty)});
+ binding = multiBinding;
+ }
+
+ BindingOperations.SetBinding(Drawing.Pen, Pen.ThicknessProperty, binding);
+ }
+
+ private static void ParentMapPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
+ {
+ MapPolyline polyline = obj as MapPolyline;
+
+ if (polyline != null)
+ {
+ polyline.UpdateGeometry();
+ polyline.SetPenThicknessBinding();
+ }
+ }
+
+ private class PenThicknessConverter : IMultiValueConverter
+ {
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ return (double)values[0] * (double)values[1] * MapBase.MeterPerDegree;
+ }
+
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
}
}
diff --git a/MapControl/MapScale.cs b/MapControl/MapScale.cs
new file mode 100644
index 00000000..2b070fc6
--- /dev/null
+++ b/MapControl/MapScale.cs
@@ -0,0 +1,92 @@
+// WPF MapControl - http://wpfmapcontrol.codeplex.com/
+// Copyright © 2012 Clemens Fischer
+// Licensed under the Microsoft Public License (Ms-PL)
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace MapControl
+{
+ ///
+ /// Draws a map scale overlay.
+ ///
+ public class MapScale : MapOverlay
+ {
+ public static readonly DependencyProperty PaddingProperty = Control.PaddingProperty.AddOwner(
+ typeof(MapOverlay), new FrameworkPropertyMetadata(new Thickness(2d)));
+
+ private double length;
+ private Size size;
+
+ static MapScale()
+ {
+ FrameworkElement.MinWidthProperty.OverrideMetadata(
+ typeof(MapScale), new FrameworkPropertyMetadata(100d));
+
+ FrameworkElement.HorizontalAlignmentProperty.OverrideMetadata(
+ typeof(MapScale), new FrameworkPropertyMetadata(HorizontalAlignment.Right));
+
+ FrameworkElement.VerticalAlignmentProperty.OverrideMetadata(
+ typeof(MapScale), new FrameworkPropertyMetadata(VerticalAlignment.Bottom));
+
+ MapOverlay.StrokeStartLineCapProperty.OverrideMetadata(
+ typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
+
+ MapOverlay.StrokeEndLineCapProperty.OverrideMetadata(
+ typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
+ }
+
+ public Thickness Padding
+ {
+ get { return (Thickness)GetValue(PaddingProperty); }
+ set { SetValue(PaddingProperty, value); }
+ }
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ double scale = ParentMap.CenterScale; // px/m
+ length = MinWidth / scale;
+ double magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
+
+ if (length / magnitude < 2d)
+ {
+ length = 2d * magnitude;
+ }
+ else if (length / magnitude < 5d)
+ {
+ length = 5d * magnitude;
+ }
+ else
+ {
+ length = 10d * magnitude;
+ }
+
+ size.Width = length * scale + StrokeThickness + Padding.Left + Padding.Right;
+ size.Height = FontSize + 2d * StrokeThickness + Padding.Top + Padding.Bottom;
+ return size;
+ }
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ double x1 = Padding.Left + StrokeThickness / 2d;
+ double x2 = size.Width - Padding.Right - StrokeThickness / 2d;
+ double y1 = size.Height / 2d;
+ double y2 = size.Height - Padding.Bottom - StrokeThickness / 2d;
+ string text = length >= 1000d ? string.Format("{0:0} km", length / 1000d) : string.Format("{0:0} m", length);
+
+ drawingContext.DrawRectangle(Background ?? ParentMap.Background, null, new Rect(size));
+ drawingContext.DrawLine(Pen, new Point(x1, y1), new Point(x1, y2));
+ drawingContext.DrawLine(Pen, new Point(x2, y1), new Point(x2, y2));
+ drawingContext.DrawLine(Pen, new Point(x1, y2), new Point(x2, y2));
+ drawingContext.DrawGlyphRun(Foreground,
+ GlyphRunText.Create(text, Typeface, FontSize, new Vector(size.Width / 2d, y1 - StrokeThickness - 1d)));
+ }
+
+ protected override void OnViewportChanged()
+ {
+ InvalidateMeasure();
+ }
+ }
+}
diff --git a/SampleApps/SampleApplication/MainWindow.xaml b/SampleApps/SampleApplication/MainWindow.xaml
index fe2c441a..e03ce68b 100644
--- a/SampleApps/SampleApplication/MainWindow.xaml
+++ b/SampleApps/SampleApplication/MainWindow.xaml
@@ -136,6 +136,7 @@
ManipulationInertiaStarting="MapManipulationInertiaStarting"
MouseMove="MapMouseMove" MouseLeave="MapMouseLeave">
+
-
diff --git a/SampleApps/SampleApplication/ViewportPositionToVisibilityConverter.cs b/SampleApps/SampleApplication/ViewportPositionToVisibilityConverter.cs
index 1af8458d..5a1aaf24 100644
--- a/SampleApps/SampleApplication/ViewportPositionToVisibilityConverter.cs
+++ b/SampleApps/SampleApplication/ViewportPositionToVisibilityConverter.cs
@@ -10,17 +10,17 @@ namespace SampleApplication
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
- Visibility visibility = Visibility.Visible;
+ Visibility visibility = Visibility.Hidden;
if (values.Length == 2 && values[0] is Map && values[1] is Point? && ((Point?)values[1]).HasValue)
{
Map parentMap = (Map)values[0];
Point position = ((Point?)values[1]).Value;
- if (position.X < 0d || position.X > parentMap.ActualWidth ||
- position.Y < 0d || position.Y > parentMap.ActualHeight)
+ if (position.X >= 0d && position.X <= parentMap.ActualWidth &&
+ position.Y >= 0d && position.Y <= parentMap.ActualHeight)
{
- visibility = Visibility.Hidden;
+ visibility = Visibility.Visible;
}
}