how to show selectors on Xamarin.Forms.maps.map - xamarin.forms

My app has a map component that I create within a Stacklayout in the XAML like this:
<maps:Map x:Name="amap">
<x:Arguments>
<maps:MapSpan>
<x:Arguments>
<maps:Position>
<x:Arguments>
<x:Double x:Name="Lat">48.2</x:Double>
<x:Double x:Name="Long">-106.6501939</x:Double>
</x:Arguments>
</maps:Position>
<x:Double>0.01</x:Double>
<x:Double>0.01</x:Double>
</x:Arguments>
</maps:MapSpan>
</x:Arguments>
</maps:Map>
I then get a location from a db search, drop a pin at that location and pan to it:
private void MoveMap(Quarter location)
{
var pin = new Pin();
pin.Position = new Position(location.Lat, location.Lon);
pin.Label = locationEntry.Text;
MoveMap(pin);
}
private void MoveMap(Pin pin)
{
amap.Pins.Add(pin);
amap.MoveToRegion(new MapSpan(pin.Position, 0.1, 0.1));
}
So far so good, but if I click on the pin it gets better...
I'm looking for a way to get the links to Google maps on the bottom to appear right off the bat. I would also like to make the label show on the pin. I've tried calling amap.SendMapClicked(pin.Position) right after the call to MoveToRegion, but nothing happens.
And finally I would like to display the "mode selector" from Google maps, which switches between satellite and default.
Is any of this possible? Lots of googling doesn't give me much hope.

Try this it's working for me
private void MoveMap(Quarter location)
{
var pin = new Pin
{
Type = PinType.Place,
Position = new Position(location.Lat, location.Lon),
Label = locationEntry.Text
};
amap.Pins.Add(item.Pins);
amap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(pin.Position.Latitude, pin.Position.Longitude),
Distance.FromMiles(50)));
}

Related

Xamarin Forms switch language at runtime on each page

I'm developing a cross-platform app (android, IOS) which has to be able to switch languages on each page at the press of a button. I'm using an AppShell for navigation and have the button for the language switch in the toolbar of the AppShell. I made resource files: AppResources.resx and AppResources.fr.resx. I reload both the current page I am on and the AppShell when switching, which seems to have the side effect of going back to the first page I have set on my navigation bar. The reload of the AppShell seems to be necessary as when I don't do it the page seems to go on top and there is no more navigation as well as the color resources I have set in the AppShell get removed. I use the below code to switch the language of my app:
private void Language_switch(object sender, EventArgs e)
{
var lang_switch = Lang.Text;
if (lang_switch == "FR")
{
CultureInfo language = new CultureInfo("fr");
Thread.CurrentThread.CurrentUICulture = language;
AppResources.Culture = language;
Application.Current.Properties[key: "LanguageCode"] = "fr_FR";
}
else
{
CultureInfo language = new CultureInfo("");
Thread.CurrentThread.CurrentUICulture = language;
AppResources.Culture = language;
Application.Current.Properties[key: "LanguageCode"] = "nl_NL";
}
Application.Current.MainPage = new Surveys();
Application.Current.MainPage = new AppShell();
}
This code used to work but is not working anymore. I do remember updating Xamarin Forms a bit ago so this might have something to do with the code not working anymore. In XAML I read from the resource files as below:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyOpinion.Views.Surveys"
xmlns:vm="clr-namespace:MyOpinion.ViewModels"
Title="{Binding Title}"
xmlns:resource="clr-namespace:MyOpinion.Resx">
<Label Text="{x:Static resource:AppResources.Openstaande}" TextColor="{StaticResource Text}" FontSize="24" Margin="0,0,0,10"/>
</ContentPage>
you can use the TranslateExtension provided from Xamarin Community Toolkit. You don't need to reload your pages.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="MyLittleApp.MainPage">
<StackLayout>
<Label Text="{xct:Translate AppResources.ATranslatedMessage}" />
<Label Text="{xct:Translate AppResources.AnotherTranslatedMessage, StringFormat='#{0}'}" />
</StackLayout>
</ContentPage>
You should Initiliaze it first:
LocalizationResourceManager.Current.PropertyChanged += (_, _) => AppResources.Culture = LocalizationResourceManager.Current.CurrentCulture;
LocalizationResourceManager.Current.Init(AppResources.ResourceManager);
After Language change you call:
LocalizationResourceManager.Current.CurrentCulture = newCulture;
Here is documentation
https://learn.microsoft.com/en-us/xamarin/community-toolkit/extensions/translateextension
https://learn.microsoft.com/en-us/xamarin/community-toolkit/helpers/localizationresourcemanager
As #Michael said i dont think either there is a way to change language without poping to Root. Though i dont recommend his idea of copping the navigation stack then pushing the pages. But i will implement the idea for you.
Note that navigation stack in readonly property.
var navStack = Application.Current.MainPage.Navigation.NavigationStack;
Application.Current.MainPage.Navigation.RemovePage(navStack.First());
Application.Current.MainPage = new AppShell ();
foreach (var item in navStack)
{
await Application.Current.MainPage.Navigation.PushAsync((Page)Activator.CreateInstance(item.GetType()));
}

Xamarin.Forms: Bind SwipeView to code and get events

I am quite new to xamarin.forms.
So I set up a swipeview (https://github.com/markolazic88/SwipeCardView/blob/master/docs/index.md)
But I can't seem to understand what they mean by "glueing" xaml with C#' together.
In the docs it says there are eventhandlers like this one:
void OnSwiped(object sender, SwipedCardEventArgs e)
{
switch (e.Direction)
{
case SwipeCardDirection.None:
break;
case SwipeCardDirection.Right:
break;
case SwipeCardDirection.Left:
break;
case SwipeCardDirection.Up:
break;
case SwipeCardDirection.Down:
break;
}
}
But just adding this function into the connect class doesn't work. Nothing fires. I was however able to access the swipeview like this:
public Screen_SwipeView()
{
InitializeComponent();
List<string> data = new List<string>() { "a", "b", "c" };
swipeview_swipeview.ItemsSource = data;
}
But this method isn't mentions in the docs.
How can I get the eventhandlers to fire. I feel like I am missing the bridge between the xaml component and the code.
I also tried making a class and letting it inherit from this components but that only threw errors.
You can also define the SwipeView properties (LeftItems, RightItems, TopItems, BottomItems) in your Xaml and then bind their command properties to a Command object define in your viewmodel. Example :
<SwipeView>
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem Text="Annuler"
IconImageSource="deleteitem.png"
BackgroundColor="LightPink"
Command="{Binding Source={x:Reference yourViewName}, Path=BindingContext.DeleteCommand}"
CommandParameter="{Binding .}" />
</SwipeItems>
</SwipeView.RightItems>
</SwipeView>
Easy one:
swipeview_swipeview.Dragging += OnDragging;
swipeview_swipeview.Swiped += OnSwiped;

Add element to XAML page whiles in Task.Delay()

I'm building a chatbot app with chat bubbles for incoming and outgoing messages. For the incoming messages, I've given it a Task.Delay() and now I'd like to give it an ActivityIndicator every time before the message pops up (i.e. I want to show the activity indicator whiles the message is being delayed). I've added the activity indicator to the XAML of the incoming messages control;
IncomingMessageItemControl
<ViewCell
x:Class="BluePillApp.Controls.IncomingMessageItemControl"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pancake="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
mc:Ignorable="d">
<Grid x:Name="Gridoo">
<pancake:PancakeView
Margin="10,10,80,10"
Padding="15"
BackgroundColor="#53ffc6"
CornerRadius="20,20,0,20"
HasShadow="False"
HorizontalOptions="StartAndExpand">
<Label
FontSize="Medium"
Text="{Binding Text}"
TextColor="#1a1a1a" />
</pancake:PancakeView>
<ActivityIndicator IsRunning="True" IsVisible="True" />
</Grid>
</ViewCell>
The problem is, in the ChatbotMessagingPage, the send button is pressed then an outgoing message is sent before getting a reply/incoming message and I've done this in MVVM like so;
ChatbotMessagingPageViewModel
//This gets the chatbots response for each message
chatbot.MainUser.ResponseReceived += async (sender, args) =>
{
await Task.Delay(1500);
Messages.Add(new ChatMessageModel() { Text = args.Response.Text, User = App.ChatBot });
};
}
#region CLASS METHODS
/// <summary>
/// This function sends a message
/// </summary>
public void Send()
{
if (!string.IsNullOrEmpty(TextToSend))
{
var msgModel = new ChatMessageModel() { Text = TextToSend, User = App.User };
//This adds a new message to the messages collection
Messages.Add(msgModel);
var result = chatbot.Evaluate(TextToSend);
result.Invoke();
//Removes the text in the Entry after message is sent
TextToSend = string.Empty;
}
}
Everytime I press the send button the ActivityIndicator comes along with the IncomingMessage, I'd like the ActivityIndicator to come first, whiles the IncomingMessage is being delayed.
I'm guessing that that view cell is the message bubble.
When you do:
Messages.Add(new ChatMessageModel() { Text = args.Response.Text, User = App.ChatBot });
Your collection is updated and your ListView or whatever hold those ViewCelss is also updated. The ActivityIndicator is part of the ViewCell so it comes at the same time as the message.
[OPTION 1] Using an additional flag
What you can do is create a flag IsBusy or IsDelay or something and bind the visibility of the ActivityIndicator and Label to it:
<Grid x:Name="Gridoo">
<pancake:PancakeView
Margin="10,10,80,10"
Padding="15"
BackgroundColor="#53ffc6"
CornerRadius="20,20,0,20"
HasShadow="False"
HorizontalOptions="StartAndExpand">
<Label
FontSize="Medium"
Text="{Binding Text}"
TextColor="#1a1a1a"
IsVisible="{Binding IsBusy, Converter={Helpers:InverseBoolConverter}}""> />
</pancake:PancakeView>
<ActivityIndicator x:Name="activityIndicator" IsRunning="True" IsVisible="{Binding IsBusy}" />
</Grid>
Note that I've used a IValueConverter to negate the value for the label. In case you're not familiar with it, check this
What's left is to add the flag in your ViewModel:
IsBusy = true; // this will make the activity indicator visible, but not the Label
// Also note that you first need to add the message
Messages.Add(new ChatMessageModel() { Text = args.Response.Text, User = App.ChatBot });
await Task.Delay(1500);
IsBusy = false; // this will hige the activity indicator visible, and make Label visible
So basically the logic is the following:
You add the message to your chat BUT the actual text is hidden when on the other hand, the activity indicator is visible.
You apply the delay
Delay ends, you change the visibility of both views.
Note that in my example I've not declared where that flag is since I'm not sure how the rest of your code looks like. It could be added to ChatMessageModel or ChatMessageViewModel since you would need a flag for each message.
[OPTION 2] in IncomingMessageItemControl.xaml.cs
A better solution could be to handle it in the code behind of your control. The issue is the same, the activity indicator and the label comes at the same time.
To fix this you can move the delay in IncomingMessageItemControl.xaml.cs.
First, you need to add x:Name to both the activity indicator and the label.
Then you could do:
private async Task ChangeVisibilityAsync()
{
Label.IsVisibe= false;
ActivityIndicator.IsVisible = true;
await Task.Delay(1500);
Label.IsVisibe = true;
ActivityIndicator.IsVisible = false;
}

