必应地图和 MVVM

And*_*ome 1 wpf mvvm bing-maps

我正在尝试做一个学校项目,我们将创建一个 Silverlight 应用程序,该应用程序使用 bing 地图作为一种地图编辑器,用于放置汽车收费站。

例子: 必应地图

有一些要求,它必须支持拖放,我们必须使用MVVM(Model View View-Model)。现在我们通过拥有一个带有一组 Image 子元素的 MapLayer 完成了拖放功能,然后连接了启用拖放的事件(请参阅下面的代码)。但是现在我们面临一个问题,我们如何将 ViewModel 连接到这个,我就是看不到它:(

我不是要求一个完整的解决方案,但一些帮助会很好。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.IO;
using Microsoft.Maps.MapControl;

namespace BingMapDragDrop
{
    public partial class MainPage : UserControl
    {
        private MapAddType AddType = MapAddType.None;
        private Location _myhome = new Location(55.6686512716393, 12.5481431962938, 0);

        private MapLayer EndNodeLayer;
        private double HideEndNodeLayer = 10.0;
        private MapLayer EndNodeIntermediatedLayer;
        private double HideEndNodeIntermediatedLayer = 10.0;
        private MapLayer RootNodeLayer;
        private double HideRootNodeLayer = 0.0;
        private MapLayer RootNodeIntermediatedLayer;
        private double HideRootNodeIntermediatedLayer = 5.0;

        public MainPage()
        {
            EndNodeLayer = new MapLayer();
            EndNodeIntermediatedLayer = new MapLayer();
            RootNodeLayer = new MapLayer();
            RootNodeIntermediatedLayer = new MapLayer();


            InitializeComponent();
            BingMap.SetView(_myhome, 15);
            BingMap.ViewChangeOnFrame += new EventHandler<MapEventArgs>(BingMap_ViewChangeOnFrame);

            // Adding the layers
            BingMap.Children.Add(EndNodeIntermediatedLayer);
            BingMap.Children.Add(EndNodeLayer);
            BingMap.Children.Add(RootNodeIntermediatedLayer);
            BingMap.Children.Add(RootNodeLayer);
        }

        private void AddEndNode(Location location, MapAddType type)
        {
            string url;

            if (type == MapAddType.Home)
                url = "Images/Home.png";
            else if (type == MapAddType.HomeWithChargingSpot)
                url = "Images/HomeWithChargingSpot.png";
            else if (type == MapAddType.Workplace)
                url = "Images/Workplace.png";
            else if (type == MapAddType.WorkplaceWithChargingSpot)
                url = "Images/WorkplaceWithChargingSpot.png";
            else if (type == MapAddType.PublicChargningSpot)
                url = "Images/PublicChargningSpot.png";
            else if (type == MapAddType.FastChargingStation)
                url = "Images/FastChargingStation.png";
            else
                return;

            var image = new Image
            {
                Source = new BitmapImage(new Uri(url, UriKind.RelativeOrAbsolute)),
                Width = 50,
                Height = 50
            };
            AddImageToLayerAsDragAbleObject(image, location, EndNodeLayer);
        }
        private void AddPowerPlant(Location location)
        {
            var image = new Image
            {
                Source = new BitmapImage(new Uri("Images/Powerplant-New.png", UriKind.RelativeOrAbsolute)),
                Width = 50,
                Height = 50
            };
            AddImageToLayerAsDragAbleObject(image, location, EndNodeLayer);
        }

        #region Bing Map Events, not related to D&D
        // Some events dose not exists so we need to make some our self.
        private double bingZoom = 0.0;
        void BingMap_ViewChangeOnFrame(object sender, MapEventArgs e)
        {
            if (BingMap.ZoomLevel != bingZoom)
            {
                bingZoom = BingMap.ZoomLevel;
                BingMapZoomLevelChanged(sender, e);
            }
        }

        private void BingMap_Loaded(object sender, RoutedEventArgs e)
        {

        }
        private void BingMap_MouseClick(object sender, MapMouseEventArgs e)
        {
            if(AddType == MapAddType.None)
                return;

            Location loc;
            if (!BingMap.TryViewportPointToLocation(e.ViewportPoint, out loc))
                return;

            switch (AddType)
            {
                case MapAddType.Powerplant:
                    AddPowerPlant(loc);
                    break;
                case MapAddType.FastChargingStation:
                case MapAddType.PublicChargningSpot:
                case MapAddType.WorkplaceWithChargingSpot:
                case MapAddType.Workplace:
                case MapAddType.HomeWithChargingSpot:
                case MapAddType.Home:
                    AddEndNode(loc, AddType);
                    break;
            }

            AddType = MapAddType.None;
        }
        private void BingMapZoomLevelChanged(object sender, MapEventArgs e)
        {
            if (BingMap.ZoomLevel <= HideEndNodeLayer && EndNodeLayer.Visibility == Visibility.Visible)
                EndNodeLayer.Visibility = Visibility.Collapsed;
            else if (BingMap.ZoomLevel > HideEndNodeLayer && EndNodeLayer.Visibility == Visibility.Collapsed)
                EndNodeLayer.Visibility = Visibility.Visible;

            if (BingMap.ZoomLevel >= HideEndNodeIntermediatedLayer && EndNodeLayer.Visibility == Visibility.Visible)
                EndNodeIntermediatedLayer.Visibility = Visibility.Collapsed;
            else if (BingMap.ZoomLevel > HideEndNodeIntermediatedLayer && EndNodeLayer.Visibility == Visibility.Collapsed)
                EndNodeIntermediatedLayer.Visibility = Visibility.Visible;

            if (BingMap.ZoomLevel <= HideRootNodeLayer && EndNodeLayer.Visibility == Visibility.Visible)
                RootNodeLayer.Visibility = Visibility.Collapsed;
            else if (BingMap.ZoomLevel > HideRootNodeLayer && EndNodeLayer.Visibility == Visibility.Collapsed)
                RootNodeLayer.Visibility = Visibility.Visible;

            if (BingMap.ZoomLevel <= HideRootNodeIntermediatedLayer && EndNodeLayer.Visibility == Visibility.Visible)
                RootNodeIntermediatedLayer.Visibility = Visibility.Collapsed;
            else if (BingMap.ZoomLevel > HideRootNodeIntermediatedLayer && EndNodeLayer.Visibility == Visibility.Collapsed)
                RootNodeIntermediatedLayer.Visibility = Visibility.Visible;
        }
        #endregion

        #region This is where the dragging magic happens
        private void AddImageToLayerAsDragAbleObject(Image image, Location location, MapLayer mapLayer)
        {
            image.MouseLeftButtonDown += new MouseButtonEventHandler(ImageMouseLeftButtonDown);
            var position = PositionOrigin.Center;
            mapLayer.AddChild(image, location, position);
        }

        private bool _isDragging = false;
        private Image _dragingObject;
        private void ImageMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _isDragging = true;
            // We need to save the object, so we are able to set the location on release
            _dragingObject = (Image)sender;

            // Here we add the events, be sure to remove them at release!
            BingMap.MousePan += new EventHandler<MapMouseDragEventArgs>(BingMapMousePan);
            BingMap.MouseLeftButtonUp += new MouseButtonEventHandler(BingMapMouseLeftButtonUp);
            BingMap.MouseMove += new MouseEventHandler(BingMapMouseMove);
        }
        // Event that is called when an image is move
        private void BingMapMouseMove(object sender, MouseEventArgs e)
        {
            var map = (Map)sender;
            if (!_isDragging) return;
            // The the location of the mouse
            var mouseMapPosition = e.GetPosition(map);
            var mouseGeocode = map.ViewportPointToLocation(mouseMapPosition);

            // Set location
            MapLayer.SetPosition(_dragingObject, mouseGeocode);
        }
        // Event that is called when an image is released
        private void BingMapMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            // Remove the events
            BingMap.MousePan -= BingMapMousePan;
            BingMap.MouseLeftButtonUp -= BingMapMouseLeftButtonUp;
            BingMap.MouseMove -= BingMapMouseMove;
            // Disable dragging
            _isDragging = false;
        }
        // Event that is called when the map is panning
        private void BingMapMousePan(object sender, MapMouseDragEventArgs e)
        {
            // We don't want the map to pan if we are dragging
            if (_isDragging)
                e.Handled = true;
        }
        #endregion

        #region Menu
        private void MenuMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if ((String)((Image)sender).Tag == "Powerplant")
                AddType = AddType == MapAddType.Powerplant ? MapAddType.None : MapAddType.Powerplant;

            if ((String)((Image)sender).Tag == "Home")
                AddType = AddType == MapAddType.Home ? MapAddType.None : MapAddType.Home;

            if ((String)((Image)sender).Tag == "HomeWithChargingSpot")
                AddType = AddType == MapAddType.HomeWithChargingSpot ? MapAddType.None : MapAddType.HomeWithChargingSpot;

            if ((String)((Image)sender).Tag == "Workplace")
                AddType = AddType == MapAddType.Workplace ? MapAddType.None : MapAddType.Workplace;

            if ((String)((Image)sender).Tag == "WorkplaceWithChargingSpot")
                AddType = AddType == MapAddType.WorkplaceWithChargingSpot ? MapAddType.None : MapAddType.WorkplaceWithChargingSpot;

            if ((String)((Image)sender).Tag == "PublicChargningSpot")
                AddType = AddType == MapAddType.PublicChargningSpot ? MapAddType.None : MapAddType.PublicChargningSpot;

            if ((String)((Image)sender).Tag == "FastChargingStation")
                AddType = AddType == MapAddType.FastChargingStation ? MapAddType.None : MapAddType.FastChargingStation;
        }
        #endregion

        #region Cursor image

        private bool IsCursorImageSet = false;

        private void UserControl_MouseMove(object sender, MouseEventArgs e)
        {
            PlaceCursorImage(e.GetPosition(this));
        }
        private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            PlaceCursorImage(e.GetPosition(this));
        }
        private void PlaceCursorImage(Point location)
        {
            if (AddType == MapAddType.None && !IsCursorImageSet)
                return;
            if (AddType == MapAddType.None && IsCursorImageSet)
            {
                IsCursorImageSet = false;
                CursorImage.Visibility = Visibility.Collapsed;
                return;
            }

            Canvas.SetTop(CursorImage, location.Y + 5.0);
            Canvas.SetLeft(CursorImage, location.X + 5.0);

            if (!IsCursorImageSet)
            {
                IsCursorImageSet = true;

                switch (AddType)
                {
                    case MapAddType.Powerplant:
                        CursorImage.Source =
                            new BitmapImage(new Uri("Images/Powerplant-New.png", UriKind.RelativeOrAbsolute));
                        break;
                    case MapAddType.Home:
                        CursorImage.Source =
                            new BitmapImage(new Uri("Images/Home.png", UriKind.RelativeOrAbsolute));
                        break;
                    case MapAddType.HomeWithChargingSpot:
                        CursorImage.Source =
                            new BitmapImage(new Uri("Images/HomeWithChargingSpot.png", UriKind.RelativeOrAbsolute));
                        break;
                    case MapAddType.Workplace:
                        CursorImage.Source =
                            new BitmapImage(new Uri("Images/Workplace.png", UriKind.RelativeOrAbsolute));
                        break;
                    case MapAddType.WorkplaceWithChargingSpot:
                        CursorImage.Source =
                            new BitmapImage(new Uri("Images/WorkplaceWithChargingSpot.png", UriKind.RelativeOrAbsolute));
                        break;
                    case MapAddType.PublicChargningSpot:
                        CursorImage.Source =
                            new BitmapImage(new Uri("Images/PublicChargningSpot.png", UriKind.RelativeOrAbsolute));
                        break;
                    case MapAddType.FastChargingStation:
                        CursorImage.Source =
                            new BitmapImage(new Uri("Images/FastChargingStation.png", UriKind.RelativeOrAbsolute));
                        break;
                    default:
                        return;
                }

                CursorImage.Visibility = Visibility.Visible;
                CursorImage.Width = 40;
                CursorImage.Height = 40;
                CursorImage.Stretch = Stretch.Uniform;
            }
        }
        #endregion
    }
}
Run Code Online (Sandbox Code Playgroud)

