简体   繁体   中英

Bing Maps with C# and MVVM Infobox and pushpin Bindings

I'm trying to implement a map screen for a Windows 8.1 program of mine and I'm using Bing Maps API with C# and MVVM.

The problems I'm facing:

First, I am able to show the pushpin and the infobox on the map using binding in the xaml but when I move the map, the infobox remains in the same position (centered in that case), not following the pushpin or the map movement.

Here the xaml:

<bm:Map ZoomLevel="15" Height="500" Width="600" ShowTraffic="False" ShowScaleBar="False" ShowNavigationBar="False" ShowBreadcrumb="False" ShowBuildings="False"
            Credentials="xxxxxxx" Name="myMap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <bm:Map.Center>
            <bm:Location Latitude="-25.450520" Longitude="-49.251557" />
        </bm:Map.Center>

        <bm:Map.Children>

            <bm:MapLayer Name="DataLayer">
                <bm:Pushpin Style="{Binding PushpinStyle}">
                    <bm:MapLayer.Position>
                        <bm:Location Latitude="{Binding Establishment.Pin.Latitude, Mode=OneWay}" Longitude="{Binding Establishment.Pin.Longitude, Mode=OneWay}" />
                    </bm:MapLayer.Position>
                </bm:Pushpin>
            </bm:MapLayer>

            <bm:MapLayer Margin="5,0,0,0" Position="{Binding InfoboxPosition}" Height="Auto" Width="Auto" >
                <Grid x:Name="Infobox" Visibility="{Binding InfoboxVisibility}" Margin="35,0,0,0" Height="Auto" >
                    <Border MaxWidth="350" Style="{StaticResource InfoboxBorderStyle}" />
                    <StackPanel Margin="10" MinWidth="200" MaxWidth="350">
                        <Grid>
                            <TextBlock Text="{Binding Establishment.Name}" Style="{StaticResource InfoboxTitleStyle}" />
                        </Grid>
                        <TextBlock Text="{Binding EstablishmentAddress}" Style="{StaticResource InfoboxContentStyle}"  MaxWidth="290" Margin="7" />
                    </StackPanel>
                </Grid>
            </bm:MapLayer>

        </bm:Map.Children>

    </bm:Map>

Second, if I have a map with multiple pushpins binded on a list, how can I add the "Pushpin.Tapped" event for each pin calling a method to move and set visible the infobox from ViewModel? Currently I make it work using simple codebehind (not VM) with something like that:

public MapControl()
    {
        this.InitializeComponent();
        AddPushpin(new Location(-25.450520, -49.251557), "Title", "Description", DataLayer, EstablishmentKindEnum.Cinema);
    }

    private async void pushpinTapped(object sender, TappedRoutedEventArgs e)
    {
        Pushpin p = sender as Pushpin;
        PushpinMetadata m = (PushpinMetadata)p.Tag;

        //Ensure there is content to be displayed before modifying the infobox control
        if (!String.IsNullOrEmpty(m.Title) || !String.IsNullOrEmpty(m.Description))
        {
            Infobox.DataContext = m;
            Infobox.Visibility = Visibility.Visible;
            MapLayer.SetPosition(Infobox, MapLayer.GetPosition(p));
        }
        else
            Infobox.Visibility = Visibility.Collapsed;
    }

    private void CloseInfobox_Tapped(object sender, TappedRoutedEventArgs e)
    {
        Infobox.Visibility = Visibility.Collapsed;
    }

    public void AddPushpin(Location latlong, string title, string description, MapLayer layer, EstablishmentKindEnum kind)
    {
        var pushpin = MapHelper.CreatePushpin(latlong, title, description, kind);
        MapLayer.SetPosition(pushpin, latlong);
        pushpin.Tapped += pushpinTapped;
        layer.Children.Add(pushpin);
    }

I need to do it all from ViewModel. I'm new on MVVM, anybody can give me some hints?

I've been reading about MVVM and some people says that the View should be responsible for all presentation interactions and the ViewModel should be responsible for the data.

I make it work with the structure below:

View:

<Grid>
    <bm:Map ZoomLevel="15" Height="500" Width="600" ShowTraffic="False" ShowScaleBar="False" ShowNavigationBar="False" ShowBreadcrumb="False" ShowBuildings="False"
            Credentials="xxxxxx" Name="myMap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" DataContextChanged="Map_DataContextChanged">
        <bm:Map.Center>
            <bm:Location Latitude="-25.450520" Longitude="-49.251557" />
        </bm:Map.Center>

        <bm:Map.Children>

            <bm:MapLayer Name="DataLayer">
                <bm:Pushpin Style="{Binding PushpinStyle}">
                    <bm:MapLayer.Position>
                        <bm:Location Latitude="{Binding Establishment.Pin.Latitude, Mode=OneWay}" Longitude="{Binding Establishment.Pin.Longitude, Mode=OneWay}" />
                    </bm:MapLayer.Position>
                </bm:Pushpin>
            </bm:MapLayer>

            <bm:MapLayer Margin="5,0,0,0"  Height="Auto" Width="Auto" >
                <Grid x:Name="Infobox" Visibility="Collapsed" Margin="35,0,0,0" Height="Auto" >
                        <Border MaxWidth="350" Style="{StaticResource InfoboxBorderStyle}" />
                    <StackPanel Margin="10" MinWidth="200" MaxWidth="350">
                        <Grid>
                            <TextBlock Text="{Binding Establishment.Name}" Style="{StaticResource InfoboxTitleStyle}" />
                            <Button Tapped="CloseInfobox_Tapped" Style="{StaticResource InfoboxCloseButtonStyle}" />
                        </Grid>
                        <TextBlock Text="{Binding EstablishmentAddress}" Style="{StaticResource InfoboxContentStyle}"  MaxWidth="290" Margin="7" />
                    </StackPanel>
                </Grid>
            </bm:MapLayer>

        </bm:Map.Children>

    </bm:Map>
</Grid>

View CodeBehind:

public sealed partial class MapControl : UserControl
{
    public MapControl()
    {
        this.InitializeComponent();
    }

    private async void pushpinTapped(object sender, TappedRoutedEventArgs e)
    {
        Pushpin p = sender as Pushpin;
        Infobox.Visibility = Visibility.Visible;
        MapLayer.SetPosition(Infobox, MapLayer.GetPosition(p));
    }

    private void CloseInfobox_Tapped(object sender, TappedRoutedEventArgs e)
    {
        Infobox.Visibility = Visibility.Collapsed;
    }

    private void Map_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
    {
        if (DataLayer.Children != null)
            foreach (var pin in DataLayer.Children)
            {
                pin.Tapped += pushpinTapped;
            }
    }
}

My codebehind add the event "Pushpin.Tapped" to all pins in the "DataLayer" after the DataSource change, and also controls the position and visibility of the Infobox. I think that is not a pattern-breaking approach and if you guys have any suggestions or comments, I'll be glad to hear it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM