Reworked sample applications

This commit is contained in:
Clemens 2021-12-05 17:16:14 +01:00
parent 9ce981a6ee
commit 32491a8e31
22 changed files with 947 additions and 706 deletions

View file

@ -67,7 +67,7 @@ namespace MapControl
if (values.Length != 4)
{
throw new FormatException("BoundingBox string must be a comma-separated list of four double values.");
throw new FormatException("BoundingBox string must be a comma-separated list of four floating point numbers.");
}
return new BoundingBox(

View file

@ -64,6 +64,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MBTiles.WinUI", "MBTiles\Wi
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinUiApp", "SampleApps\WinUiApp\WinUiApp.csproj", "{751EF297-7CF4-4879-BA8F-42661FA68668}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectionDemo", "SampleApps\ProjectionDemo\ProjectionDemo.csproj", "{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -428,6 +430,22 @@ Global
{751EF297-7CF4-4879-BA8F-42661FA68668}.Release|x86.ActiveCfg = Release|x86
{751EF297-7CF4-4879-BA8F-42661FA68668}.Release|x86.Build.0 = Release|x86
{751EF297-7CF4-4879-BA8F-42661FA68668}.Release|x86.Deploy.0 = Release|x86
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Debug|arm64.ActiveCfg = Debug|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Debug|arm64.Build.0 = Debug|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Debug|x64.ActiveCfg = Debug|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Debug|x64.Build.0 = Debug|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Debug|x86.ActiveCfg = Debug|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Debug|x86.Build.0 = Debug|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Release|Any CPU.Build.0 = Release|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Release|arm64.ActiveCfg = Release|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Release|arm64.Build.0 = Release|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Release|x64.ActiveCfg = Release|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Release|x64.Build.0 = Release|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Release|x86.ActiveCfg = Release|Any CPU
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -454,6 +472,7 @@ Global
{1F9FBADF-65C0-453D-9B45-7A88044F807F} = {2FDC8B91-FB95-4C57-8183-63587FBFE180}
{817D606F-A22D-485C-89CF-86062C8E97EF} = {CEAD0EA1-A971-4F5F-9EAE-C72F75D1F737}
{751EF297-7CF4-4879-BA8F-42661FA68668} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC}
{AC8C7BE0-9E72-434B-8BF3-FAEFAC2E859C} = {8F2103C2-78AF-4810-8FB9-67572F50C8FC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {458346DD-B23F-4FDC-8F9D-A10F1882A4DB}

View file

@ -6,7 +6,6 @@ using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

View file

@ -4,11 +4,11 @@
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Version>6.1.0</Version>
<Version>7.0.0</Version>
<Authors>Clemens Fischer</Authors>
<Description>XAML Map Control Map Projection Demo Application</Description>
<Product>XAML Map Control</Product>
<Copyright>Copyright © 2020 Clemens Fischer</Copyright>
<Copyright>Copyright © 2021 Clemens Fischer</Copyright>
</PropertyGroup>
<ItemGroup>

View file

@ -1,182 +0,0 @@
using MapControl;
using System;
using System.Collections.Generic;
using System.ComponentModel;
#if WINUI
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
#elif UWP
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#else
using System.Windows;
using System.Windows.Media;
#endif
namespace SampleApplication
{
public class MapLayers : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// See https://wiki.openstreetmap.org/wiki/Tile_servers for a list of free OpenStreetMap Tile Servers
private readonly Dictionary<string, UIElement> mapLayers = new Dictionary<string, UIElement>
{
{
"OpenStreetMap",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "https://tile.openstreetmap.org/{z}/{x}/{y}.png" },
SourceName = "OpenStreetMap",
Description = "© [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)"
}
},
{
"OpenStreetMap German",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "https://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png" },
SourceName = "OpenStreetMap German",
Description = "© [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)"
}
},
{
"OpenStreetMap French",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "http://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png" },
SourceName = "OpenStreetMap French",
Description = "© [OpenStreetMap France](https://www.openstreetmap.fr/mentions-legales/) © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)"
}
},
{
"OpenTopoMap",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "https://tile.opentopomap.org/{z}/{x}/{y}.png" },
SourceName = "OpenTopoMap",
Description = "© [OpenTopoMap](https://opentopomap.org/) © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)",
MaxZoomLevel = 17
}
},
{
"Seamarks",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png" },
SourceName = "OpenSeaMap",
MinZoomLevel = 9,
MaxZoomLevel = 18
}
},
{
"Bing Maps Road",
new BingMapsTileLayer
{
Mode = BingMapsTileLayer.MapMode.Road,
SourceName = "Bing Maps Road",
Description = "© [Microsoft](http://www.bing.com/maps/)"
}
},
{
"Bing Maps Aerial",
new BingMapsTileLayer
{
Mode = BingMapsTileLayer.MapMode.Aerial,
SourceName = "Bing Maps Aerial",
Description = "© [Microsoft](http://www.bing.com/maps/)",
MapForeground = new SolidColorBrush(Colors.White),
MapBackground = new SolidColorBrush(Colors.Black)
}
},
{
"Bing Maps Aerial with Labels",
new BingMapsTileLayer
{
Mode = BingMapsTileLayer.MapMode.AerialWithLabels,
SourceName = "Bing Maps Hybrid",
Description = "© [Microsoft](http://www.bing.com/maps/)",
MapForeground = new SolidColorBrush(Colors.White),
MapBackground = new SolidColorBrush(Colors.Black)
}
},
{
"TopPlusOpen WMTS",
new WmtsTileLayer
{
SourceName = "TopPlusOpen",
Description = "© [BKG](https://gdz.bkg.bund.de/index.php/default/webdienste/topplus-produkte/wmts-topplusopen-wmts-topplus-open.html)",
CapabilitiesUri = new Uri("https://sgx.geodatenzentrum.de/wmts_topplus_open/1.0.0/WMTSCapabilities.xml")
}
},
{
"TopPlusOpen WMS",
new WmsImageLayer
{
Description = "© [BKG](https://gdz.bkg.bund.de/index.php/default/webdienste/topplus-produkte/wms-topplusopen-mit-layer-fur-normalausgabe-und-druck-wms-topplus-open.html)",
ServiceUri = new Uri("https://sgx.geodatenzentrum.de/wms_topplus_open")
}
},
{
"OpenStreetMap WMS",
new WmsImageLayer
{
Description = "© [terrestris GmbH & Co. KG](http://ows.terrestris.de/) © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)",
ServiceUri = new Uri("http://ows.terrestris.de/osm/service")
}
},
};
private string currentMapLayerName = "OpenStreetMap";
public string CurrentMapLayerName
{
get { return currentMapLayerName; }
set
{
currentMapLayerName = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentMapLayerName)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentMapLayer)));
}
}
public UIElement CurrentMapLayer
{
get { return mapLayers[currentMapLayerName]; }
}
public UIElement SeamarksLayer
{
get { return mapLayers["Seamarks"]; }
}
public List<string> MapLayerNames { get; } = new List<string>
{
"OpenStreetMap",
"OpenStreetMap German",
"OpenStreetMap French",
"OpenTopoMap",
"TopPlusOpen WMTS",
"TopPlusOpen WMS",
"OpenStreetMap WMS",
};
public MapLayers()
{
// Add Bing Maps TileLayers with tile URLs retrieved from the Imagery Metadata Service
// (http://msdn.microsoft.com/en-us/library/ff701716.aspx).
// A Bing Maps API Key (http://msdn.microsoft.com/en-us/library/ff428642.aspx) is required
// for using these layers and must be assigned to the static BingMapsTileLayer.ApiKey property.
if (!string.IsNullOrEmpty(BingMapsTileLayer.ApiKey))
{
MapLayerNames.Add("Bing Maps Road");
MapLayerNames.Add("Bing Maps Aerial");
MapLayerNames.Add("Bing Maps Aerial with Labels");
}
}
}
}