我的 WPF 代码

<UserControl x:Class="BingMapDragDrop.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    mc:Ignorable="d" MouseMove="UserControl_MouseMove" Width="800" Height="600" MouseLeftButtonUp="UserControl_MouseLeftButtonUp">
    <Canvas IsHitTestVisible="True">
        <StackPanel HorizontalAlignment="Left" Name="stackPanelMenu" Width="75" Margin="0,12,0,12" Canvas.Top="0" Height="588">
            <Image Name="imagePowerplant" Stretch="Uniform" Width="35" Source="/BingMapDragDrop;component/Images/Powerplant-New.png" Tag="Powerplant" MouseLeftButtonUp="MenuMouseLeftButtonUp" />
            <Image Name="imageHome" Stretch="Uniform" Width="35" Source="/BingMapDragDrop;component/Images/Home.png" Tag="Home" MouseLeftButtonUp="MenuMouseLeftButtonUp" />
            <Image Name="imageHomeWithChargingSpot" Stretch="Uniform" Width="35" Source="/BingMapDragDrop;component/Images/HomeWithChargingSpot.png" Tag="HomeWithChargingSpot" MouseLeftButtonUp="MenuMouseLeftButtonUp" />
            <Image Name="imageWorkplace" Stretch="Uniform" Width="35" Source="/BingMapDragDrop;component/Images/Workplace.png" Tag="Workplace" MouseLeftButtonUp="MenuMouseLeftButtonUp" />
            <Image Name="imageWorkplaceWithChargingSpot" Stretch="Uniform" Width="35" Source="/BingMapDragDrop;component/Images/WorkplaceWithChargingSpot.png" Tag="WorkplaceWithChargingSpot" MouseLeftButtonUp="MenuMouseLeftButtonUp" />
            <Image Name="imagePublicChargningSpot" Stretch="Uniform" Width="35" Source="/BingMapDragDrop;component/Images/PublicChargningSpot.png" Tag="PublicChargningSpot" MouseLeftButtonUp="MenuMouseLeftButtonUp" Height="49" />
            <Image Name="imageFastChargingStation" Stretch="Uniform" Width="35" Source="/BingMapDragDrop;component/Images/FastChargingStation.png" Tag="FastChargingStation" MouseLeftButtonUp="MenuMouseLeftButtonUp" />
        </StackPanel>
        <m:Map x:Name="BingMap" CredentialsProvider="[Insert credentials here]" Mode="Aerial" Loaded="BingMap_Loaded" MouseClick="BingMap_MouseClick" Canvas.Left="72" Canvas.Top="0" Margin="0" Height="600" Width="728" />

        <Image Name="CursorImage" Visibility="Collapsed" IsHitTestVisible="False" Opacity="0.5" />
    </Canvas>
