Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public override string ToString()
new GalleryPageFactory(() => new ImageButtonControlPage(), "ImageButton Feature Matrix"),
new GalleryPageFactory(() => new BoxViewControlPage(), "BoxView Feature Matrix"),
new GalleryPageFactory(() => new EditorControlPage(), "Editor Feature Matrix"),
new GalleryPageFactory(() => new MapControlPage(), "Map Feature Matrix"),
};

public CorePageView(Page rootPage)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:maps="clr-namespace:Microsoft.Maui.Controls.Maps;assembly=Microsoft.Maui.Controls.Maps"
xmlns:local="clr-namespace:Maui.Controls.Sample"
x:DataType="local:MapViewModel"
x:Class="Maui.Controls.Sample.MapControlMainPage"
Title="MapControlPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Options"
Clicked="NavigateToOptionsPage_Clicked"
AutomationId="Options"/>
</ContentPage.ToolbarItems>

<StackLayout Padding="20"
Spacing="20">
<maps:Map x:Name="TestMap"
AutomationId="MapView"
IsShowingUser="{Binding IsShowingUser}"
IsScrollEnabled="{Binding IsScrollEnabled}"
IsTrafficEnabled="{Binding IsTrafficEnabled}"
IsZoomEnabled="{Binding IsZoomEnabled}"
IsVisible="{Binding IsVisible}"
MapType="{Binding MapType}"
MapClicked="OnMapClicked"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"/>
<!-- Map Clicked and Marker Clicked Event Labels -->
<StackLayout Spacing="2"
Padding="10,0,10,0">
<Label x:Name="MapClickedLabel"
Text="Map Clicked: "
FontSize="10"
TextColor="DarkBlue"
AutomationId="MapClickedLabel"/>
<Label x:Name="MarkerClickedLabel"
Text="Marker Clicked: "
FontSize="10"
TextColor="DarkBlue"
AutomationId="MarkerClickedLabel"/>
<Label Text="{Binding ItemsSource, StringFormat='ItemsSource: {0}'}"
FontSize="10"
TextColor="DarkGreen"/>
<Label Text="{Binding ItemTemplate, StringFormat='ItemTemplate: {0}'}"
FontSize="10"
TextColor="DarkGreen"/>
<Label Text="{Binding ItemTemplateSelector, StringFormat='ItemTemplateSelector: {0}'}"
FontSize="10"
TextColor="DarkGreen"/>
<Label Text="{Binding VisibleRegion.LatitudeDegrees, StringFormat='VisibleRegion LatitudeDegrees: {0.00000000}'}"
FontSize="10"
AutomationId="VisibleRegionLatitudeDegrees"
TextColor="DarkGreen"/>
<Label Text="{Binding VisibleRegion.LongitudeDegrees, StringFormat='VisibleRegion LongitudeDegrees: {0.00000000}'}"
FontSize="10"
AutomationId="VisibleRegionLongitudeDegrees"
TextColor="DarkGreen"/>
</StackLayout>
</StackLayout>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
using System.Collections.Specialized;
using System.ComponentModel;
using Microsoft.Maui.Controls.Maps;

namespace Maui.Controls.Sample;

public class MapControlPage : NavigationPage
{
private MapViewModel _viewModel;
public MapControlPage()
{
_viewModel = new MapViewModel();
PushAsync(new MapControlMainPage(_viewModel));
}
}

public partial class MapControlMainPage : ContentPage
{
private MapViewModel _viewModel;
public MapControlMainPage(MapViewModel viewModel)
{

InitializeComponent();
_viewModel = viewModel;
BindingContext = _viewModel;

// Set initial label text to 'Not clicked'
var mapLabel = this.FindByName<Microsoft.Maui.Controls.Label>("MapClickedLabel");
if (mapLabel != null)
mapLabel.Text = "Map Clicked: Not clicked";
var markerLabel = this.FindByName<Microsoft.Maui.Controls.Label>("MarkerClickedLabel");
if (markerLabel != null)
markerLabel.Text = "Marker Clicked: Not clicked";

// Handle MapElements collection changes manually since it's not directly bindable
_viewModel.MapElements.CollectionChanged += OnMapElementsCollectionChanged;

// Note: Pins collection changes are handled through ItemsSource binding
// Manual Pins handling is only needed when ItemsSource is null
_viewModel.Pins.CollectionChanged += OnPinsCollectionChanged;
_viewModel.PropertyChanged += OnViewModelPropertyChanged;

// ItemsSource, ItemTemplate, and ItemTemplateSelector are already bound in XAML
// No need for programmatic binding as it can cause conflicts

// Set initial map region to Pearl City
TestMap.MoveToRegion(_viewModel.InitialRegion);

// Ensure ItemTemplate and ItemTemplateSelector are synchronized with ViewModel
if (_viewModel.ItemTemplate != null)
{
TestMap.ItemTemplate = _viewModel.ItemTemplate;
}
if (_viewModel.ItemTemplateSelector != null)
{
TestMap.ItemTemplateSelector = _viewModel.ItemTemplateSelector;
}

// Subscribe to map events to sync VisibleRegion back to ViewModel
TestMap.PropertyChanged += OnMapPropertyChanged;

// Subscribe to MessagingCenter for label updates
Microsoft.Maui.Controls.MessagingCenter.Subscribe<object, string>(this, "MapClickedLabelUpdate", (sender, value) =>
{
var label = this.FindByName<Microsoft.Maui.Controls.Label>("MapClickedLabel");
if (label != null)
label.Text = value;
});
Microsoft.Maui.Controls.MessagingCenter.Subscribe<object, string>(this, "MarkerClickedLabelUpdate", (sender, value) =>
{
var label = this.FindByName<Microsoft.Maui.Controls.Label>("MarkerClickedLabel");
if (label != null)
label.Text = value;
});

TestMap.MapClicked += OnMapClicked;

}

private void OnMapElementsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (MapElement element in e.NewItems)
{
TestMap.MapElements.Add(element);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (MapElement element in e.OldItems)
{
TestMap.MapElements.Remove(element);
}
}
else if (e.Action == NotifyCollectionChangedAction.Reset)
{
TestMap.MapElements.Clear();
}
}

