2017-06-25 23:05:48 +02:00
|
|
|
|
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
2023-01-03 15:12:53 +01:00
|
|
|
|
// Copyright © 2023 Clemens Fischer
|
2012-11-23 16:16:09 +01:00
|
|
|
|
// Licensed under the Microsoft Public License (Ms-PL)
|
|
|
|
|
|
|
2018-11-13 21:35:48 +01:00
|
|
|
|
using System;
|
2021-06-14 21:41:37 +02:00
|
|
|
|
#if WINUI
|
|
|
|
|
|
using Windows.Foundation;
|
|
|
|
|
|
using Microsoft.UI.Xaml;
|
|
|
|
|
|
using Microsoft.UI.Xaml.Controls;
|
|
|
|
|
|
using Microsoft.UI.Xaml.Data;
|
2022-11-06 15:27:39 +01:00
|
|
|
|
using Microsoft.UI.Xaml.Media;
|
2021-11-17 23:17:11 +01:00
|
|
|
|
#elif UWP
|
2018-11-13 21:35:48 +01:00
|
|
|
|
using Windows.Foundation;
|
2013-08-17 08:41:11 +02:00
|
|
|
|
using Windows.UI.Xaml;
|
|
|
|
|
|
using Windows.UI.Xaml.Controls;
|
2021-01-11 22:35:17 +01:00
|
|
|
|
using Windows.UI.Xaml.Data;
|
2022-11-06 15:27:39 +01:00
|
|
|
|
using Windows.UI.Xaml.Media;
|
2017-09-05 20:57:17 +02:00
|
|
|
|
#else
|
|
|
|
|
|
using System.Windows;
|
|
|
|
|
|
using System.Windows.Controls;
|
2021-01-11 22:35:17 +01:00
|
|
|
|
using System.Windows.Data;
|
2022-11-06 15:27:39 +01:00
|
|
|
|
using System.Windows.Media;
|
2017-09-05 20:57:17 +02:00
|
|
|
|
#endif
|
2013-08-17 08:41:11 +02:00
|
|
|
|
|
2012-11-23 16:16:09 +01:00
|
|
|
|
namespace MapControl
|
|
|
|
|
|
{
|
2021-01-11 22:35:17 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Container class for an item in a MapItemsControl.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public partial class MapItem : ListBoxItem
|
|
|
|
|
|
{
|
2021-01-13 00:08:55 +01:00
|
|
|
|
/// <summary>
|
2021-01-16 18:32:31 +01:00
|
|
|
|
/// Gets/sets MapPanel.AutoCollapse.
|
2021-01-13 00:08:55 +01:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool AutoCollapse
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (bool)GetValue(AutoCollapseProperty);
|
|
|
|
|
|
set => SetValue(AutoCollapseProperty, value);
|
2021-01-13 00:08:55 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2021-01-16 18:32:31 +01:00
|
|
|
|
/// Gets/sets MapPanel.Location.
|
2021-01-13 00:08:55 +01:00
|
|
|
|
/// </summary>
|
2021-01-11 22:35:17 +01:00
|
|
|
|
public Location Location
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (Location)GetValue(LocationProperty);
|
|
|
|
|
|
set => SetValue(LocationProperty, value);
|
2021-01-11 22:35:17 +01:00
|
|
|
|
}
|
2022-11-06 15:27:39 +01:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets a Transform for scaling and rotating geometries
|
|
|
|
|
|
/// in map coordinates (meters) to view coordinates (pixels).
|
|
|
|
|
|
/// </summary>
|
2022-12-01 23:49:57 +01:00
|
|
|
|
public Transform MapTransform => mapTransform ?? (mapTransform = new MatrixTransform());
|
2022-11-06 15:27:39 +01:00
|
|
|
|
|
|
|
|
|
|
private MatrixTransform mapTransform;
|
|
|
|
|
|
|
|
|
|
|
|
protected override Size ArrangeOverride(Size bounds)
|
|
|
|
|
|
{
|
2022-11-30 22:18:45 +01:00
|
|
|
|
// If MapTransform is used, update its Matrix property.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (mapTransform != null)
|
2022-11-06 15:27:39 +01:00
|
|
|
|
{
|
|
|
|
|
|
var parentMap = (VisualTreeHelper.GetParent(this) as MapPanel)?.ParentMap;
|
|
|
|
|
|
|
|
|
|
|
|
if (parentMap != null && Location != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var scale = parentMap.GetScale(Location);
|
|
|
|
|
|
var matrix = new Matrix(scale.X, 0d, 0d, scale.Y, 0d, 0d);
|
|
|
|
|
|
matrix.Rotate(parentMap.ViewTransform.Rotation);
|
|
|
|
|
|
mapTransform.Matrix = matrix;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return base.ArrangeOverride(bounds);
|
|
|
|
|
|
}
|
2022-11-03 21:16:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Manages a collection of selectable items on a Map.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public partial class MapItemsControl : ListBox
|
|
|
|
|
|
{
|
|
|
|
|
|
public static readonly DependencyProperty LocationMemberPathProperty = DependencyProperty.Register(
|
|
|
|
|
|
nameof(LocationMemberPath), typeof(string), typeof(MapItemsControl), new PropertyMetadata(null));
|
2021-01-11 22:35:17 +01:00
|
|
|
|
|
2021-01-13 00:08:55 +01:00
|
|
|
|
/// <summary>
|
2022-11-03 21:16:46 +01:00
|
|
|
|
/// Path to a source property for binding the Location property of MapItem containers.
|
2021-01-13 00:08:55 +01:00
|
|
|
|
/// </summary>
|
2021-01-11 22:35:17 +01:00
|
|
|
|
public string LocationMemberPath
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (string)GetValue(LocationMemberPathProperty);
|
|
|
|
|
|
set => SetValue(LocationMemberPathProperty, value);
|
2021-01-11 22:35:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-08-17 08:41:11 +02:00
|
|
|
|
protected override DependencyObject GetContainerForItemOverride()
|
|
|
|
|
|
{
|
|
|
|
|
|
return new MapItem();
|
|
|
|
|
|
}
|
2014-06-11 23:03:13 +02:00
|
|
|
|
|
|
|
|
|
|
protected override bool IsItemItsOwnContainerOverride(object item)
|
|
|
|
|
|
{
|
|
|
|
|
|
return item is MapItem;
|
|
|
|
|
|
}
|
2018-11-10 22:38:46 +01:00
|
|
|
|
|
2022-11-03 21:16:46 +01:00
|
|
|
|
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.PrepareContainerForItemOverride(element, item);
|
|
|
|
|
|
|
|
|
|
|
|
if (LocationMemberPath != null && element is MapItem mapItem)
|
|
|
|
|
|
{
|
|
|
|
|
|
mapItem.SetBinding(MapItem.LocationProperty,
|
|
|
|
|
|
new Binding
|
|
|
|
|
|
{
|
|
|
|
|
|
Path = new PropertyPath(LocationMemberPath),
|
|
|
|
|
|
Source = item
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.ClearContainerForItemOverride(element, item);
|
|
|
|
|
|
|
|
|
|
|
|
if (LocationMemberPath != null && element is MapItem mapItem)
|
|
|
|
|
|
{
|
|
|
|
|
|
mapItem.ClearValue(MapItem.LocationProperty);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-21 22:18:16 +02:00
|
|
|
|
public void SelectItems(Predicate<object> predicate)
|
2018-11-13 21:35:48 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (SelectionMode == SelectionMode.Single)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new InvalidOperationException("SelectionMode must not be Single");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var item in Items)
|
|
|
|
|
|
{
|
|
|
|
|
|
var selected = predicate(item);
|
|
|
|
|
|
|
|
|
|
|
|
if (selected != SelectedItems.Contains(item))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (selected)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedItems.Add(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedItems.Remove(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-21 22:18:16 +02:00
|
|
|
|
public void SelectItemsByLocation(Predicate<Location> predicate)
|
2018-11-13 21:35:48 +01:00
|
|
|
|
{
|
|
|
|
|
|
SelectItems(item =>
|
|
|
|
|
|
{
|
2018-11-14 17:53:37 +01:00
|
|
|
|
var loc = MapPanel.GetLocation(ContainerFromItem(item));
|
2018-11-13 21:35:48 +01:00
|
|
|
|
return loc != null && predicate(loc);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-21 22:18:16 +02:00
|
|
|
|
public void SelectItemsByPosition(Predicate<Point> predicate)
|
2018-11-13 21:35:48 +01:00
|
|
|
|
{
|
|
|
|
|
|
SelectItems(item =>
|
|
|
|
|
|
{
|
2020-03-28 21:53:38 +01:00
|
|
|
|
var pos = MapPanel.GetViewPosition(ContainerFromItem(item));
|
2018-11-13 21:35:48 +01:00
|
|
|
|
return pos.HasValue && predicate(pos.Value);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void SelectItemsInRect(Rect rect)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectItemsByPosition(p => rect.Contains(p));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-14 17:53:37 +01:00
|
|
|
|
protected internal void OnItemClicked(FrameworkElement mapItem, bool controlKey, bool shiftKey)
|
2018-11-10 22:38:46 +01:00
|
|
|
|
{
|
2018-11-14 17:53:37 +01:00
|
|
|
|
var item = ItemFromContainer(mapItem);
|
2018-11-13 21:35:48 +01:00
|
|
|
|
|
|
|
|
|
|
if (SelectionMode == SelectionMode.Single)
|
|
|
|
|
|
{
|
2022-11-30 22:18:45 +01:00
|
|
|
|
// Single -> set only SelectedItem.
|
2018-11-13 21:35:48 +01:00
|
|
|
|
|
|
|
|
|
|
if (SelectedItem != item)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedItem = item;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (controlKey)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedItem = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (SelectionMode == SelectionMode.Multiple || controlKey)
|
|
|
|
|
|
{
|
2022-11-30 22:18:45 +01:00
|
|
|
|
// Multiple or Extended with Ctrl -> toggle item in SelectedItems.
|
2018-11-13 21:35:48 +01:00
|
|
|
|
|
|
|
|
|
|
if (SelectedItems.Contains(item))
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedItems.Remove(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedItems.Add(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (shiftKey && SelectedItem != null)
|
|
|
|
|
|
{
|
2022-11-30 22:18:45 +01:00
|
|
|
|
// Extended with Shift -> select items in view rectangle.
|
2018-11-13 21:35:48 +01:00
|
|
|
|
|
2020-03-28 21:53:38 +01:00
|
|
|
|
var p1 = MapPanel.GetViewPosition(ContainerFromItem(SelectedItem));
|
|
|
|
|
|
var p2 = MapPanel.GetViewPosition(mapItem);
|
2018-11-13 21:35:48 +01:00
|
|
|
|
|
|
|
|
|
|
if (p1.HasValue && p2.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectItemsInRect(new Rect(p1.Value, p2.Value));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (SelectedItem != item)
|
|
|
|
|
|
{
|
2022-11-30 22:18:45 +01:00
|
|
|
|
// Extended without Control or Shift -> set selected item.
|
2018-11-13 21:35:48 +01:00
|
|
|
|
|
|
|
|
|
|
SelectedItem = item;
|
|
|
|
|
|
}
|
2018-11-10 22:38:46 +01:00
|
|
|
|
}
|
2012-11-23 16:16:09 +01:00
|
|
|
|
}
|
2013-08-17 08:41:11 +02:00
|
|
|
|
}
|