i am creating a custom renderer for Xamarin.Forms Frame control to enable extended functionality like gradient background.
for now i got it to work somehow on iOS but i am working on the android version too.
First i tried to override the SetupLayer method as it is in the default frame renderer see here, and add a sublayer there
The way i got it to work was to override Draw method and add a sublayer to the frame view and set the frame layer background to UIColor.Clear.
The issue that i got is that controls i put inside of frame and also the gradient is somehow faded like there is some layer blending stuff going on. something like .5 opacity.
Any advice how to get the Layer behind (Gradient) fully opaque?
Am i doing it wrong ?
Thanks in advance.
Update : I had removed unnecessary code from sample for not creating confusion, the issue i am facing is understand how layers blending work in iOS, as the top-layers blend with added gradient layer and look more faded than normal frame.
//not working
private void SetupLayer()
{
***
var gl = new CAGradientLayer
{
StartPoint = new CGPoint(0, 0),
EndPoint = new CGPoint(1, 1),
Frame = rect,
Colors = new CGColor[]
{
_gradinetControl.StartColor.ToCGColor(),
_gradinetControl.EndColor.ToCGColor()
},
CornerRadius = _gradinetControl.CornerRadius
};
Layer.BackgroundColor = UIColor.Clear.CGColor;
Layer.InsertSublayer(gl,0);
***
}
*
//working but strange fade blending
public override void Draw(CGRect rect)
{
var gl = new CAGradientLayer
{
StartPoint = new CGPoint(0, 0),
EndPoint = new CGPoint(1, 1),
Frame = rect,
Colors = new CGColor[]
{
_gradinetControl.StartColor.ToCGColor(),
_gradinetControl.EndColor.ToCGColor()
},
CornerRadius = _gradinetControl.CornerRadius
};
NativeView.Layer.BackgroundColor = UIColor.Clear.CGColor;
NativeView.Layer.InsertSublayer(gl,0);
base.Draw(rect);
}
I think exists a plugin for this. You can take a look there:
XFGloss
in XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xfg="clr-namespace:XFGloss;assembly=XFGloss"
x:Class="XFGlossSample.Views.AboutPage"
Title="XFGloss Sample App" Padding="10">
<xfg:ContentPageGloss.BackgroundGradient>
<xfg:Gradient Rotation="150">
<xfg:GradientStep StepColor="White" StepPercentage="0" />
<xfg:GradientStep StepColor="White" StepPercentage=".5" />
<xfg:GradientStep StepColor="#ccd9ff" StepPercentage="1" />
</xfg:Gradient>
</xfg:ContentPageGloss.BackgroundGradient>
...
</ContentPage>
In code
namespace XFGlossSample.Views
{
public class AboutPage : ContentPage
{
public AboutPage()
{
Title = "XFGloss Sample App";
Padding = 10;
// Manually construct a multi-color gradient at an angle of our choosing
var bkgrndGradient = new Gradient()
{
Rotation = 150,
Steps = new GradientStepCollection()
{
new GradientStep(Color.White, 0),
new GradientStep(Color.White, .5),
new GradientStep(Color.FromHex("#ccd9ff"), 1)
}
};
ContentPageGloss.SetBackgroundGradient(this, bkgrndGradient);
Content = { ... }
}
}
}
Image with the issue
Finally i found a solution on a Xamarin forum post. here
It seems iOS render colors differently on layers. the workaround was to remake the cgcolor based on the xamarin forms color.
public class ExtendedFrameRenderer : FrameRenderer
{
public override void Draw(CGRect rect)
{
var gl = new CAGradientLayer
{
StartPoint = new CGPoint(0, 0),
EndPoint = new CGPoint(1, 1),
Frame = rect,
Colors = new CGColor[]
{
//old
//_gradinetControl.StartColor.ToCGColor(),
// _gradinetControl.EndColor.ToCGColor()
//fix
ToCGColor(__gradinetControl.StartColor),
ToCGColor(__gradinetControl.EndColor)
},
CornerRadius = _gradinetControl.CornerRadius
};
NativeView.Layer.BackgroundColor = UIColor.Clear.CGColor;
NativeView.Layer.InsertSublayer(gl,0);
base.Draw(rect);
}
public static CGColor ToCGColor(Color color)
{
return new CGColor(CGColorSpace.CreateSrgb(), new nfloat[] { (float)color.R, (float)color.G, (float)color.B, (float)color.A });
}
}
Related
I'm trying to make a horizontal UIStackView with 2 images and a label:
The first image ico_qol should be anchored to the left side of the view.
The second image ico_edit should be anchored to the right side of the view.
The label should be anchored in between the two images.
The view should be sized to the height of the images which are the same. It should span the entire screen width.
What I'm trying to achieve is something like this (selected elements in blue):
Here is my code:
public partial class ViewController : UIViewController
{
public ViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
UIStackView header = new UIStackView();
header.Axis = UILayoutConstraintAxis.Horizontal;
View.AddSubview(header);
//< color name = "healthy_green" >#409900</color>
var healthy_green = new UIColor(red: 0.25f, green: 0.60f, blue: 0.00f, alpha: 1.00f);
//< color name = "optimistic_orange" >#FF5F00</color>
var optimistic_orange = new UIColor(red: 1.00f, green: 0.37f, blue: 0.00f, alpha: 1.00f);
// Enable Auto Layout
header.TranslatesAutoresizingMaskIntoConstraints = false;
var ic_qol_image = UIImage.FromBundle("ic_qol");
ic_qol_image.ApplyTintColor(healthy_green);
var ic_qol = new UIImageView(ic_qol_image);
ic_qol.SizeToImage();
var qol_title = new UILabel { TranslatesAutoresizingMaskIntoConstraints = false, Text = #"Quality of Life" };
var ic_edit_image = UIImage.FromBundle("ic_edit");
ic_edit_image.ApplyTintColor(optimistic_orange);
var ic_edit = new UIImageView(ic_edit_image);
ic_edit.SizeToImage();
var qolHeaderViews = new UIView[] { ic_qol, qol_title, ic_edit };
header.AddSubviews(qolHeaderViews);
header.Anchor(top: View.SafeAreaLayoutGuide.TopAnchor, leading: View.LeadingAnchor, trailing: View.TrailingAnchor);
ic_qol.Anchor(top: header.TopAnchor, leading: header.LeadingAnchor);
ic_edit.Anchor(top: header.TopAnchor, trailing: header.TrailingAnchor);
qol_title.Anchor(top: View.SafeAreaLayoutGuide.TopAnchor, leading: ic_qol.RightAnchor, trailing: ic_edit.RightAnchor, padding: new UIEdgeInsets(0, 10, 0, 0)); <=== fails here.
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
}
internal static class extensions
{
// https://stackoverflow.com/a/48601685/15186
internal static void SizeToImage(this UIImageView uIImageView)
{
//Grab loc
var xC = uIImageView.Center.X;
var yC = uIImageView.Center.Y;
//Size to fit
uIImageView.Frame = new CGRect(x: 0, y: 0, width: uIImageView.Image.Size.Width / 2, height: uIImageView.Image.Size.Height / 2);
//Move to loc
uIImageView.Center = new CGPoint(x: xC, y: yC);
}
internal static void FillParentView(this UIView uIView)
{
uIView.Anchor(top: uIView.Superview?.TopAnchor, leading: uIView.Superview?.LeadingAnchor, bottom: uIView.Superview?.BottomAnchor, trailing: uIView.Superview.TrailingAnchor);
}
internal static void AnchorSize(this UIView uIView, UIView to)
{
uIView.WidthAnchor.ConstraintEqualTo(to.WidthAnchor).Active = true;
uIView.HeightAnchor.ConstraintEqualTo(to.HeightAnchor).Active = true;
}
internal static void Anchor(this UIView uIView, NSLayoutYAxisAnchor top = null, NSLayoutXAxisAnchor leading = null, NSLayoutYAxisAnchor bottom = null, NSLayoutXAxisAnchor trailing = null, UIEdgeInsets padding = default, CGSize size= default)
{
uIView.TranslatesAutoresizingMaskIntoConstraints = false;
if (top != null)
{
uIView.TopAnchor.ConstraintEqualTo(top, padding.Top).Active = true;
}
if (leading != null)
{
uIView.LeadingAnchor.ConstraintEqualTo(leading, padding.Left).Active = true; <=== fails here.
}
if (bottom != null)
{
uIView.BottomAnchor.ConstraintEqualTo(bottom, -padding.Bottom).Active = true;
}
if (trailing != null)
{
uIView.TrailingAnchor.ConstraintEqualTo(trailing, -padding.Right).Active = true;
}
if ( size.Width != 0)
{
uIView.WidthAnchor.ConstraintEqualTo(size.Width).Active = true;
}
if ( size.Height != 0)
{
uIView.HeightAnchor.ConstraintEqualTo(size.Height).Active = true;
}
}
}
I'm trying to constraint the Leading anchor of the Label to the trailing anchor of the image but it fails with the following exception:
A constraint cannot be made between <NSLayoutXAxisAnchor:0x280bcb980 "UILabel:0x1015132a0'Quality of Life'.leading">' and '<NSLayoutXAxisAnchor:0x280bcb840 "UIImageView:0x101510ea0.right">' because their units are not compatible.
I understand that their units may be incompatible but how can I do that anyways? How to make the units compatible?
Credits: The extensions methods I'm using are originally created during this video tutorial. I ported them to c#.
You can achieve this layout easily with a "horizontal" stack view.
Note: I don't use xamarin / c#, so I'll just describe what you want to do.
create the stack view
set its properties to
Axis: Horizontal
Distribution: Fill
Alignment: Center (this is the vertical alignment of the subviews)
Spacing: 8 (you probably want a little space between subviews)
create 2 image views
load an image into each image view
set width and height constraints on the image views as desired (it looks like you are using 1/2 the height and width of your images?)
create a label
add the image views and label to the stack view as arranged subviews
header.AddArrangedSubview(ic_qol);
header.AddArrangedSubview(qol_title);
header.AddArrangedSubview(ic_edit);
add the stack view to the view
View.AddSubview(header);
The last step will be to add constraints to put the stack view at the top, spanning the width of the view:
// Get the parent view's layout
var margins = View.LayoutMarginsGuide;
// Pin the top edge of the stackView to the margin
header.TopAnchor.ConstraintEqualTo (margins.TopAnchor).Active = true;
// Pin the leading edge of the stackView to the margin
header.LeadingAnchor.ConstraintEqualTo (margins.LeadingAnchor).Active = true;
// Pin the trailing edge of the stackView to the margin
header.TrailingAnchor.ConstraintEqualTo (margins.TrailingAnchor).Active = true;
The result should look about like this:
Each imageView is constrained to 40-pts width and height-equalTo-width. The label has a cyan background to make it easy to see the layout, and text alignment is center. The outlines are just there so we can see the stackView's frame.
and at runtime, without the frame outlines:
The following custom control
public class DummyControl : FrameworkElement
{
private Visual visual;
protected override Visual GetVisualChild(int index)
{
return visual;
}
protected override int VisualChildrenCount { get; } = 1;
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
var pt = hitTestParameters.HitPoint;
return new PointHitTestResult(visual, pt);
}
public DummyControl()
{
var dv = new DrawingVisual();
using (var ctx = dv.RenderOpen())
{
var penTransparent = new Pen(Brushes.Transparent, 0);
ctx.DrawRectangle(Brushes.Green, penTransparent, new Rect(0, 0, 1000, 1000));
ctx.DrawLine(new Pen(Brushes.Red, 3), new Point(0, 500), new Point(1000, 500));
ctx.DrawLine(new Pen(Brushes.Red, 3), new Point(500, 0), new Point(500, 1000));
}
var m = new Matrix();
m.Scale(0.5, 0.5);
RenderTransform = new MatrixTransform(m);
//Does work; but only the left top quater enters hit test
//var hv = new HostVisual();
//var vt = new VisualTarget(hv);
//vt.RootVisual = dv;
//visual = hv;
//Never enters hit test
visual = dv;
}
}
The xaml
<Window x:Class="MyNamespace.TestWindow"
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:local="clr-namespace:MyNamespace"
mc:Ignorable="d">
<Border Width="500" Height="500">
<local:DummyControl />
</Border>
</Window>
Display a green area with two red coordinate lines through the center. But its hit testing behavior is not understandable for me.
I put a breakpoint in the method HitTestCore but it never hits.
If I un-comment the code to use HostVisual and VisualTarget instead, it hits but only when the mouse is in the left top quater (indiciaed by the red lines given above)
How could the above being explained and how can I could make it work as expected (enters hit test on full range)?
(Originally, I just wanted to handle mouse events on the custom control. Some existing solutions pointed me to overriding the HitTestCore method. So if you could provide any idea that can let me handle mouse events, I don't have to make HitTestCore method working.)
Update
Clemen's answer is good if I decided to use DrawingVisual. However, when I use HostVisual and VisualTarget it is Not working without overriding HitTestCore, and even I do this, still only the top left quater will receive mouse events.
The original question also includes explainations. Also, the use of HostVisual allows me to run the render (time consuming in my real case) in another thread.
(Let me hightlight the code using HostVisual above)
//Does work; but only the left top quater enters hit test
//var hv = new HostVisual();
//var vt = new VisualTarget(hv);
//vt.RootVisual = dv;
//visual = hv;
Any idea?
UPDATE #2
Clemen's new answer is still not working for my purpose. Yes, all the visual area receives hit test. However, what I wanted is to have the full viewport to receive hit test. Which, in his case, is the blank area as he scaled the full visual to the visual area.
In order to establish a visual tree (and thus make hit testing work by default), you also have to call AddVisualChild. From MSDN:
The AddVisualChild method sets up the parent-child relationship
between two visual objects. This method must be used when you need
greater low-level control over the underlying storage implementation
of visual child objects. VisualCollection can be used as a default
implementation for storing child objects.
Besides that, your control should re-render whenever its size changes:
public class DummyControl : FrameworkElement
{
private readonly DrawingVisual visual = new DrawingVisual();
public DummyControl()
{
AddVisualChild(visual);
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Visual GetVisualChild(int index)
{
return visual;
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
using (var dc = visual.RenderOpen())
{
var width = sizeInfo.NewSize.Width;
var height = sizeInfo.NewSize.Height;
var linePen = new Pen(Brushes.Red, 3);
dc.DrawRectangle(Brushes.Green, null, new Rect(0, 0, width, height));
dc.DrawLine(linePen, new Point(0, height / 2), new Point(width, height / 2));
dc.DrawLine(linePen, new Point(width / 2, 0), new Point(width / 2, height));
}
base.OnRenderSizeChanged(sizeInfo);
}
}
When your control uses a HostVisual and a VisualTarget it would still have to re-render itself when its size changes, and also call AddVisualChild to establish a visual tree.
public class DummyControl : FrameworkElement
{
private readonly DrawingVisual drawingVisual = new DrawingVisual();
private readonly HostVisual hostVisual = new HostVisual();
public DummyControl()
{
var visualTarget = new VisualTarget(hostVisual);
visualTarget.RootVisual = drawingVisual;
AddVisualChild(hostVisual);
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Visual GetVisualChild(int index)
{
return hostVisual;
}
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParams)
{
return new PointHitTestResult(hostVisual, hitTestParams.HitPoint);
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
using (var dc = drawingVisual.RenderOpen())
{
var width = sizeInfo.NewSize.Width;
var height = sizeInfo.NewSize.Height;
var linePen = new Pen(Brushes.Red, 3);
dc.DrawRectangle(Brushes.Green, null, new Rect(0, 0, width, height));
dc.DrawLine(linePen, new Point(0, height / 2), new Point(width, height / 2));
dc.DrawLine(linePen, new Point(width / 2, 0), new Point(width / 2, height));
}
base.OnRenderSizeChanged(sizeInfo);
}
}
You could now set a RenderTransform and still get correct hit testing:
<Border>
<local:DummyControl MouseDown="DummyControl_MouseDown">
<local:DummyControl.RenderTransform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
</local:DummyControl.RenderTransform>
</local:DummyControl>
</Border>
This will work for you.
public class DummyControl : FrameworkElement
{
protected override void OnRender(DrawingContext ctx)
{
Pen penTransparent = new Pen(Brushes.Transparent, 0);
ctx.DrawGeometry(Brushes.Green, null, rectGeo);
ctx.DrawGeometry(Brushes.Red, new Pen(Brushes.Red, 3), line1Geo);
ctx.DrawGeometry(Brushes.Red, new Pen(Brushes.Red, 3), line2Geo);
base.OnRender(ctx);
}
RectangleGeometry rectGeo;
LineGeometry line1Geo, line2Geo;
public DummyControl()
{
rectGeo = new RectangleGeometry(new Rect(0, 0, 1000, 1000));
line1Geo = new LineGeometry(new Point(0, 500), new Point(1000, 500));
line2Geo = new LineGeometry(new Point(500, 0), new Point(500, 1000));
this.MouseDown += DummyControl_MouseDown;
}
void DummyControl_MouseDown(object sender, MouseButtonEventArgs e)
{
}
}
I'm having some trouble with gestures in xamarin forms. I've used an example I found from the web that wraps an Image control in a PanContainer which handles panning gestures:
using System;
using Xamarin.Forms;
namespace TestGestures
{
public class PanContainer : ContentView
{
double x, y;
public PanContainer ()
{
// Set PanGestureRecognizer.TouchPoints to control the
// number of touch points needed to pan
var panGesture = new PanGestureRecognizer ();
panGesture.PanUpdated += OnPanUpdated;
GestureRecognizers.Add (panGesture);
}
void OnPanUpdated (object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType) {
case GestureStatus.Running:
// Translate and ensure we don't pan beyond the wrapped user interface element bounds.
Content.TranslationX = Math.Max (Math.Min (0, x + e.TotalX), -Math.Abs (Content.Width - App.ScreenWidth));
Content.TranslationY = Math.Max (Math.Min (0, y + e.TotalY), -Math.Abs (Content.Height - App.ScreenHeight));
break;
case GestureStatus.Completed:
// Store the translation applied during the pan
x = Content.TranslationX;
y = Content.TranslationY;
break;
}
}
}
}
Wrapper usage:
public HomePageCS ()
{
Content = new AbsoluteLayout {
Padding = new Thickness (20),
Children = {
new PanContainer {
Content = new Image {
Source = ImageSource.FromFile ("MonoMonkey.jpg"),
WidthRequest = 1024,
HeightRequest = 768
}
}
}
};
}
This example works absolutely fine, no problems at all.
The problems start when I replace the image that is wrapped in the PanContainer with a control that inherits from View and has a customer renderer for each platform. When I do this the OnPanUpdated method never gets called.
Does anyone know why this happens on a View and is there any workaround to get it to work?
Is it possible to change the constraints of a RelativeLayout after they have been set one time?
In the documentation I see methods like GetBoundsConstraint(BindableObject), but I think you can only use them if you have a XAML file. For now I'm trying to do this in code. I get null if I try
RelativeLayout.GetBoundsConstraint(this.label);
The only way I found out is to remove the children from the layout and add it with the new constraints again.
Example:
public class TestPage : ContentPage
{
private RelativeLayout relativeLayout;
private BoxView view;
private bool moreWidth = false;
public TestPage()
{
this.view = new BoxView
{
BackgroundColor = Color.Yellow,
};
Button button = new Button
{
Text = "change",
TextColor = Color.Black,
};
button.Clicked += Button_Clicked;
this.relativeLayout = new RelativeLayout();
this.relativeLayout.Children.Add(this.view,
Constraint.Constant(0),
Constraint.Constant(0),
Constraint.Constant(80),
Constraint.RelativeToParent((parent) =>
{
return parent.Height;
}));
this.relativeLayout.Children.Add(button,
Constraint.RelativeToParent((parent) =>
{
return parent.Width / 2;
}),
Constraint.RelativeToParent((parent) =>
{
return parent.Height / 2;
}));
this.Content = this.relativeLayout;
}
private void Button_Clicked(object sender, EventArgs e)
{
double width = 0;
if(this.moreWidth)
{
width = 120;
}
else
{
width = 80;
}
var c= BoundsConstraint.FromExpression((Expression<Func<Rectangle>>)(() => new Rectangle(0, 0, width, this.Content.Height)), new View[0]);
RelativeLayout.SetBoundsConstraint(this.view, c);
this.relativeLayout.ForceLayout();
this.moreWidth = !this.moreWidth;
}
}
It does not officially possible with the current version of Xamarin Forms. The RelativeLayout container only recomputes constraints when adding/removing items from its children collection (it caches the solved constraints - presumable for performance). Even though the various constraints are implemented as Bindable Properties, they still do not get recomputed when changed.
I assume that the intention is to someday respect constraint updates, which would be useful with animations for example, but for now it doesn't appear to work that way.
HOWEVER, I took a look at the decompiled source for RelativeLayout and it is possible to hack together a way around it - but it might not suit your needs, depending on how much functionality you require and how complex your constraint definitions are.
See this example code (the key part is setting the constraint using SetBoundsConstraint, which overrides the internally computed bounds of the added view - and then calling ForceLayout()):
public partial class App : Application
{
public App ()
{
var label = new Label {
Text = "Test",
HorizontalTextAlignment = TextAlignment.Center,
VerticalTextAlignment = TextAlignment.Center,
BackgroundColor = Color.Silver
};
var layout = new RelativeLayout ();
layout.Children.Add (label,
Constraint.Constant (50),
Constraint.Constant (100),
Constraint.Constant (260),
Constraint.Constant (30));
MainPage = new ContentPage {
Content = layout
};
var fwd = true;
layout.Animate ("bounce",
(delta) => {
var d = fwd ? delta : 1.0 - delta;
var y = 100.0 + (50.0 * d);
var c = BoundsConstraint.FromExpression ((Expression<Func<Rectangle>>)(() => new Rectangle (50, y, 260, 30)), new View [0]);
RelativeLayout.SetBoundsConstraint(label, c);
layout.ForceLayout ();
}, 16, 800, Easing.SinInOut, (f, b) => {
// reset direction
fwd = !fwd;
}, () => {
// keep bouncing
return true;
});
}
}
Yes. This possible.
Layout code:
<StackLayout RelativeLayout.XConstraint="{Binding XConstaint}" ...>
VM code:
public Constraint XConstaint
{
get => _xConstaint;
set { SetFieldValue(ref _xConstaint, value, nameof(XConstaint)); }
}
public override void OnAppearing()
{
base.OnAppearing();
XConstaint = Constraint.RelativeToParent((parent) => { return parent.Width - 128; });
}
If i understood you correctly , I would try the following
Implement SizeChanged event for relativeLayout
like :-
var relativeLayout = new RelativeLayout ();
relativeLayout.SizeChanged += someEventForTesting;
and invoke the event whenever you want to resize the relative layout or some particular child.
public void someEventForTesting(Object sender , EventArgs)
{
var layout = (RelativeLayout)sender ;
//here you have access to layout and every child , you can add them again with new constraint .
}
I am just playing around with bing maps at the moment. I have followed the tutorials to create routes and add mapicons etc, however I have found that a mapIcon wont show if it on the route. I have tried playing with the Z-Index property of the mapIcon, however this seemed to have little effect, as I think it only has an effect with other MapElements.
Does anyone else know a way to make this happen?
My current code for creating the route and trying to set a MapIcon on the destination is (For abit of Content MapFunctions is just a static class I've made for functons such as finding the route, get the current location etc):
private async void DrawRoute()
{
// Check if a destination has been set
if(MapFunctions.destination != null)
{
route = await MapFunctions.GetRouteAsync(MapFunctions.destination.Point);
if (route != null)
{
// Use the route to initialize a MapRouteView.
MapRouteView viewOfRoute = new MapRouteView(route.Route);
viewOfRoute.RouteColor = Colors.Yellow;
viewOfRoute.OutlineColor = Colors.Black;
// Add the new MapRouteView to the Routes collection
// of the MapControl.
MyMap.Routes.Add(viewOfRoute);
// Fit the MapControl to the route.
await MyMap.TrySetViewBoundsAsync(
route.Route.BoundingBox,
null,
Windows.UI.Xaml.Controls.Maps.MapAnimationKind.None);
AddDestinationMapElement(MapFunctions.destination.Point);
// Start timer to update the remaining time of the journey
UpdateRemainingTime_Tick(this, new Object());
dispatcherTimer.Start();
}
else
{
MessageDialog message = new MessageDialog("An error occured while trying to calculate your route. Please try again later.", "Error");
await message.ShowAsync();
}
}
}
private void AddDestinationMapElement(Geopoint dest)
{
MapIcon MapIcon1 = new MapIcon();
MapIcon1.Location = new Geopoint(new BasicGeoposition()
{
Latitude = dest.Position.Latitude,
Longitude = dest.Position.Longitude
});
MapIcon1.Visible = true;
MapIcon1.ZIndex = int.MaxValue;
MapIcon1.Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Images/mapIcon.png"));
MapIcon1.NormalizedAnchorPoint = new Point(0.5, 1.0);
MapIcon1.Title = "Destination";
MyMap.MapElements.Add(MapIcon1);
}
If you want to add pushpin on top of your elements, you can use MapOverlay that will allow you to add pushpin (with image or any XAML element) into your map control:
MapOverlay pushpinStart = new MapOverlay();
pushpinStart.PositionOrigin = new Point(0.5, 0.5);
pushpinStart.Content = new Image()
{
Source =
new BitmapImage(
new Uri("../Assets/Ui/ui-pin-start.png", UriKind.Relative))
};
pushpinStart.GeoCoordinate = posCollection[0];
If you want to stay with MapIcon, then the rendering engine will calculate what's best to be displayed based on the zoom level and current location as well as other elements collision algorithm's result. So I'm not sure that what you're looking for.
And for WP8.1, here is the equivalent code:
Image iconStart = new Image();
iconStart.Source = new BitmapImage(new Uri("ms-appx:///Assets/Ui/ui-pin-start.png"));
this.Map.Children.Add(iconStart);
MapControl.SetLocation(iconStart, new Geopoint(pos[0]));
MapControl.SetNormalizedAnchorPoint(iconStart, new Point(0.5, 0.5));