View file

@ -0,0 +1,158 @@
using MapControl;
using System.Collections.Generic;
using System.Linq;
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
#elif UWP
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
#else
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
#endif
namespace SampleApplication
{
public class MapLayersMenuButton : MenuButton
{
public MapLayersMenuButton()
{
#if WINUI || UWP
Content = new FontIcon
{
FontFamily = new FontFamily("Segoe MDL2 Assets"),
Glyph = "\uE81E"
};
#else
FontFamily = new FontFamily("Segoe MDL2 Assets");
Content = "\uE81E";
#endif
}
public static readonly DependencyProperty MapProperty = DependencyProperty.Register(
nameof(Map), typeof(MapBase), typeof(MapLayersMenuButton),
new PropertyMetadata(null, (o, e) => ((MapLayersMenuButton)o).InitializeMenu()));
public static readonly DependencyProperty MapLayersProperty = DependencyProperty.Register(
nameof(MapLayers), typeof(IDictionary<string, UIElement>), typeof(MapLayersMenuButton),
new PropertyMetadata(null, (o, e) => ((MapLayersMenuButton)o).InitializeMenu()));
public static readonly DependencyProperty MapOverlaysProperty = DependencyProperty.Register(
nameof(MapOverlays), typeof(IDictionary<string, UIElement>), typeof(MapLayersMenuButton),
new PropertyMetadata(null, (o, e) => ((MapLayersMenuButton)o).InitializeMenu()));
public MapBase Map
{
get { return (MapBase)GetValue(MapProperty); }
set { SetValue(MapProperty, value); }
}
public IDictionary<string, UIElement> MapLayers
{
get { return (IDictionary<string, UIElement>)GetValue(MapLayersProperty); }
set { SetValue(MapLayersProperty, value); }
}
public IDictionary<string, UIElement> MapOverlays
{
get { return (IDictionary<string, UIElement>)GetValue(MapOverlaysProperty); }
set { SetValue(MapOverlaysProperty, value); }
}
private void InitializeMenu()
{
if (Map != null && MapLayers != null)
{
var menu = CreateMenu();
foreach (var layer in MapLayers)
{
menu.Items.Add(CreateMenuItem(layer.Key, layer.Value, MapLayerClicked));
}
var initialLayer = MapLayers.Values.FirstOrDefault();
if (MapOverlays != null && MapOverlays.Any())
{
if (initialLayer != null)
{
menu.Items.Add(CreateSeparator());
}
foreach (var overlay in MapOverlays)
{
menu.Items.Add(CreateMenuItem(overlay.Key, overlay.Value, MapOverlayClicked));
}
}
if (initialLayer != null)
{
SetMapLayer(initialLayer);
}
}
}
private void MapLayerClicked(object sender, RoutedEventArgs e)
{
var item = (FrameworkElement)sender;
var layer = (UIElement)item.Tag;
SetMapLayer(layer);
}
private void MapOverlayClicked(object sender, RoutedEventArgs e)
{
var item = (FrameworkElement)sender;
var layer = (UIElement)item.Tag;
ToggleMapOverlay(layer);
}
private void SetMapLayer(UIElement layer)
{
Map.MapLayer = layer;
UpdateCheckedStates();
}
private void ToggleMapOverlay(UIElement layer)
{
if (Map.Children.Contains(layer))
{
Map.Children.Remove(layer);
}
else
{
int index = 1;
foreach (var overlay in MapOverlays.Values)
{
if (overlay == layer)
{
Map.Children.Insert(index, layer);
break;
}
if (Map.Children.Contains(overlay))
{
index++;
}
}
}
UpdateCheckedStates();
}
private void UpdateCheckedStates()
{
foreach (var item in GetMenuItems())
{
item.IsChecked = Map.Children.Contains((UIElement)item.Tag);
}
}
}
}

View file

@ -0,0 +1,94 @@
using MapControl;
using System.Collections.Generic;
using System.Linq;
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
#elif UWP
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
#else
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
#endif
namespace SampleApplication
{
public class MapProjectionsMenuButton : MenuButton
{
public MapProjectionsMenuButton()
{
#if WINUI || UWP
Content = new FontIcon
{
FontFamily = new FontFamily("Segoe MDL2 Assets"),
Glyph = "\uE809"
};
#else
FontFamily = new FontFamily("Segoe MDL2 Assets");
Content = "\uE809";
#endif
}
public static readonly DependencyProperty MapProperty = DependencyProperty.Register(
nameof(Map), typeof(MapBase), typeof(MapProjectionsMenuButton),
new PropertyMetadata(null, (o, e) => ((MapProjectionsMenuButton)o).InitializeMenu()));
public static readonly DependencyProperty MapProjectionsProperty = DependencyProperty.Register(
nameof(MapProjections), typeof(IDictionary<string, MapProjection>), typeof(MapProjectionsMenuButton),
new PropertyMetadata(null, (o, e) => ((MapProjectionsMenuButton)o).InitializeMenu()));
public MapBase Map
{
get { return (MapBase)GetValue(MapProperty); }
set { SetValue(MapProperty, value); }
}
public IDictionary<string, MapProjection> MapProjections
{
get { return (IDictionary<string, MapProjection>)GetValue(MapProjectionsProperty); }
set { SetValue(MapProjectionsProperty, value); }
}
private void InitializeMenu()
{
if (Map != null && MapProjections != null)
{
var menu = CreateMenu();
foreach (var projection in MapProjections)
{
menu.Items.Add(CreateMenuItem(projection.Key, projection.Value, MapProjectionClicked));
}
var initialProjection = MapProjections.Values.FirstOrDefault();
if (initialProjection != null)
{
SetMapProjection(initialProjection);
}
}
}
private void MapProjectionClicked(object sender, RoutedEventArgs e)
{
var item = (FrameworkElement)sender;
var projection = (MapProjection)item.Tag;
SetMapProjection(projection);
}
private void SetMapProjection(MapProjection projection)
{
Map.MapProjection = projection;
foreach (var item in GetMenuItems())
{
item.IsChecked = Map.MapProjection == (MapProjection)item.Tag;
}
}
}
}

View file