private void OnPinsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Only handle pins manually when ItemsSource is null
// When ItemsSource is set, the binding handles pin display automatically
if (_viewModel.ItemsSource == null)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (Pin pin in e.NewItems)
{
TestMap.Pins.Add(pin);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (Pin pin in e.OldItems)
{
TestMap.Pins.Remove(pin);
}
}
else if (e.Action == NotifyCollectionChangedAction.Reset)
{
TestMap.Pins.Clear();
}
}
}

private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(_viewModel.ItemsSource))
{
TestMap.Pins.Clear(); // Clear existing pins if any
TestMap.ItemsSource = _viewModel.ItemsSource;
}
else if (e.PropertyName == nameof(_viewModel.ItemTemplate))
{
// When ItemTemplate changes in ViewModel, update the actual Map
TestMap.ItemTemplate = _viewModel.ItemTemplate;
}
else if (e.PropertyName == nameof(_viewModel.ItemTemplateSelector))
{
TestMap.ItemTemplate = null;
// When ItemTemplateSelector changes in ViewModel, update the actual Map
TestMap.ItemTemplateSelector = _viewModel.ItemTemplateSelector;
}
else if (e.PropertyName == nameof(_viewModel.VisibleRegion))
{
// When VisibleRegion changes in ViewModel, update the actual Map
if (_viewModel.VisibleRegion != null)
{
TestMap.MoveToRegion(_viewModel.VisibleRegion);
}
}
}

private void OnMapPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Update ViewModel's VisibleRegion when the map's VisibleRegion changes
if (e.PropertyName == "VisibleRegion" && TestMap.VisibleRegion != null)
{
// Only update if it's different to avoid infinite loops
if (_viewModel.VisibleRegion != TestMap.VisibleRegion)
{
_viewModel.VisibleRegion = TestMap.VisibleRegion;
}
}
}

private Pin _lastClickPin;
private void OnMapClicked(object sender, Microsoft.Maui.Controls.Maps.MapClickedEventArgs e)
{
var label = this.FindByName<Microsoft.Maui.Controls.Label>("MapClickedLabel");
if (label != null)
label.Text = $"Map Clicked: Latitude: {e.Location.Latitude:F6}, Longitude: {e.Location.Longitude:F6}";

// Remove previous click marker if it exists
if (_lastClickPin != null)
{
if (TestMap.Pins.Contains(_lastClickPin))
TestMap.Pins.Remove(_lastClickPin);
_lastClickPin = null;
}

// Add a new pin at the clicked location
var clickPin = new Pin
{
Label = "Clicked Location",
Location = e.Location,
Address = $"Lat: {e.Location.Latitude:F6}, Lng: {e.Location.Longitude:F6}",
Type = PinType.Place
};
// Attach MarkerClicked and InfoWindowClicked events
clickPin.MarkerClicked += OnPinMarkerClicked;
clickPin.InfoWindowClicked += OnPinInfoWindowClicked;
TestMap.Pins.Add(clickPin);
_lastClickPin = clickPin;
}
private void OnPinMarkerClicked(object sender, PinClickedEventArgs e)
{
var label = this.FindByName<Microsoft.Maui.Controls.Label>("MarkerClickedLabel");
if (label != null)
label.Text = "Marker Clicked: Pin tapped (info window will show)";
e.HideInfoWindow = false;
}

private void OnPinInfoWindowClicked(object sender, PinClickedEventArgs e)
{
var label = this.FindByName<Microsoft.Maui.Controls.Label>("MarkerClickedLabel");
if (label != null)
label.Text = "Marker Clicked: Info window tapped (info window will hide)";
e.HideInfoWindow = true;
}

public void ResetClickedLocationPinAndLabel()
{
// Remove the clicked location pin if it exists
if (_lastClickPin != null)
{
if (TestMap.Pins.Contains(_lastClickPin))
TestMap.Pins.Remove(_lastClickPin);
_lastClickPin = null;
}
// Reset the labels to 'Not clicked'
var mapLabel = this.FindByName<Microsoft.Maui.Controls.Label>("MapClickedLabel");
if (mapLabel != null)
mapLabel.Text = "Map Clicked: Not clicked";
var markerLabel = this.FindByName<Microsoft.Maui.Controls.Label>("MarkerClickedLabel");
if (markerLabel != null)
markerLabel.Text = "Marker Clicked: Not clicked";
}
private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
{
// Use the existing view model instead of creating a new one
await Navigation.PushAsync(new MapOptionsPage(_viewModel));
}
}
Loading
Loading