</UserControl>
Run Code Online (Sandbox Code Playgroud)

Ray*_*rns 5

我不想告诉你这一点,但你已经编写的代码看起来不像 WPF 或 MVVM 代码。您正在以一种非常类似于 WinForms 的方式执行此操作。

将您的代码“转换”到 MVVM 不会像那样“重做”。

您要问的第一件事是,“我的模型中应该有什么?” 显然,该模型由一系列对象组成,例如房屋和发电厂。这些对象中的每一个都至少有一个类型和一个位置。

我建议您定义某种 MappableObject 类并在模型中使用 ObservableCollection 来存储可映射对象。

您的主控件显然应该是一个使用 Canvas 作为其 ItemsPanel 的 ItemsControl。项目模板必须设置 Canvas.Left 和 Canvas.Top 以匹配项目的位置。您需要一个转换器才能从位置转换为点。

现在,如果您的 ItemsControl 将其 ItemsSource 绑定到您的 ObservableCollection,则每次将可映射对象添加到集合中时,它都会出现在其位置中。每当您更改其位置时,它都会移动到屏幕上的新位置。

您将需要一些东西来处理拖动事件并显示光标。我会使用与您已有的代码大致相同的代码,只是将它放在您的 ItemTemplate 中使用的 UserControl 中。它可以找到它的容器并使用它来将拖动坐标映射到地图位置。随着拖动的进行,它可以更新 MappableObject 的位置属性。请记住使用 TransformToVisual 将所有坐标转换到地图上,而不是使用控件的本地坐标。

要为对象获取正确的图像,请使用转换器:您的模型知道它是什么类型的对象,并且您可以命名 .png 文件以匹配,以便您的转换器可以通过构建直接来自对象类型的 Url。

我希望这些建议可以帮助您朝着正确的方向前进。使用 MVVM 在 WPF 中执行此操作比使用旧的 WinForms 方式非常简单且清晰,但是您将需要使用许多不习惯的新技术,因此会有一个学习曲线。

祝您成功。