@ -1,95 +1,233 @@
using MapControl;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System;
using System.Collections.Generic;
#if WINUI
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
#elif UWP
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
#else
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
#endif
namespace SampleApplication
{
public class ViewModelBase : INotifyPropertyChanged
public class PointItem
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Location Location { get; set; }
}
public class PointItem : ViewModelBase
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
RaisePropertyChanged(nameof(Name));
}
}
private Location location;
public Location Location
{
get { return location; }
set
{
location = value;
RaisePropertyChanged(nameof(Location));
}
}
}
public class Polyline
public class PolylineItem
{
public LocationCollection Locations { get; set; }
}
public class MapViewModel : ViewModelBase
public class MapViewModel
{
private Location mapCenter = new Location(53.5, 8.2);
public Location MapCenter
public List<PointItem> Points { get; } = new List<PointItem>();
public List<PointItem> Pushpins { get; } = new List<PointItem>();
public List<PolylineItem> Polylines { get; } = new List<PolylineItem>();
public Dictionary<string, MapProjection> MapProjections { get; } = new Dictionary<string, MapProjection>
{
{ "Web Mercator", new WebMercatorProjection() },
{ "World Mercator", new WorldMercatorProjection() },
{ "Equirectangular", new EquirectangularProjection() },
{ "Orthographic", new OrthographicProjection() },
{ "Gnomonic", new GnomonicProjection() },
{ "Stereographic", new StereographicProjection() }
};
public Dictionary<string, UIElement> MapLayers { get; } = new Dictionary<string, UIElement>
{
get { return mapCenter; }
set
{
mapCenter = value;
RaisePropertyChanged(nameof(MapCenter));
"OpenStreetMap",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "https://tile.openstreetmap.org/{z}/{x}/{y}.png" },
SourceName = "OpenStreetMap",
Description = "© [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)"
}
},
{
"OpenStreetMap German",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "https://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png" },
SourceName = "OpenStreetMap German",
Description = "© [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)"
}
},
{
"OpenStreetMap French",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "http://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png" },
SourceName = "OpenStreetMap French",
Description = "© [OpenStreetMap France](https://www.openstreetmap.fr/mentions-legales/) © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)"
}
},
{
"OpenTopoMap",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "https://tile.opentopomap.org/{z}/{x}/{y}.png" },
SourceName = "OpenTopoMap",
Description = "© [OpenTopoMap](https://opentopomap.org/) © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)",
MaxZoomLevel = 17
}
},
{
"TopPlusOpen WMTS",
new WmtsTileLayer
{
SourceName = "TopPlusOpen",
Description = "© [BKG](https://gdz.bkg.bund.de/index.php/default/webdienste/topplus-produkte/wmts-topplusopen-wmts-topplus-open.html)",
CapabilitiesUri = new Uri("https://sgx.geodatenzentrum.de/wmts_topplus_open/1.0.0/WMTSCapabilities.xml")
}
},
{
"TopPlusOpen WMS",
new WmsImageLayer
{
Description = "© [BKG](https://gdz.bkg.bund.de/index.php/default/webdienste/topplus-produkte/wms-topplusopen-mit-layer-fur-normalausgabe-und-druck-wms-topplus-open.html)",
ServiceUri = new Uri("https://sgx.geodatenzentrum.de/wms_topplus_open")
}
},
{
"OpenStreetMap WMS",
new WmsImageLayer
{
Description = "© [terrestris GmbH & Co. KG](http://ows.terrestris.de/) © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)",
ServiceUri = new Uri("http://ows.terrestris.de/osm/service")
}
}
}
};
public ObservableCollection<PointItem> Points { get; } = new ObservableCollection<PointItem>();
public ObservableCollection<PointItem> Pushpins { get; } = new ObservableCollection<PointItem>();
public ObservableCollection<Polyline> Polylines { get; } = new ObservableCollection<Polyline>();
public MapLayers MapLayers { get; } = new MapLayers();
public Dictionary<string, UIElement> MapOverlays { get; } = new Dictionary<string, UIElement>
{
{
"Sample Image",
new Image()
},
{
"Seamarks",
new MapTileLayer
{
TileSource = new TileSource { UriFormat = "http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png" },
SourceName = "OpenSeaMap",
MinZoomLevel = 9,
MaxZoomLevel = 18
}
},
{
"Graticule",
new MapGraticule
{
Opacity = 0.75
}
},
{
"Scale",
new MapScale
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Bottom
}
}
};
public MapViewModel()
{
// Add Bing Maps TileLayers with tile URLs retrieved from the Imagery Metadata Service
// (http://msdn.microsoft.com/en-us/library/ff701716.aspx).
// A Bing Maps API Key (http://msdn.microsoft.com/en-us/library/ff428642.aspx) is required
// for using these layers and must be assigned to the static BingMapsTileLayer.ApiKey property.
if (!string.IsNullOrEmpty(BingMapsTileLayer.ApiKey))
{
MapLayers.Add(
"Bing Maps Road",
new BingMapsTileLayer
{
Mode = BingMapsTileLayer.MapMode.Road,
SourceName = "Bing Maps Road",
Description = "© [Microsoft](http://www.bing.com/maps/)"
});
MapLayers.Add(
"Bing Maps Aerial",
new BingMapsTileLayer
{
Mode = BingMapsTileLayer.MapMode.Aerial,
SourceName = "Bing Maps Aerial",
Description = "© [Microsoft](http://www.bing.com/maps/)",
MapForeground = new SolidColorBrush(Colors.White),
MapBackground = new SolidColorBrush(Colors.Black)
});
MapLayers.Add(
"Bing Maps Aerial with Labels",
new BingMapsTileLayer
{
Mode = BingMapsTileLayer.MapMode.AerialWithLabels,
SourceName = "Bing Maps Hybrid",
Description = "© [Microsoft](http://www.bing.com/maps/)",
MapForeground = new SolidColorBrush(Colors.White),
MapBackground = new SolidColorBrush(Colors.Black)
});
}
var sampleImage = (Image)MapOverlays["Sample Image"];
#if WINUI || UWP
sampleImage.Source = new BitmapImage(new Uri("ms-appx:///10_535_330.jpg"));
#else
sampleImage.Source = new BitmapImage(new Uri("pack://siteoforigin:,,,/10_535_330.jpg"));
#endif
MapPanel.SetBoundingBox(sampleImage, new BoundingBox(53.54031, 8.08594, 53.74871, 8.43750));
Points.Add(new PointItem
{
Name = "Steinbake Leitdamm",
Location = new Location(53.51217, 8.16603)
});
Points.Add(new PointItem
{
Name = "Buhne 2",
Location = new Location(53.50926, 8.15815)
});
Points.Add(new PointItem
{
Name = "Buhne 4",
Location = new Location(53.50468, 8.15343)
});
Points.Add(new PointItem
{
Name = "Buhne 6",
Location = new Location(53.50092, 8.15267)
});
Points.Add(new PointItem
{
Name = "Buhne 8",
Location = new Location(53.49871, 8.15321)
});
Points.Add(new PointItem
{
Name = "Buhne 10",
@ -101,27 +239,31 @@ namespace SampleApplication
Name = "WHV - Eckwarderhörne",
Location = new Location(53.5495, 8.1877)
});
Pushpins.Add(new PointItem
{
Name = "JadeWeserPort",
Location = new Location(53.5914, 8.14)
});
Pushpins.Add(new PointItem
{
Name = "Kurhaus Dangast",
Location = new Location(53.447, 8.1114)
});
Pushpins.Add(new PointItem
{
Name = "Eckwarderhörne",
Location = new Location(53.5207, 8.2323)
});
Polylines.Add(new Polyline
Polylines.Add(new PolylineItem
{
Locations = LocationCollection.Parse("53.5140,8.1451 53.5123,8.1506 53.5156,8.1623 53.5276,8.1757 53.5491,8.1852 53.5495,8.1877 53.5426,8.1993 53.5184,8.2219 53.5182,8.2386 53.5195,8.2387")
});
Polylines.Add(new Polyline
Polylines.Add(new PolylineItem
{
Locations = LocationCollection.Parse("53.5978,8.1212 53.6018,8.1494 53.5859,8.1554 53.5852,8.1531 53.5841,8.1539 53.5802,8.1392 53.5826,8.1309 53.5867,8.1317 53.5978,8.1212")
});

View file

@ -0,0 +1,73 @@
using System.Collections.Generic;
using System.Linq;
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
#elif UWP
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
#else
using System.Windows;
using System.Windows.Controls;
#endif
namespace SampleApplication
{
public class MenuButton : Button
{
#if WINUI || UWP
protected MenuFlyout CreateMenu()
{
var menu = new MenuFlyout();
Flyout = menu;
return menu;
}
protected IEnumerable<ToggleMenuFlyoutItem> GetMenuItems()
{
return ((MenuFlyout)Flyout).Items.OfType<ToggleMenuFlyoutItem>();
}
protected static ToggleMenuFlyoutItem CreateMenuItem(string text, object item, RoutedEventHandler click)
{
var menuItem = new ToggleMenuFlyoutItem { Text = text, Tag = item };
menuItem.Click += click;
return menuItem;
}
protected static MenuFlyoutSeparator CreateSeparator()
{
return new MenuFlyoutSeparator();
}
#else
protected ContextMenu CreateMenu()
{
var menu = new ContextMenu();
ContextMenu = menu;
return menu;
}
protected IEnumerable<MenuItem> GetMenuItems()
{
return ContextMenu.Items.OfType<MenuItem>();
}
protected static MenuItem CreateMenuItem(string text, object item, RoutedEventHandler click)
{
var menuItem = new MenuItem { Header = text, Tag = item };
menuItem.Click += click;
return menuItem;
}
protected static Separator CreateSeparator()
{
return new Separator();
}
protected MenuButton()
{
Click += (s, e) => ContextMenu.IsOpen = true;
}
#endif
}
}

View file

@ -2,129 +2,99 @@
x:Class="SampleApplication.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SampleApplication"
xmlns:map="using:MapControl">
xmlns:map="using:MapControl"
xmlns:local="using:SampleApplication">
<Page.Resources>
<DataTemplate x:Key="PolylineItemTemplate">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</DataTemplate>
<Style x:Key="PolylineItemStyle" TargetType="map:MapItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="PointItemStyle" TargetType="map:MapItem">
<Setter Property="AutoCollapse" Value="True"/>
<Setter Property="LocationMemberPath" Value="Location"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<Canvas>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="PointerOver"/>
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath" Storyboard.TargetProperty="Opacity" To="0.75" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedUnfocused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath" Storyboard.TargetProperty="Opacity" To="0.75" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedPointerOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath" Storyboard.TargetProperty="Opacity" To="0.75" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedPressed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath" Storyboard.TargetProperty="Opacity" To="0.75" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="selectedPath" Fill="White" Opacity="0">
<Path.Data>
<EllipseGeometry RadiusX="12" RadiusY="12"/>
</Path.Data>
</Path>
<Path Fill="Transparent" Stroke="Gray" StrokeThickness="2">
<Path.Data>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</Path.Data>
</Path>
<Grid Canvas.Left="15" Canvas.Top="-8">
<TextBlock Margin="2,0,2,0" Text="{Binding Name}"/>
</Grid>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="PushpinItemStyle" TargetType="map:MapItem">
<Setter Property="AutoCollapse" Value="True"/>
<Setter Property="LocationMemberPath" Value="Location"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:Pushpin Content="{Binding Name}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<map:WebMercatorProjection x:Key="WebMercatorProjection"/>
<map:WorldMercatorProjection x:Key="WorldMercatorProjection"/>
<map:EquirectangularProjection x:Key="EquirectangularProjection"/>
<map:OrthographicProjection x:Key="OrthographicProjection"/>
<map:GnomonicProjection x:Key="GnomonicProjection"/>
<map:StereographicProjection x:Key="StereographicProjection"/>
</Page.Resources>
<Page.DataContext>
<local:MapViewModel/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.Resources>
<DataTemplate x:Key="PolylineItemTemplate">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</DataTemplate>
<map:Map x:Name="map" MinZoomLevel="2" MaxZoomLevel="20" ZoomLevel="11" ManipulationMode="All"
Center="{x:Bind ViewModel.MapCenter, Mode=TwoWay}"
MapLayer="{x:Bind ViewModel.MapLayers.CurrentMapLayer, Mode=OneWay}"
MapProjection="{Binding SelectedValue, ElementName=projectionComboBox,
FallbackValue={StaticResource WebMercatorProjection},
TargetNullValue={StaticResource WebMercatorProjection}}">
<Style x:Key="PointItemStyle" TargetType="map:MapItem">
<Setter Property="AutoCollapse" Value="True"/>
<Setter Property="LocationMemberPath" Value="Location"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<Canvas>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="PointerOver"/>
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath" Storyboard.TargetProperty="Opacity" To="0.75" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedUnfocused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath" Storyboard.TargetProperty="Opacity" To="0.75" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedPointerOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath" Storyboard.TargetProperty="Opacity" To="0.75" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedPressed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath" Storyboard.TargetProperty="Opacity" To="0.75" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="selectedPath" Fill="White" Opacity="0">
<Path.Data>
<EllipseGeometry RadiusX="12" RadiusY="12"/>
</Path.Data>
</Path>
<Path Fill="Transparent" Stroke="Gray" StrokeThickness="2">
<Path.Data>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</Path.Data>
</Path>
<Grid Canvas.Left="15" Canvas.Top="-8">
<TextBlock Margin="2,0,2,0" Text="{Binding Name}"/>
</Grid>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Image x:Name="mapImage" Source="10_535_330.jpg" Opacity="0.5" Stretch="Fill">
<map:MapPanel.BoundingBox>
<map:BoundingBox South="53.54031" North="53.74871" West="8.08594" East="8.43750"/>
</map:MapPanel.BoundingBox>
</Image>
<Style x:Key="PushpinItemStyle" TargetType="map:MapItem">
<Setter Property="AutoCollapse" Value="True"/>
<Setter Property="LocationMemberPath" Value="Location"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:Pushpin Content="{Binding Name}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<map:MapGraticule x:Name="graticule" Opacity="0.6"
Visibility="{Binding IsChecked, ElementName=graticuleCheckBox}"/>
<map:Map x:Name="map"
MinZoomLevel="2" MaxZoomLevel="21" ZoomLevel="11" ManipulationMode="All"
MapLayer="{Binding MapLayers[OpenStreetMap]}"
PointerMoved="MapPointerMoved"
PointerExited="MapPointerExited">
<map:Map.Center>
<map:Location Latitude="53.5" Longitude="8.2"/>
</map:Map.Center>
<map:MapScale HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
<!-- use ItemTemplate or ItemContainerStyle alternatively -->
<map:MapItemsControl ItemsSource="{Binding Polylines}"
ItemTemplate="{StaticResource PolylineItemTemplate}"/>
<!--<map:MapItemsControl ItemsSource="{Binding Polylines}"
ItemContainerStyle="{StaticResource PolylineItemStyle}"/>-->
<map:MapItemsControl ItemsSource="{Binding Points}"
ItemContainerStyle="{StaticResource PointItemStyle}"
@ -142,55 +112,29 @@
<Border HorizontalAlignment="Right" VerticalAlignment="Bottom" Background="#BFFFFFFF">
<TextBlock Margin="2" FontSize="10"
local:HyperlinkText.InlinesSource="{Binding MapLayers.CurrentMapLayer.Description}"/>
local:HyperlinkText.InlinesSource="{Binding MapLayer.Description, ElementName=map}"/>
</Border>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<StackPanel Margin="5">
<TextBlock Text="Zoom Level" HorizontalAlignment="Center" FontSize="12"/>
<Slider Margin="10,-10,10,0" Width="100" SmallChange="0.1"
Minimum="{Binding MinZoomLevel, ElementName=map}"
Maximum="{Binding MaxZoomLevel, ElementName=map}"
Value="{Binding TargetZoomLevel, ElementName=map, Mode=TwoWay}"/>
</StackPanel>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Margin="6" Background="#7FFFFFFF">
<local:MapLayersMenuButton
Margin="2" Padding="8" ToolTipService.ToolTip="Map Layers"
Map="{Binding ElementName=map}"
MapLayers="{Binding MapLayers}"
MapOverlays="{Binding MapOverlays}"/>
<StackPanel Margin="5">
<TextBlock Text="Heading" HorizontalAlignment="Center" FontSize="12"/>
<Slider Margin="10,-10,10,0" Width="100" Minimum="0" Maximum="360" SmallChange="5" LargeChange="45"
Value="{Binding Heading, ElementName=map, Mode=TwoWay}"/>
</StackPanel>
<!--<local:MapProjectionsMenuButton
Margin="2" Padding="8" ToolTipService.ToolTip="Map Projections"
Map="{Binding ElementName=map}"
MapProjections="{Binding MapProjections}"/>-->
<StackPanel Margin="5,5,25,5">
<TextBlock Text="Image Opacity" HorizontalAlignment="Center" FontSize="12"/>
<Slider Margin="10,-10,10,0" Width="100" Value="50" ValueChanged="ImageOpacitySliderValueChanged"/>
</StackPanel>
<Slider Orientation="Vertical" Margin="4,8" Height="100"
Minimum="{Binding MinZoomLevel, ElementName=map}"
Maximum="{Binding MaxZoomLevel, ElementName=map}"
Value="{Binding TargetZoomLevel, ElementName=map, Mode=TwoWay}"/>
</StackPanel>
<CheckBox x:Name="graticuleCheckBox"
VerticalAlignment="Center" Content="Graticule"/>
<CheckBox VerticalAlignment="Center" Content="Seamarks"
Checked="SeamarksChecked" Unchecked="SeamarksUnchecked"/>
<ComboBox Width="250" VerticalAlignment="Center" Margin="0,5"
ItemsSource="{Binding MapLayers.MapLayerNames}"
SelectedItem="{Binding MapLayers.CurrentMapLayerName, Mode=TwoWay}"/>
<ComboBox x:Name="projectionComboBox" Width="150" VerticalAlignment="Center" Margin="5"
SelectedIndex="0" SelectedValuePath="Tag"
SelectedValue="{Binding MapProjection, ElementName=map, Mode=TwoWay}">
<ComboBoxItem Content="Web Mercator" Tag="{StaticResource WebMercatorProjection}"/>
<ComboBoxItem Content="World Mercator" Tag="{StaticResource WorldMercatorProjection}"/>
<ComboBoxItem Content="Equirectangular" Tag="{StaticResource EquirectangularProjection}"/>
<ComboBoxItem Content="Orthographic" Tag="{StaticResource OrthographicProjection}"/>
<ComboBoxItem Content="Gnomonic" Tag="{StaticResource GnomonicProjection}"/>
<ComboBoxItem Content="Stereographic" Tag="{StaticResource StereographicProjection}"/>
</ComboBox>
</StackPanel>
</Grid>
<Border HorizontalAlignment="Center" VerticalAlignment="Top" Margin="4" Background="#AFFFFFFF">
<TextBlock x:Name="mouseLocation" Margin="4,2" Visibility="Collapsed"/>
</Border>
</Grid>
</Page>

View file

@ -1,10 +1,11 @@
using System;
using MapControl;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using MapControl;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
namespace SampleApplication
{
@ -28,30 +29,42 @@ namespace SampleApplication
}
}
public MapViewModel ViewModel { get; } = new MapViewModel();
public MainPage()
{
InitializeComponent();
DataContext = ViewModel;
}
private void ImageOpacitySliderValueChanged(object sender, RangeBaseValueChangedEventArgs e)
private void MapPointerMoved(object sender, PointerRoutedEventArgs e)
{
if (mapImage != null)
var location = map.ViewToLocation(e.GetCurrentPoint(map).Position);
var latitude = (int)Math.Round(location.Latitude * 60000d);
var longitude = (int)Math.Round(Location.NormalizeLongitude(location.Longitude) * 60000d);
var latHemisphere = 'N';
var lonHemisphere = 'E';
if (latitude < 0)
{
mapImage.Opacity = e.NewValue / 100;
latitude = -latitude;
latHemisphere = 'S';
}
if (longitude < 0)
{
longitude = -longitude;
lonHemisphere = 'W';
}
mouseLocation.Text = string.Format(CultureInfo.InvariantCulture,
"{0} {1:00} {2:00.000}\n{3} {4:000} {5:00.000}",
latHemisphere, latitude / 60000, (latitude % 60000) / 1000d,
lonHemisphere, longitude / 60000, (longitude % 60000) / 1000d);
mouseLocation.Visibility = Visibility.Visible;
}
private void SeamarksChecked(object sender, RoutedEventArgs e)
private void MapPointerExited(object sender, PointerRoutedEventArgs e)
{
map.Children.Insert(map.Children.IndexOf(graticule), ViewModel.MapLayers.SeamarksLayer);
}
private void SeamarksUnchecked(object sender, RoutedEventArgs e)
{
map.Children.Remove(ViewModel.MapLayers.SeamarksLayer);
mouseLocation.Visibility = Visibility.Collapsed;
mouseLocation.Text = string.Empty;
}
}
}

View file

@ -14,8 +14,8 @@
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="UniversalApp.App">
<uap:VisualElements DisplayName="XAML Map Control Test Application" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="UniversalApp" BackgroundColor="transparent">
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="SampleApplication.App">
<uap:VisualElements DisplayName="XAML Map Control Test Application" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="SampleApplication" BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
</uap:DefaultTile>
<uap:SplashScreen Image="Assets\SplashScreen.png" />

View file

@ -7,7 +7,7 @@
<ProjectGuid>{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UniversalApp</RootNamespace>
<RootNamespace>SampleApplication</RootNamespace>
<AssemblyName>UniversalApp</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
@ -51,12 +51,18 @@
<Compile Include="..\Shared\HyperlinkText.cs">
<Link>HyperlinkText.cs</Link>
</Compile>
<Compile Include="..\Shared\MapLayers.cs">
<Link>MapLayers.cs</Link>
<Compile Include="..\Shared\MapLayersMenuButton.cs">
<Link>MapLayersMenuButton.cs</Link>
</Compile>
<Compile Include="..\Shared\MapProjectionsMenuButton.cs">
<Link>MapProjectionsMenuButton.cs</Link>
</Compile>
<Compile Include="..\Shared\MapViewModel.cs">
<Link>MapViewModel.cs</Link>
</Compile>
<Compile Include="..\Shared\MenuButton.cs">
<Link>MenuButton.cs</Link>
</Compile>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>

View file

@ -2,24 +2,15 @@
x:Class="SampleApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:map="using:MapControl">
xmlns:map="using:MapControl"
xmlns:local="using:SampleApplication">
<Grid x:Name="root">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="PolylineItemTemplate">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</DataTemplate>
<Style x:Key="PolylineItemStyle" TargetType="map:MapItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="PointItemStyle" TargetType="map:MapItem">
<Setter Property="AutoCollapse" Value="True"/>
<Setter Property="LocationMemberPath" Value="Location"/>
@ -90,32 +81,22 @@
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.DataContext>
<local:MapViewModel/>
</Grid.DataContext>
<map:Map x:Name="map" ZoomLevel="11" MaxZoomLevel="21" MouseWheelZoomDelta=".5"
Center="{Binding MapCenter}"
MapLayer="{Binding MapLayers.CurrentMapLayer}"
ViewportChanged="MapViewportChanged">
<map:Map x:Name="map"
MinZoomLevel="2" MaxZoomLevel="21" ZoomLevel="11" MouseWheelZoomDelta="1"
MapLayer="{Binding MapLayers[OpenStreetMap]}"
ViewportChanged="MapViewportChanged"
PointerMoved="MapPointerMoved"
PointerExited="MapPointerExited">
<map:Map.Center>
<map:Location Latitude="53.5" Longitude="8.2"/>
</map:Map.Center>
<Image x:Name="mapImage" Source="10_535_330.jpg" Opacity="0.5" Stretch="Fill">
<map:MapPanel.BoundingBox>
<map:BoundingBox South="53.54031" North="53.74871" West="8.08594" East="8.43750"/>
</map:MapPanel.BoundingBox>
</Image>
<map:MapGraticule x:Name="graticule" Opacity="0.6"
Visibility="{Binding IsChecked, ElementName=graticuleCheckBox}"/>
<map:MapScale HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
<!-- use ItemTemplate or ItemContainerStyle alternatively -->
<map:MapItemsControl ItemsSource="{Binding Polylines}"
ItemTemplate="{StaticResource PolylineItemTemplate}"/>
<!--<map:MapItemsControl ItemsSource="{Binding Polylines}"
ItemContainerStyle="{StaticResource PolylineItemStyle}"/>-->
<map:MapItemsControl ItemsSource="{Binding Points}"
ItemContainerStyle="{StaticResource PointItemStyle}"
@ -131,23 +112,30 @@
</map:Pushpin>
</map:Map>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border HorizontalAlignment="Right" VerticalAlignment="Bottom" Background="#AFFFFFFF">
<TextBlock Margin="4,2" FontSize="10" local:HyperlinkText.InlinesSource="{Binding MapLayer.Description, ElementName=map}"/>
</Border>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<CheckBox x:Name="graticuleCheckBox"
VerticalAlignment="Center" Content="Graticule"/>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Margin="6">
<local:MapLayersMenuButton
Margin="2" Padding="8" ToolTipService.ToolTip="Map Layers and Overlays"
Map="{Binding ElementName=map}"
MapLayers="{Binding MapLayers}"
MapOverlays="{Binding MapOverlays}"/>
<CheckBox VerticalAlignment="Center" Content="Seamarks"
Checked="SeamarksChecked" Unchecked="SeamarksUnchecked"/>
<!--<local:MapProjectionsMenuButton
Margin="2" Padding="8" ToolTipService.ToolTip="Map Projections"
Map="{Binding ElementName=map}"
MapProjections="{Binding MapProjections}"/>-->
<ComboBox Width="250" VerticalAlignment="Center" Margin="5"
ItemsSource="{Binding MapLayers.MapLayerNames}"
SelectedItem="{Binding MapLayers.CurrentMapLayerName, Mode=TwoWay}"/>
</StackPanel>
</Grid>
<Slider Orientation="Vertical" Margin="4,8" Height="100"
Minimum="{Binding MinZoomLevel, ElementName=map}"
Maximum="{Binding MaxZoomLevel, ElementName=map}"
Value="{Binding TargetZoomLevel, ElementName=map, Mode=TwoWay}"/>
</StackPanel>
<Border HorizontalAlignment="Center" VerticalAlignment="Top" Margin="4" Background="#AFFFFFFF">
<TextBlock x:Name="mouseLocation" Margin="4,2" Visibility="Collapsed"/>
</Border>
</Grid>
</Window>

View file

@ -1,8 +1,10 @@
using MapControl;
using MapControl.Caching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
@ -10,8 +12,6 @@ namespace SampleApplication
{
public sealed partial class MainWindow : Window
{
private readonly MapViewModel viewModel = new();
static MainWindow()
{
try
@ -39,8 +39,6 @@ namespace SampleApplication
Title = "XAML Map Control - WinUI Sample Application";
root.DataContext = viewModel;
if (TileImageLoader.Cache is ImageFileCache)
{
Activated += WindowActivated;
@ -55,19 +53,42 @@ namespace SampleApplication
await ((ImageFileCache)TileImageLoader.Cache).Clean();
}
private void SeamarksChecked(object sender, RoutedEventArgs e)
{
map.Children.Insert(map.Children.IndexOf(graticule), viewModel.MapLayers.SeamarksLayer);
}
private void SeamarksUnchecked(object sender, RoutedEventArgs e)
{
map.Children.Remove(viewModel.MapLayers.SeamarksLayer);
}
private void MapViewportChanged(object sender, ViewportChangedEventArgs e)
{
GC.Collect();
}
private void MapPointerMoved(object sender, PointerRoutedEventArgs e)
{
var location = map.ViewToLocation(e.GetCurrentPoint(map).Position);
var latitude = (int)Math.Round(location.Latitude * 60000d);
var longitude = (int)Math.Round(Location.NormalizeLongitude(location.Longitude) * 60000d);
var latHemisphere = 'N';
var lonHemisphere = 'E';
if (latitude < 0)
{
latitude = -latitude;
latHemisphere = 'S';
}
if (longitude < 0)
{
longitude = -longitude;
lonHemisphere = 'W';
}
mouseLocation.Text = string.Format(CultureInfo.InvariantCulture,
"{0} {1:00} {2:00.000}\n{3} {4:000} {5:00.000}",
latHemisphere, latitude / 60000, (latitude % 60000) / 1000d,
lonHemisphere, longitude / 60000, (longitude % 60000) / 1000d);
mouseLocation.Visibility = Visibility.Visible;
}
private void MapPointerExited(object sender, PointerRoutedEventArgs e)
{
mouseLocation.Visibility = Visibility.Collapsed;
mouseLocation.Text = string.Empty;
}
}
}

View file

@ -31,8 +31,8 @@
Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="WinUiApp"
Description="WinUiApp"
DisplayName="XAML Map Control Test Application"
Description="SampleApplication"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">

View file

@ -24,7 +24,7 @@
</ItemGroup>
<ItemGroup>
<Content Include="..\Shared\10_535_330.jpg" Link="10_535_330.jpg" />
<Content Include="..\Shared\10_535_330.jpg" Link="10_535_330.jpg"/>
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<ItemGroup />
</Project>

View file

@ -5,147 +5,114 @@
xmlns:local="clr-namespace:SampleApplication"
Title="XAML MapControl - WPF Sample Application" Height="600" Width="900"
Stylus.IsPressAndHoldEnabled="False">
<Window.Resources>
<DataTemplate x:Key="PolylineItemTemplate">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</DataTemplate>
<Style x:Key="PolylineItemStyle" TargetType="map:MapItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="PointItemStyle" TargetType="map:MapItem">
<EventSetter Event="TouchDown" Handler="MapItemTouchDown"/>
<Setter Property="AutoCollapse" Value="True"/>
<Setter Property="Location" Value="{Binding Location}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<Canvas>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="hoverPath"
Storyboard.TargetProperty="Opacity"
To="0.7" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath"
Storyboard.TargetProperty="Opacity"
To="0.7" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="selectedPath" Fill="White" Opacity="0">
<Path.Data>
<EllipseGeometry RadiusX="12" RadiusY="12"/>
</Path.Data>
</Path>
<Path x:Name="hoverPath" StrokeThickness="6" Stroke="White" Opacity="0">
<Path.Data>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</Path.Data>
</Path>
<Path StrokeThickness="2" Stroke="Gray" Fill="Transparent">
<Path.Data>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</Path.Data>
</Path>
<Grid Canvas.Left="15" Canvas.Top="-8">
<local:OutlinedText Margin="1" OutlineThickness="1.5" Text="{Binding Name}"
Background="{Binding Background, RelativeSource={RelativeSource AncestorType=map:MapBase}}"/>
</Grid>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="PushpinItemStyle" TargetType="map:MapItem">
<EventSetter Event="TouchDown" Handler="MapItemTouchDown"/>
<Setter Property="AutoCollapse" Value="True"/>
<Setter Property="Location" Value="{Binding Location}"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:Pushpin Content="{Binding Name}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Foreground" Value="OrangeRed"/>
</Trigger>
</Style.Triggers>
</Style>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<map:WebMercatorProjection x:Key="WebMercatorProjection"/>
<map:WorldMercatorProjection x:Key="WorldMercatorProjection"/>
<map:EquirectangularProjection x:Key="EquirectangularProjection"/>
<map:OrthographicProjection x:Key="OrthographicProjection"/>
<map:GnomonicProjection x:Key="GnomonicProjection"/>
<map:StereographicProjection x:Key="StereographicProjection"/>
</Window.Resources>
<Window.DataContext>
<local:MapViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.Resources>
<DataTemplate DataType="{x:Type local:PolylineItem}">
<map:MapPolyline Locations="{Binding Locations}" Stroke="Red" StrokeThickness="3"/>
</DataTemplate>
<Style x:Key="PointItemStyle" TargetType="map:MapItem">
<EventSetter Event="TouchDown" Handler="MapItemTouchDown"/>
<Setter Property="AutoCollapse" Value="True"/>
<Setter Property="Location" Value="{Binding Location}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<Canvas>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="hoverPath"
Storyboard.TargetProperty="Opacity"
To="0.7" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="selectedPath"
Storyboard.TargetProperty="Opacity"
To="0.7" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="selectedPath" Fill="White" Opacity="0">
<Path.Data>
<EllipseGeometry RadiusX="12" RadiusY="12"/>
</Path.Data>
</Path>
<Path x:Name="hoverPath" StrokeThickness="6" Stroke="White" Opacity="0">
<Path.Data>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</Path.Data>
</Path>
<Path StrokeThickness="2" Stroke="Gray" Fill="Transparent">
<Path.Data>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</Path.Data>
</Path>
<Grid Canvas.Left="15" Canvas.Top="-8">
<local:OutlinedText Margin="1" OutlineThickness="1.5" Text="{Binding Name}"
Background="{Binding Background, RelativeSource={RelativeSource AncestorType=map:MapBase}}"/>
</Grid>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="PushpinItemStyle" TargetType="map:MapItem">
<EventSetter Event="TouchDown" Handler="MapItemTouchDown"/>
<Setter Property="AutoCollapse" Value="True"/>
<Setter Property="Location" Value="{Binding Location}"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<map:Pushpin Content="{Binding Name}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Foreground" Value="OrangeRed"/>
</Trigger>
</Style.Triggers>
</Style>
<map:WebMercatorProjection x:Key="WebMercatorProjection"/>
<map:WorldMercatorProjection x:Key="WorldMercatorProjection"/>
<map:EquirectangularProjection x:Key="EquirectangularProjection"/>
<map:OrthographicProjection x:Key="OrthographicProjection"/>
<map:GnomonicProjection x:Key="GnomonicProjection"/>
<map:StereographicProjection x:Key="StereographicProjection"/>
</Grid.Resources>
<Grid.DataContext>
<local:MapViewModel/>
</Grid.DataContext>
<map:Map x:Name="map" ZoomLevel="11" MaxZoomLevel="21" MouseWheelZoomDelta="1"
Center="{Binding MapCenter}"
MapLayer="{Binding MapLayers.CurrentMapLayer}"
MapProjection="{Binding SelectedValue, ElementName=projectionComboBox,
FallbackValue={StaticResource WebMercatorProjection},
TargetNullValue={StaticResource WebMercatorProjection}}"
Center="53.5,8.2"
MapLayer="{Binding MapLayers[OpenStreetMap]}"
MouseLeftButtonDown="MapMouseLeftButtonDown"
MouseRightButtonDown="MapMouseRightButtonDown"
MouseMove="MapMouseMove" MouseLeave="MapMouseLeave"
ManipulationInertiaStarting="MapManipulationInertiaStarting">
<Image Source="10_535_330.jpg" Stretch="Fill"
Opacity="{Binding Value, ElementName=imageOpacitySlider}"
map:MapPanel.BoundingBox="53.54031,8.08594,53.74871,8.43750"/>
<map:MapGraticule x:Name="graticule" Opacity="0.6"
Visibility="{Binding IsChecked, ElementName=graticuleCheckBox,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
<map:MapScale HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
<!-- use ItemTemplate or ItemContainerStyle alternatively -->
<map:MapItemsControl ItemsSource="{Binding Polylines}"
ItemTemplate="{StaticResource PolylineItemTemplate}"/>
<!--<map:MapItemsControl ItemsSource="{Binding Polylines}"
ItemContainerStyle="{StaticResource PolylineItemStyle}"/>-->
<map:MapItemsControl ItemsSource="{Binding Polylines}"/>
<map:MapItemsControl ItemsSource="{Binding Points}"
ItemContainerStyle="{StaticResource PointItemStyle}"
@ -169,58 +136,34 @@
</map:Map>
<Border HorizontalAlignment="Right" VerticalAlignment="Bottom" Background="#AFFFFFFF">
<TextBlock Margin="4,2" FontSize="10" local:HyperlinkText.InlinesSource="{Binding MapLayer.Description, ElementName=map}"/>
<TextBlock Margin="4,2" FontSize="10"
local:HyperlinkText.InlinesSource="{Binding MapLayer.Description, ElementName=map}"/>
</Border>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="mouseLocation" Margin="5" VerticalAlignment="Bottom" FontFamily="Segoe UI Mono"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<StackPanel Margin="5">
<TextBlock Text="Zoom Level" Margin="0,0,0,2" HorizontalAlignment="Center" FontSize="10"/>
<Slider AutoToolTipPlacement="TopLeft" AutoToolTipPrecision="0"
Width="75" VerticalAlignment="Center" SmallChange="0.01"
Minimum="{Binding MinZoomLevel, ElementName=map}"
Maximum="{Binding MaxZoomLevel, ElementName=map}"
Value="{Binding TargetZoomLevel, ElementName=map}"/>
</StackPanel>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Margin="6">
<local:MapLayersMenuButton
Margin="2" Padding="6" FontSize="16" ToolTip="Map Layers and Overlays"
Map="{Binding ElementName=map}"
MapLayers="{Binding MapLayers}"
MapOverlays="{Binding MapOverlays}"/>
<StackPanel Margin="5">
<TextBlock Text="Heading" Margin="0,0,0,2" HorizontalAlignment="Center" FontSize="10"/>
<Slider AutoToolTipPlacement="TopLeft" AutoToolTipPrecision="0"
Width="75" VerticalAlignment="Center" SmallChange="5" LargeChange="45"
Minimum="0" Maximum="360" Value="{Binding TargetHeading, ElementName=map}"/>
</StackPanel>
<!--<local:MapProjectionsMenuButton
Margin="2" Padding="6" FontSize="16" ToolTip="Map Projections"
Map="{Binding ElementName=map}"
MapProjections="{Binding MapProjections}"/>-->
<StackPanel Margin="5">
<TextBlock Text="Image Opacity" Margin="0,0,0,2" HorizontalAlignment="Center" FontSize="10"/>
<Slider x:Name="imageOpacitySlider" AutoToolTipPlacement="TopLeft" AutoToolTipPrecision="1"
Width="75" VerticalAlignment="Center" Minimum="0" Maximum="1" Value="0.5"/>
</StackPanel>
<Slider Orientation="Vertical" Margin="8" Height="100"
Minimum="{Binding MinZoomLevel, ElementName=map}"
Maximum="{Binding MaxZoomLevel, ElementName=map}"
Value="{Binding TargetZoomLevel, ElementName=map}"
SmallChange="0.1"
AutoToolTipPlacement="BottomRight" AutoToolTipPrecision="0"/>
</StackPanel>
<CheckBox x:Name="graticuleCheckBox" ToolTip="Graticule Overlay" Margin="8"
VerticalAlignment="Bottom" Content="Graticule"/>
<CheckBox ToolTip="Seamarks Overlay" Margin="8" VerticalAlignment="Bottom" Content="Seamarks"
Checked="SeamarksChecked" Unchecked="SeamarksUnchecked"/>
<ComboBox ToolTip="Map Layer" Width="200" Margin="5" VerticalAlignment="Bottom"
ItemsSource="{Binding MapLayers.MapLayerNames}"
SelectedItem="{Binding MapLayers.CurrentMapLayerName}"/>
<ComboBox x:Name="projectionComboBox" ToolTip="Map Projection" Width="120" Margin="5" VerticalAlignment="Bottom"
SelectedValuePath="Tag" SelectedIndex="0">
<ComboBoxItem Content="Web Mercator" Tag="{StaticResource WebMercatorProjection}"/>
<ComboBoxItem Content="World Mercator" Tag="{StaticResource WorldMercatorProjection}"/>
<ComboBoxItem Content="Equirectangular" Tag="{StaticResource EquirectangularProjection}"/>
<ComboBoxItem Content="Orthographic" Tag="{StaticResource OrthographicProjection}"/>
<ComboBoxItem Content="Gnomonic" Tag="{StaticResource GnomonicProjection}"/>
<ComboBoxItem Content="Stereographic" Tag="{StaticResource StereographicProjection}"/>
</ComboBox>
</StackPanel>
</Grid>
<local:OutlinedText
x:Name="mouseLocation" Margin="4"
Background="{Binding Background, ElementName=map}"
Foreground="{Binding Foreground, ElementName=map}"
HorizontalAlignment="Center" VerticalAlignment="Top"/>
</Grid>
</Window>

View file

@ -1,12 +1,12 @@
using System;
using MapControl;
using MapControl.Caching;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using MapControl;
using MapControl.Caching;
namespace SampleApplication
{
@ -108,15 +108,5 @@ namespace SampleApplication
mapItem.IsSelected = !mapItem.IsSelected;
e.Handled = true;
}
private void SeamarksChecked(object sender, RoutedEventArgs e)
{
map.Children.Insert(map.Children.IndexOf(graticule), ((MapViewModel)DataContext).MapLayers.SeamarksLayer);
}
private void SeamarksUnchecked(object sender, RoutedEventArgs e)
{
map.Children.Remove(((MapViewModel)DataContext).MapLayers.SeamarksLayer);
}
}
}

