Reputation: 29963
I am trying to implement the ability for a user to draw and refine a polygon on a Bing Map control.
I have added a shape layer, and overridden the tap events to draw a simple polygon and that all works perfectly. I would like to further refine the control so that a user can drag and drop any of Location objects of the drawn polygon to alter its shape, but I'm not having any luck.
I have tried to add a MapItemControl, bound to the Locations property of my polygon, but without success - I don't see the pins appear on the map, even though my polygon does render correctly. Presumably the Locations collection isn't notifying the UI of any changes to the collection, so I may have to maintain a separate collection for these pins.
Once I get this going, however, I still need to be able to drag and drop the pins around to modify the polygon. Has anyone implemented this on Windows 8 that could share some thoughts, please?
EDIT
I now have my pins showing the Locations collection on the map. I'd ideally like to tie these back to the Locations collection of the polygon, but beggars can't be choosers. I now just need to be able to drag the pins around.
Upvotes: 2
Views: 1622
Reputation: 5001
I added a drag handle module to the polygon in order to edit each point in the polygon. This is the walk-through I used.
This is for version 7.0. Not sure which version you are using, but it is easily adaptable.
Edit I did this in javascript/html. Just noticed you tagged this as windows 8. Curious, are you making a silver-light control for windows 8 phones?
WPF C# Solution
Here is what I came up with and it is very easy to implement, all you need to do is hook my event onto your MapPolygon
object. The idea behind the code, is once you click on the polygon, we place a pushpin (which we create events so that we can drag it) at each vertice on the polygon. As we drag and drop each pushpin we adjust the polygon.Locations as applicable.
polygon.MouseDown += new MouseButtonEventHandler(polygon_MouseDownResize);
And a way to exit the resize event, I choose to just hook onto the key down and used 'esc' to stop editing, but you can do this on right click or any other event you choose to. All your doing is clearing pushPinVertices
list and resetting event variables.
// === Editable polygon Events ===
private bool _shapeEdit;
public MapPolygon selectedPoly { get; set; }
public List<Pushpin> pushPinVertices { get; set; }
void polygon_MouseDownResize(object sender, MouseButtonEventArgs e)
{
if (!_shapeEdit && selectedPoly == null)
{
_shapeEdit = true;
selectedPoly = sender as MapPolygon;
pushPinVertices = new List<Pushpin>();
int i = 0;
foreach (Microsoft.Maps.MapControl.WPF.Location vertice in selectedPoly.Locations)
{
Pushpin verticeBlock = new Pushpin();
// I use a template to place a 'vertice marker' instead of a pushpin, il provide resource below
verticeBlock.Template = (ControlTemplate)Application.Current.Resources["PushPinTemplate"];
verticeBlock.Content = "vertice";
verticeBlock.Location = vertice;
verticeBlock.MouseDown += new MouseButtonEventHandler(pin_MouseDown);
verticeBlock.MouseUp += new MouseButtonEventHandler(pin_MouseUp);
myMap.Children.Add(verticeBlock);
pushPinVertices.Add(verticeBlock);
i++;
}
}
}
private void myMap_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Escape)
{
if (_shapeEdit && selectedPoly != null)
{
foreach (Pushpin p in pushPinVertices)
{
myMap.Children.Remove(p);
}
_shapeEdit = false;
selectedPoly = null;
}
}
}
// Note: I needed my window to pass bing maps the keydown event
private void Window_KeyDown(object sender, KeyEventArgs e)
{
myMap_KeyDown(sender, e);
}
// === Editable polygon Events ===
// ==== Draggable pushpin events =====
Vector _mouseToMarker;
private bool _IsPinDragging;
public Pushpin SelectedPushpin { get; set; }
void pin_MouseUp(object sender, MouseButtonEventArgs e)
{
LocationCollection locCol = new LocationCollection();
foreach (Pushpin p in pushPinVertices)
{
locCol.Add(p.Location);
}
selectedPoly.Locations = locCol;
bingMapRefresh();
_IsPinDragging = false;
SelectedPushpin = null;
}
void pin_MouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
SelectedPushpin = (Pushpin)sender;
_IsPinDragging = true;
_mouseToMarker = Point.Subtract(
myMap.LocationToViewportPoint(SelectedPushpin.Location),
e.GetPosition(myMap));
}
private void myMap_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (_IsPinDragging && SelectedPushpin != null)
{
SelectedPushpin.Location = myMap.ViewportPointToLocation(Point.Add(e.GetPosition(myMap), _mouseToMarker));
e.Handled = true;
}
}
}
// ==== Draggable pushpin events =====
// Nice little maprefresh I found online since the bingmap WPF doesnt always seem to update elements after certain event orders
private void bingMapRefresh()
{
//myMap.UpdateLayout();
var mapCenter = myMap.Center;
mapCenter.Latitude += 0.00001;
myMap.SetView(mapCenter, myMap.ZoomLevel);
}
As for the resource to overwrite the pushpin icon, I just used an ImageBrush
an made a small white square. Note you may need to adjust the Margin
property depending on how long / wide you make your marker. This is because generally the pushpin is anchored above the location by default.
<Application.Resources>
<ControlTemplate x:Key="PushPinTemplate">
<Grid>
<Rectangle Width="10" Height="10" Margin="0 35 0 0">
<Rectangle.Fill>
<ImageBrush ImageSource="pack://application:,,,/Images/DragIcon.gif"/>
</Rectangle.Fill>
</Rectangle>
</Grid>
</ControlTemplate>
</Application.Resources>
Upvotes: 2