setting up a simple component with data binding

I am trying to set up a component with data binding. This is basically a seperate content view that would have a property Item of type Item and supports binding. The following is the definition for the binding:
public static readonly BindableProperty ItemProperty
= BindableProperty.Create(
nameof(Item), typeof(Item), typeof(ItemComponent), null,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: ItemPropertyChanged);
private readonly ItemComponentViewModel vm;
static void ItemPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = (ItemComponent)bindable;
view.Item = (Item)newValue;
}
public Item Item
{
get => (Item)GetValue(ItemProperty);
set
{
SetValue(ItemProperty, value);
if (vm != null) vm.Data = value; // break point here
}
}
The item doesn't seem to get bound. The commented line had a breakpoint and doesn't break. The complete source code is here: https://github.com/neville-nazerane/xamarin-component-sample
The above code can be found in the ItemComponent class. This component is called in the MainPage class.
Update
Just to explain what I am trying to simulate and why:
Why do we use MVVM in pages? While we'll have better type safety and performance by using the behind code directly, when the page's logic gets bigger, it becomes cleaner to handle it with a view model and to have a view that is simply bound to it.
Why do we have components? So that we can reuse a UI we intend to use with some functionality. If this functionality becomes complex it might need a view model for the same reason explained above. Hence, if pages need view models, I don't see why components won't need them at some point too.
This being considered this does feel like a particle requirement without easy to find examples.
So after looking at your example it turns out it's a bit of a complicated problem. So if my explanation is not clear, please let me know.
Basically the problem lies in these 2 code pieces:
MainPage.xaml(line 14):
<local:ItemComponent Item="{Binding Demo}" />
ItemComponent.xaml.cs (line 43):
public ItemComponent()
{
InitializeComponent();
vm = new ItemComponentViewModel();
BindingContext = vm; //this breaks the functionality
}
The first part you tell it to bind to the Demo property, and as normal it looks for this property in it's BindingContext. However in the second part you override it's BindigContext and set it to a ItemComponentViewModel this ViewModel however does not have a property Demo so the {Binding Demo} does not work on this new BindingContext you've set.
Now a possible solution for your demo application would be to change MainPage.xaml to the following code:
<?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:local="clr-namespace:SampleApp"
x:Class="SampleApp.MainPage"
x:DataType="local:MainViewModel"
x:Name="MyDemoPage">
<StackLayout>
<Label Text="Manual:" />
<Label Text="{Binding Demo.Title}" />
<Label Text="Component: " />
<local:ItemComponent Item="{Binding Path=BindingContext.Demo, Source={x:Reference MyDemoPage}}" />
</StackLayout>
</ContentPage>
Basically we now place the Demo binding outside of the BindingContext of our ItemComponent control. However if you want to use it in a ListView (if I remember correctly from your original question, this solution might not work and it's possible you'll have to drop the ItemComponentViewModel and bind directly to the properties (ListView will already make sure that the BindingContext of your ItemComponent is set to the current Item, no need to pass it around through a bindable property.
Hope this helps!

System.ArgumentException: <Timeout exceeded getting exception details> Xamarin.Forms

I am new to this awesome platform, I made very simple example it contains only one XAML page. Yesterday it worked flawlessly, but when ran it today, it threw this exception out of nowhere
This the exception:
HelloPage.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"
x:Class="SAP.HelloPage">
<!-- la configuration personalisé pour chaque system d'éxploitation -->
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="0,10,0,0"
Android="0,40,0,0">
</OnPlatform>
</ContentPage.Padding>
<StackLayout HorizontalOptions="Center" BindingContext="{x:Reference sliderID}">
<Button Clicked="Button_Clicked" Text="Suivant"/>
<Label x:Name="lb_font" Text="Font is :"/>
<Slider Minimum="16" Maximum="45" x:Name="sliderID" ValueChanged="sliderID_ValueChanged"/>
<Label x:Name="lb_quote"/>
</StackLayout>
<ContentPage>
HelloPage.xaml.cs :
public partial class HelloPage : ContentPage
{
// quotes
List<string> quotes = new List<string>()
{
"Bienvenu, la première paragraphe",
"une autre quote, cella j'adore",
"La troisième est magnifique"
};
int pos = 0;
public HelloPage()
{
InitializeComponent();
// settings initialisation
lb_quote.Text = quotes.ElementAt(0);
}
private void Button_Clicked(object sender, EventArgs e)
{
if(pos == quotes.Count)
{
pos = 0;
}
lb_quote.Text = quotes.ElementAt(pos);
pos += 1;
}
private void sliderID_ValueChanged(object sender, ValueChangedEventArgs e)
{
lb_font.Text ="Font Size : " + sliderID.Value.ToString();
lb_quote.FontSize = sliderID.Value;
}
}
This one is quite subtle and hard to catch (well, actually it isn't hard to catch; just put a try/catch around InitializeComponent and you can examine the exception).
XAML is declarative which makes us believe that the order of attributes of a control does not matter. Unfortunately, since at some point the declarative XAML will be turned into a sequence of property assignments, the order of attributes does matter and it's the order of your Slider attributes that is causing your exception to be thrown.
You can imagine your Slider being constructed the following way:
var slider = new Slider();
slider.Minimum = 16;
slider.Maximum = 45;
...
But Slider.Minimum and Slider.Maximum seem to check if the values passed are valid.
When your code begins, the value of Maximum defaults to 0. But before its value can be assigned, Minimum's value is assigned a value of 16.
At this moment your Minimum value (16) is greater than the default Maximum value (0) and hence an ArgumentOutOfRangeException is thrown.
To solve it, just set the Maximum before the Minimum value and it does work.
As #Paul mentioned. Wrap it with try-catch and it gives more details about the exception. I spent few hours before figuring out to do that

Resources