控件可以实现:
在知道显示的地图区域时:
地图控件所需的数据结构:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
namespace SmartScreen.Controls.Models
{
public class MapItem
{
public Brush Fill { get; protected set; }
}
public class GeoPint
{
public GeoPint(double lat, double lon)
{
Latitude = lat;
Longitude = lon;
}
public double Latitude { get; set; }
public double Longitude { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SmartScreen.Controls.Models
{
public class MapPolyLineItem : MapItem
{
public double Longitude { get; set; }
public double Latitude { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
namespace SmartScreen.Controls.Models
{
public class MapPushPinItem : MapItem
{
public string Title { get; set; }
public double Longitude { get; set; }
public double Latitude { get; set; }
private int warnningStatus;
///
/// 警示状态,0:安全 1:预警 -1:警告
///
public int WarnningStatus
{
get { return warnningStatus; }
set
{
warnningStatus = value;
if (warnningStatus == 0)
{
Fill = (SolidColorBrush)Application.Current.FindResource("SafeColor");
}
else if (warnningStatus == 1)
{
Fill = (SolidColorBrush)Application.Current.FindResource("WarnningColor");
}
else if (warnningStatus == -1)
{
Fill = (SolidColorBrush)Application.Current.FindResource("DangerColor");
}
}
}
}
}
地图控件XAML(可以去掉zoomPanel:ZoomAndPanControl):
<UserControl x:Class="SmartScreen.Controls.MapControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SmartScreen.Controls" xmlns:zoomPanel="clr-namespace:ZoomAndPan;assembly=ZoomAndPan"
mc:Ignorable="d"
x:Name="root"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="Line">
"Stroke" Value="{StaticResource DangerColor}"/>
"StrokeThickness" Value="2"/>
"Effect">
"Gray" BlurRadius="8" ShadowDepth="0"/>
Style>
UserControl.Resources>
<Grid>
<zoomPanel:ZoomAndPanControl x:Name="zoomPanel"
MouseWheel="map_MouseWheel"
MaxContentScale="2.5"
MinContentScale="1"
MouseLeftButtonDown="Grid_MouseLeftButtonDown"
MouseLeftButtonUp="Grid_MouseLeftButtonUp"
MouseMove="Grid_MouseMove">
<zoomPanel:ZoomAndPanControl.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="trans"/>
<ScaleTransform x:Name="scale"/>
TransformGroup>
zoomPanel:ZoomAndPanControl.RenderTransform>
<Grid>
<Image x:Name="bottomImage" Width="{Binding ElementName=root,Path=ActualWidth}" Height="{Binding ElementName=root,Path=ActualHeight}"
Stretch="Fill" RenderOptions.BitmapScalingMode="Fant"/>
<Canvas x:Name="PushPincanvas">
Canvas>
<Canvas x:Name="PolyLinecanvas">
Canvas>
Grid>
zoomPanel:ZoomAndPanControl>
Grid>
UserControl>
地图控件后台代码:
using SmartScreen.Controls.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace SmartScreen.Controls
{
///
/// MapControl.xaml 的交互逻辑
///
public partial class MapControl : UserControl
{
public MapControl()
{
InitializeComponent();
SizeChanged += (s, e) => { RefreshPushPin(); RefreshPolyline(); };
}
///
/// 地图图片的路径
///
public Uri MapPath
{
get { return (Uri)GetValue(MapPathProperty); }
set { SetValue(MapPathProperty, value); }
}
public static readonly DependencyProperty MapPathProperty =
DependencyProperty.Register("MapPath", typeof(Uri), typeof(MapControl), new PropertyMetadata(default(Uri), OnMapPathChanged));
private static void OnMapPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MapControl ctrl = (MapControl)d;
ctrl.bottomImage.Source = new BitmapImage(ctrl.MapPath);
}
///
/// 左上角经纬度
///
public Point LeftTop
{
get { return (Point)GetValue(LeftTopProperty); }
set { SetValue(LeftTopProperty, value); }
}
public static readonly DependencyProperty LeftTopProperty =
DependencyProperty.Register("LeftTop", typeof(Point), typeof(MapControl), new PropertyMetadata(default(Point), OnMapReDrawChanged));
///
/// 右下角经纬度
///
public Point RightBottom
{
get { return (Point)GetValue(RightBottomProperty); }
set { SetValue(RightBottomProperty, value); }
}
public static readonly DependencyProperty RightBottomProperty =
DependencyProperty.Register("RightBottom", typeof(Point), typeof(MapControl), new PropertyMetadata(default(Point), OnMapReDrawChanged));
///
/// 地图显示的图钉部件
///
public ObservableCollection<MapPushPinItem> PushPins
{
get { return (ObservableCollection<MapPushPinItem>)GetValue(PushPinsProperty); }
set { SetValue(PushPinsProperty, value); }
}
public static readonly DependencyProperty PushPinsProperty =
DependencyProperty.Register("PushPins", typeof(ObservableCollection<MapPushPinItem>), typeof(MapControl), new PropertyMetadata(null, OnPushpinChanged));
private static void OnPushpinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MapControl map)
{
if (map.PushPins != null)
{
map.PushPins.CollectionChanged += (s, er) => map.RefreshPushPin();
}
map.RefreshPushPin();
}
}
///
/// 地图显示的轨迹线段
///
public ObservableCollection<MapPolyLineItem> PolyLines
{
get { return (ObservableCollection<MapPolyLineItem>)GetValue(PolyLinesProperty); }
set { SetValue(PolyLinesProperty, value); }
}
public static readonly DependencyProperty PolyLinesProperty =
DependencyProperty.Register("PolyLines", typeof(ObservableCollection<MapPolyLineItem>), typeof(MapControl), new PropertyMetadata(null, OnPolyLineChaned));
private static void OnPolyLineChaned(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MapControl map)
{
if (map.PolyLines != null)
{
map.PolyLines.CollectionChanged += (s, er) => map.RefreshPolyline();
}
map.RefreshPolyline();
}
}
private static void OnMapReDrawChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MapControl control = (MapControl)d;
control.RefreshPushPin();
control.RefreshPolyline();
}
///
/// 刷新标记点的显示
///
private void RefreshPushPin()
{
if (RightBottom == default(Point) || LeftTop == default(Point))
{
return;
}
var mapWidth = ActualWidth;
var mapHeight = ActualHeight;
var xRange = RightBottom.X - LeftTop.X;
var yRange = LeftTop.Y - RightBottom.Y;
if (PushPins != null)
{
PushPincanvas.Children.Clear();
foreach (var pushpin in PushPins)
{
// 先计算在X Y 轴上的位置
var canvasLeft = (pushpin.Longitude - LeftTop.X) / xRange * mapWidth;
var canvasTop = (LeftTop.Y - pushpin.Latitude) / yRange * mapHeight;
var pushpinCtrl = new MapPushPin();
pushpinCtrl.DataContext = pushpin;
Canvas.SetLeft(pushpinCtrl, canvasLeft);
Canvas.SetTop(pushpinCtrl, canvasTop);
PushPincanvas.Children.Add(pushpinCtrl);
}
}
}
///
/// 刷新轨迹的显示
///
private void RefreshPolyline()
{
if (RightBottom == default(Point) || LeftTop == default(Point))
{
return;
}
var mapWidth = ActualWidth;
var mapHeight = ActualHeight;
var xRange = RightBottom.X - LeftTop.X;
var yRange = LeftTop.Y - RightBottom.Y;
if (PolyLines != null)
{
PolyLinecanvas.Children.Clear();
MapPolyLineItem lastPoint = null;
foreach (var polyline in PolyLines)
{
if (lastPoint == null)
{
lastPoint = polyline;
continue;
}
Line line = new Line();
line.DataContext = polyline;
line.X1 = 0;
line.Y1 = 0;
var canvasLeft = (lastPoint.Longitude - LeftTop.X) / xRange * mapWidth;
var canvasTop = (LeftTop.Y - lastPoint.Latitude) / yRange * mapHeight;
var point2x = (polyline.Longitude - LeftTop.X) / xRange * mapWidth - canvasLeft;
var point2y = (LeftTop.Y - polyline.Latitude) / yRange * mapHeight - canvasTop;
line.X2 = point2x;
line.Y2 = point2y;
Canvas.SetLeft(line, canvasLeft);
Canvas.SetTop(line, canvasTop);
PolyLinecanvas.Children.Add(line);
lastPoint = polyline;
}
}
}
#region 缩放移动
public FrameworkElement Container
{
get { return (FrameworkElement)GetValue(ContainerProperty); }
set { SetValue(ContainerProperty, value); }
}
public static readonly DependencyProperty ContainerProperty =
DependencyProperty.Register("Container", typeof(FrameworkElement), typeof(MapControl), new PropertyMetadata(null));
public bool EnableZoomPan
{
get { return (bool)GetValue(EnableZoomPanProperty); }
set { SetValue(EnableZoomPanProperty, value); }
}
public static readonly DependencyProperty EnableZoomPanProperty =
DependencyProperty.Register("EnableZoomPan", typeof(bool), typeof(MapControl), new PropertyMetadata(false));
Point lastPoint = default;
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!EnableZoomPan)
{
return;
}
lastPoint = e.GetPosition(Container);
zoomPanel.CaptureMouse();
}
private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!EnableZoomPan)
{
return;
}
lastPoint = default;
zoomPanel.ReleaseMouseCapture();
}
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
if (!EnableZoomPan)
{
return;
}
if (e.LeftButton == MouseButtonState.Pressed)
{
var nowPoint = e.GetPosition(Container);
if (lastPoint == default)
{
lastPoint = nowPoint;
return;
}
var offsetX = (nowPoint.X - lastPoint.X) / scale.ScaleX;
var offsetY = (nowPoint.Y - lastPoint.Y) / scale.ScaleX;
lastPoint = nowPoint;
trans.X += offsetX;
trans.Y += offsetY;
}
}
private void map_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (!EnableZoomPan)
{
return;
}
var nowMouse = e.GetPosition(this);
if (e.Delta > 0)
{
zoomPanel.ZoomIn(nowMouse);
}
else
{
zoomPanel.ZoomOut(nowMouse);
}
if (zoomPanel.ContentScale == 1)
{
trans.X = 0;
trans.Y = 0;
}
}
#endregion
}
}