View file

@ -1,4 +1,5 @@
using System.Windows;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
@ -6,36 +7,49 @@ namespace SampleApplication
{
public class OutlinedText : FrameworkElement
{
private GlyphRun glyphRun;
private FormattedText text;
private Geometry outline;
public static readonly DependencyProperty TextProperty = TextBlock.TextProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
typeof(OutlinedText),
new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontSizeProperty = TextBlock.FontSizeProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
typeof(OutlinedText),
new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontFamilyProperty = TextBlock.FontFamilyProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
typeof(OutlinedText),
new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontStyleProperty = TextBlock.FontStyleProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
typeof(OutlinedText),
new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontWeightProperty = TextBlock.FontWeightProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
typeof(OutlinedText),
new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });
public static readonly DependencyProperty FontStretchProperty = TextBlock.FontStretchProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
typeof(OutlinedText),
new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });
public static readonly DependencyProperty ForegroundProperty = TextBlock.ForegroundProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
typeof(OutlinedText),
new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });
public static readonly DependencyProperty BackgroundProperty = TextBlock.BackgroundProperty.AddOwner(
typeof(OutlinedText), new FrameworkPropertyMetadata(Brushes.White, (o, e) => ((OutlinedText)o).glyphRun = null) { AffectsMeasure = true });
typeof(OutlinedText),
new FrameworkPropertyMetadata(Brushes.White, (o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });
public static readonly DependencyProperty OutlineThicknessProperty = DependencyProperty.Register(
"OutlineThickness", typeof(double), typeof(OutlinedText),
new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => ((OutlinedText)o).glyphRun = null));
nameof(OutlineThickness), typeof(double), typeof(OutlinedText),
new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => ((OutlinedText)o).text = null));
public OutlinedText()
{
IsHitTestVisible = false;
}
public string Text
{
@ -93,23 +107,23 @@ namespace SampleApplication
protected override Size MeasureOverride(Size availableSize)
{
return CheckGlyphRun() ? outline.Bounds.Size : new Size();
return ValidateText() ? outline.Bounds.Size : new Size();
}
protected override void OnRender(DrawingContext drawingContext)
{
if (CheckGlyphRun())
if (ValidateText())
{
var location = outline.Bounds.Location;
drawingContext.PushTransform(new TranslateTransform(-location.X, -location.Y));
drawingContext.DrawGeometry(Background, null, outline);
drawingContext.DrawGlyphRun(Foreground, glyphRun);
drawingContext.DrawText(text, new Point());
}
}
private bool CheckGlyphRun()
private bool ValidateText()
{
if (glyphRun == null)
if (text == null)
{
if (string.IsNullOrEmpty(Text))
{
@ -123,19 +137,22 @@ namespace SampleApplication
return false;
}
var glyphIndices = new ushort[Text.Length];
var advanceWidths = new double[Text.Length];
text = new FormattedText(Text,
CultureInfo.InvariantCulture,
FlowDirection.LeftToRight,
typeface,
FontSize,
Foreground,
VisualTreeHelper.GetDpi(this).PixelsPerDip);
for (int i = 0; i < Text.Length; i++)
{
var glyphIndex = glyphTypeface.CharacterToGlyphMap[Text[i]];
glyphIndices[i] = glyphIndex;
advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * FontSize;
}
glyphRun = new GlyphRun(glyphTypeface, 0, false, FontSize, 1f, glyphIndices, new Point(), advanceWidths, null, null, null, null, null, null);
outline = glyphRun.BuildGeometry().GetWidenedPathGeometry(new Pen(null, OutlineThickness * 2d));
outline = text.BuildGeometry(new Point()).GetWidenedPathGeometry(
new Pen
{
Thickness = OutlineThickness * 2d,
LineJoin = PenLineJoin.Round,
StartLineCap = PenLineCap.Round,
EndLineCap = PenLineCap.Round
});
}
return true;

View file

@ -16,12 +16,18 @@
<Compile Include="..\Shared\*.cs" />
</ItemGroup>
<ItemGroup>
<Compile Remove="..\Shared\MapLayers.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MapControl\WPF\MapControl.WPF.csproj" />
</ItemGroup>
<ItemGroup>
<Resource Include="..\Shared\10_535_330.jpg" Link="10_535_330.jpg" />
<Content Include="..\Shared\10_535_330.jpg" Link="10_535_330.jpg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
@ -29,4 +35,9 @@
<Reference Include="System.Runtime.Caching" />
</ItemGroup>
<ItemGroup>
<None Update="App.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>