Here is my code
public GeoCoordinateCollection AddCirclePath(GeoCoordinate geoCoordinate, Color stroke, double strokeThickness)
{
Point geoPoint = map.ConvertGeoCoordinateToViewportPoint(geoCoordinate);
MapPolygon polyCircle = new MapPolygon();
polyCircle.FillColor = Color.FromArgb(250, 220, 220, 0);
polyCircle.StrokeColor = stroke;
polyCircle.StrokeThickness = strokeThickness;
polyCircle.StrokeDashed = false;
polyCircle.Path = MapUtils.CreateCircle(geoCoordinate, geoCoordinate.HorizontalAccuracy);
map.MapElements.Add(polyCircle);
//click event
GestureListener gestureListener = GestureService.GetGestureListener(polyCircle);
gestureListener.Tap += new EventHandler<GestureEventArgs>(CircleTapped);
return polyCircle.Path;
}
private void CircleTapped(object sender, GestureEventArgs e)
{
Logger.Log("Circle tapped");
}
I add circle on map using above code but I'm unable to get tap event on this map element. I'm using Microsoft.Phone.Maps.Controls.Map class for map
There's several bugs in the control and hit testing. One solution is sometimes to add the last vextex in your element twice (I know it doesn't make sense but I've seen this resolve tap issues with polylines, so I'm guessing it's the same with polygon). It could also be that your ring orientation is wrong (clockwise vs counterclockwise)
(This is quite old post but in case someone else is looking for solution, here's a simple example.)
You should be able to use API directly to get the tapped map element w/o any conversions or workarounds.
Add a polygon.
var collection = new GeoCoordinateCollection();
collection.Add(new GeoCoordinate(0d, 0d));
collection.Add(new GeoCoordinate(10d, 0d));
collection.Add(new GeoCoordinate(10d, 10d));
collection.Add(new GeoCoordinate(0d, 10d));
collection.Add(new GeoCoordinate(0d, 0d));
var poly = new MapPolygon();
poly.FillColor = Color.FromArgb(80, 255, 0, 0);
poly.StrokeColor = Colors.Red;
poly.StrokeThickness = 15;
poly.Path = collection;
Map.MapElements.Add(poly);
And catch the tap.
private void Map_OnTap(object sender, GestureEventArgs e)
{
var point = e.GetPosition(Map);
//var coordinate = Map.ConvertViewportPointToGeoCoordinate(point);
int elements = Map.GetMapElementsAt(point).Count;
System.Diagnostics.Debug.WriteLine(string.Format("Hit {0} map element(s)", elements));
}
Bug regarding polylines does not apply to polygons, you do not need to add the last point twice. You should still have same starting- and ending points to keep the polygon closed (although this is not mandatory, either). Ring orientation does not seem to affect wether element is selectable or not.
Related
I have a WPF window containing a Canvas which is populated with rotated Rectangles in code. The rectangles each have a MouseDown event and their positions will be distributed according to coordinates provided by the user. Often two or more will overlap, partially obstructing the rectangle beneath it.
I need the MouseDown event to fire for each rectangle that is under the mouse when it is pressed, even if that rectangle is obstructed by another rectangle, but I am only getting the MouseDown event for the topmost rectangle.
I have tried setting e.Handled for the clicked rectangle, and routing the events through the Canvas with no luck, and even gone as far as trying to locate the objects beneath the mouse based on their coordinates, but the rotation of the rectangles make that difficult to calculate.
public MainWindow()
{
InitializeComponent();
Rectangle r1 = new Rectangle() {Width = 80, Height = 120, Fill = Brushes.Blue };
r1.MouseDown += r_MouseDown;
RotateTransform rt1 = new RotateTransform(60);
r1.RenderTransform = rt1;
Canvas.SetLeft(r1, 150);
Canvas.SetTop(r1, 50);
canvas1.Children.Add(r1);
Rectangle r2 = new Rectangle() { Width = 150, Height = 50, Fill = Brushes.Green };
r2.MouseDown += r_MouseDown;
RotateTransform rt2 = new RotateTransform(15);
r2.RenderTransform = rt2;
Canvas.SetLeft(r2, 100);
Canvas.SetTop(r2, 100);
canvas1.Children.Add(r2);
}
private void r_MouseDown(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("Rectangle Clicked");
}
}
There is another question that is similar to this, but it has no accepted answer and it is quite unclear as to what the final solution should be to resolve this issue. Let's see if we can be a little more clear.
First off, the solution outlined below will use the VisualTreeHelper.HitTest method in order to identify if the mouse has clicked your rectangles. The VisualTreeHelper allows us to find the rectangles even if they have moved around due to things like Canvas.SetTop and various .RenderTransform operations.
Secondly, we are going to be capturing the click event on your canvas element rather than on the individual rectangles. This allows us to handle things at the canvas level and check all the rectangles at once, as it were.
public MainWindow()
{
InitializeComponent();
//Additional rectangle for testing.
Rectangle r3 = new Rectangle() { Width = 175, Height = 80, Fill = Brushes.Goldenrod };
Canvas.SetLeft(r3, 80);
Canvas.SetTop(r3, 80);
canvas1.Children.Add(r3);
Rectangle r1 = new Rectangle() { Width = 80, Height = 120, Fill = Brushes.Blue };
RotateTransform rt1 = new RotateTransform(60);
r1.RenderTransform = rt1;
Canvas.SetLeft(r1, 100);
Canvas.SetTop(r1, 100);
canvas1.Children.Add(r1);
Rectangle r2 = new Rectangle() { Width = 150, Height = 50, Fill = Brushes.Green };
RotateTransform rt2 = new RotateTransform(15);
r2.LayoutTransform = rt2;
Canvas.SetLeft(r2, 100);
Canvas.SetTop(r2, 100);
canvas1.Children.Add(r2);
//Mouse 'click' event.
canvas1.PreviewMouseDown += canvasMouseDown;
}
//list to store the hit test results
private List<HitTestResult> hitResultsList = new List<HitTestResult>();
The HitTest method being used is the more complicated one, because the simplest version of that method only returns "the topmost" item. And by topmost, they mean the first item drawn, so it's actually visually the one on the bottom of the stack of rectangles. In order to get all of the rectangles, we need to use the complicated version of the HitTest method shown below.
private void canvasMouseDown(object sender, MouseButtonEventArgs e)
{
if (canvas1.Children.Count > 0)
{
// Retrieve the coordinates of the mouse position.
Point pt = e.GetPosition((UIElement)sender);
// Clear the contents of the list used for hit test results.
hitResultsList.Clear();
// Set up a callback to receive the hit test result enumeration.
VisualTreeHelper.HitTest(canvas1,
new HitTestFilterCallback(MyHitTestFilter),
new HitTestResultCallback(MyHitTestResult),
new PointHitTestParameters(pt));
// Perform actions on the hit test results list.
if (hitResultsList.Count > 0)
{
string msg = null;
foreach (HitTestResult htr in hitResultsList)
{
Rectangle r = (Rectangle)htr.VisualHit;
msg += r.Fill.ToString() + "\n";
}
//Message displaying the fill colors of all the rectangles
//under the mouse when it was clicked.
MessageBox.Show(msg);
}
}
}
// Filter the hit test values for each object in the enumeration.
private HitTestFilterBehavior MyHitTestFilter(DependencyObject o)
{
// Test for the object value you want to filter.
if (o.GetType() == typeof(Label))
{
// Visual object and descendants are NOT part of hit test results enumeration.
return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
}
else
{
// Visual object is part of hit test results enumeration.
return HitTestFilterBehavior.Continue;
}
}
// Add the hit test result to the list of results.
private HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
//Filter out the canvas object.
if (!result.VisualHit.ToString().Contains("Canvas"))
{
hitResultsList.Add(result);
}
// Set the behavior to return visuals at all z-order levels.
return HitTestResultBehavior.Continue;
}
The test example above just displays a message box showing the fill colors of all rectangles under the mouse pointer when it was clicked; verifying that VisualTreeHelper did in fact retrieve all the rectangles in the stack.
Im creating Path objects with curvy edges on a Canvas. I want to be able to rotate them and move them around the canvas. But when i try to apply several transform to and object it only visually displays the last one. I assume its because the transforms are applied to the coordinates of the Path object and are only displayed but not saved afterwards.
So if i would run something like:
my_canvas.Children[0].RenderTransform = new TranslateTransform(0, 100);
my_canvas.Children[0].RenderTransform = new TranslateTransform(0, 150);
it would move my Path 150 pixels down.
Is there a way i can save the transform progress of RenderTransform or do i have to recreate my Path with different parameters/write a method to displace the pixels manually?
Edit
Another example:
my_canvas.Children[0].MouseDown += (object sender, MouseButtonEventArgs e) =>
{
if (e.LeftButton == MouseButtonState.Pressed)
{
MouseDownLocation = e.GetPosition(my_canvas);
}
};
my_canvas.Children[0].MouseMove += (object sender, MouseEventArgs e) =>
{
if (e.LeftButton == MouseButtonState.Pressed)
{
my_canvas.Children[0].RenderTransform = new TranslateTransform(-(MouseDownLocation.X - e.GetPosition(my_canvas).X), -(MouseDownLocation.Y - e.GetPosition(my_canvas).Y));
}
};
This code allows me to move my element and it works fine: i can pick it up, visually move it, and let it go. But only once. If try to do it again it tries to do the transformation based on the elements' previous position. And as I am typing this i realize that i can probably solve this by keeping track of the offsets the transformations are causing.
Just add the next translation to the X and Y values of the existing RenderTransform:
my_canvas.Children[0].RenderTransform = new TranslateTransform(0, 100);
((TranslateTransform)my_canvas.Children[0].RenderTransform).Y += 150;
Or use Canvas.Left and Canvas.Top instead of a TranslateTransform:
UIElement element = my_canvas.Children[0];
Canvas.SetTop(element, 100);
Canvas.SetTop(element, Canvas.GetTop(element) + 150);
I have a method which finds relationships between two objects, if it exists, I would like to draw two Lineshapes to the link. I have started to implement the first lineshape, however whenever I test the code the lines persist. I have tried multiple methods (as you can see) and these do not refresh the canvas for the new lines to be drawn.
private void DrawRelationshipLines()
{
_canvas = new ShapeContainer {Parent = panelCredentialsVisualisation};
//These methods below do not redraw the canvas
_canvas.Shapes.Remove(_tableinfoLine);
_canvas.Shapes.Clear();
_canvas.Refresh();
_canvas.Update();
//
List<string> relationships = lvSelectedTableInfoCredentialsIntersection.GetAllRelationships();
if (relationships.Capacity == 0)
return;
foreach (string context in relationships)
{
Label contextLabelName = GetLabelByName(context);
_tableinfoLine = new LineShape
{
Parent = _canvas,
BorderWidth = 2,
BorderColor = Color.BlueViolet,
StartPoint = new Point(lblselectedTableinfo.Right, lblselectedTableinfo.Top + 10),
EndPoint = new Point(contextLabelName.Left, contextLabelName.Top + 10)
};
}
The code works fine in searching for relationships and drawing them, however I'd like to be able to clear the canvas before drawing a different relationship, is this possible?
Thanks if anyone can help.
Moving _canvas = new ShapeContainer {Parent = panelCredentialsVisualisation}; outside of the method made this work. Seems like initializing a new ShapeContainer each time was causing issues.
I'm using the ILNumerics Math Library to create some 2D Plots. To display the data I use the ILPlotCube Class. I would like to turn off the default behavior of the EventHandler for MouseDoubleClick events, because I would like to implement my own. Is that possible?
Here's some more context:
The default event handler of an ILPlotCube for MouseDoubleClick events resets the view to default values. Usually this works quite well, but there seems to be a problem with very small x- and y-values. When I add a linePlot with very small y-values the limits of the plots are automatically set to YMax=0.525 and YMin=-0.525. Unfortunately, that's not what I want. So, I set the values myself after adding the linePlot and the plot looks exactly how I'd like to have it. Great... but: if I double click on the scene it uses the default (0.525) values again. Dough! That's why I would like to turn off or overwrite this behavior.
Any ideas?
private void ilPanel1_Load(object sender, EventArgs e)
{
var scene = new ILScene();
//data with very small "y-values"
ILArray<float> line1 = new float[,] {
{0.0f, 1.0f, 2.0f },
{2.042166e-08f, 2.070141e-08f , 2.042166e-08f} };
var linePlot1 = new ILLinePlot(line1.T,
lineColor: Color.Blue,
lineWidth: 3,
markerStyle: MarkerStyle.Dot);
//Create Plot Cube
var plotCube = new ILPlotCube();
plotCube.Add(linePlot1);
//plotCube.Plots.Limits.YMax is now 0.525
//plotCube.Plots.Limits.YMin is now -0.525
//manually set the value
float maxY = 0.0f;
using (ILScope.Enter())
{
var aPos = linePlot1.Line.Positions.Storage["1;:"];
maxY = ILMath.max(aPos).FirstOrDefault();
}
plotCube.Plots.Limits.YMax = maxY;
plotCube.Plots.Limits.YMin = 0.0f;
var plot = scene.Add(plotCube);
ilPanel1.Scene = scene;
}
Thanks,
Tim
All nodes in ILNumerics provide access to the common mouse handlers which you can use to provide your own logic - or to simply disable existing individual handlers. In your case, you can override the double click handler for the plot cube:
// ....
plotCube.MouseDoubleClick += (_e, _a) => {
_a.Cancel = true;
};
// you may want disable zoom and pan as well?
plot.AllowZoom = false;
plot.AllowPan = false;
// continue with your code here...
ilPanel1.Scene = scene;
The mouse handlers in ILNumerics are actually very flexible and powerful. See the documentation here: http://ilnumerics.net/mouse-events.html
#Edit: for your situation, the ILPlotCube.AutoScaleOnAdd property might also be of interest. It determines, if the plot cube limits are to be recomputed once a new plot has been added to the plot cube. You might find false more convenient.
I've got a Canvas with a few UIElements on. After I've moved them on the canvas by animating the top and left properties, very occasionally a subsiquent call to Canvas.GetTop results in NaN.
Am I not 'closing' the animations properly?
Here's how I'm doing the move
private void InternalMove(double durationMS, FrameworkElement fElement, Point point, EventHandler callback)
{
_moveElement = fElement;
_destination = point;
Duration duration = new Duration(TimeSpan.FromMilliseconds(durationMS));
DoubleAnimation moveLeftAnimation = new DoubleAnimation(Canvas.GetLeft(fElement), point.X, duration, FillBehavior.Stop);
Storyboard.SetTargetProperty(moveLeftAnimation, new PropertyPath("(Canvas.Left)"));
DoubleAnimation moveTopAnimation = new DoubleAnimation(Canvas.GetTop(fElement), point.Y, duration, FillBehavior.Stop);
Storyboard.SetTargetProperty(moveTopAnimation, new PropertyPath("(Canvas.Top)"));
// Create a storyboard to contain the animation.
_moveStoryboard = new Storyboard();
if (callback != null) _moveStoryboard.Completed += callback;
_moveStoryboard.Completed += new EventHandler(s1_Completed);
_moveStoryboard.Children.Add(moveLeftAnimation);
_moveStoryboard.Children.Add(moveTopAnimation);
_moveStoryboard.FillBehavior = FillBehavior.Stop;
_moveStoryboard.Begin(fElement);
}
private void s1_Completed(object sender, EventArgs e)
{
if (_moveStoryboard != null)
{
_moveStoryboard.BeginAnimation(Canvas.LeftProperty, null, HandoffBehavior.Compose);
_moveStoryboard.BeginAnimation(Canvas.TopProperty, null, HandoffBehavior.Compose);
}
Canvas.SetLeft(_moveElement, _destination.X);
Canvas.SetTop(_moveElement, _destination.Y);
}
thanks
It seems than Canvas.GetTop(x) can return 'Nan' if the value is not explictly set (even tho I do explicitly set it I still sometimes get that result).
An alternative method I'm now using is
Vector offset = VisualTreeHelper.GetOffset(fElement);
which returns the position of fElement within it's container.
I've run into a similar situation (NaN), but in a different context. As I recall, it had something to do with how the element was positioned in the container.
Sorry I couldn't provide more help, but maybe this will provide some guidance.
Check back your xaml file in Properties Layout make sure value of left or top show only number but not mix (auto).
Example: Top 123(auto), this caused Canvas.GetTop will return NaN.