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()));
}
Related
Good morning
I have a TabbedPage in my application. Due to restictions I would like to display one more tab on Android than on iOS.
My current TabbedPage look like:
<TabbedPage>
...
<ContentPage x:Name="Page1"/>
<ContentPage x:Name="Page2"/>
<ContentPage x:Name="Page3"/>
<ContentPage x:Name="Page4"/>
</TabbedPage>
I have decided to render Page3 only for Android. I changed my code into:
<TabbedPage>
...
<ContentPage x:Name="Page1"/>
<ContentPage x:Name="Page2"/>
<OnPlatform x:TypeArguments="Page">
<On Platform="Android">
<On.Value>
<ContentPage x:Name="Page3"/>
</On.Value>
</On>
</OnPlatform>
<ContentPage x:Name="Page4"/>
</TabbedPage>
This leads to runtime exception when entering into this TabbedPage:
An error occurred: 'Value cannot be null. Parameter name: item'.
Callstack: ' at
Xamarin.Forms.ObservableWrapper`2[TTrack,TRestrict].Add (TRestrict
item) [0x00008] in D:\a\1\s\Xamarin.Forms.Core\ObservableWrapper.cs:27
I tried to remove x:Name but did not work. Any ideas?
PS. As a workaround I am always able to:
Constructor()
{
if(Runtime.IsIOS)
{
this.Children.Remove(this.Page3)
}
}
However would be better to not render it at all and have it at XAML level.
Unless someone comes up with a way to do this in XAML, this is the best you can do (building on Jason's comment):
xaml:
<ContentPage x:Name="Page1"/>
<ContentPage x:Name="Page2"/>
<!-- no Page3 in XAML -->
<ContentPage x:Name="Page4"/>
c#:
private ContentPage page3;
Constructor()
{
InitializeComponent();
if (Runtime.IsAndroid)
{
page3 = new ContentPage();
// After pages 1 and 2.
Children.Insert(2, page3);
}
}
// Elsewhere in code-behind.
if (page3 != null)
{
...refer to page3...
}
This has the advantage of not constructing the page at all on iOS.
It also makes it easy to test whether page3 is there (page3 != null).
In practice, the other pages will typically be their own classes. Given partial class Page3 : ContentPage elsewhere:
private Page3 page3;
...
page3 = new Page3();
According to this,xaml is an alternative to programming code for instantiating and initializing objects, and organizing those objects in parent-child hierarchies.If you add one additional tab on android it will cause a NullReferenceException on ios.So you may want to do it in codebehind with Device.RuntimePlatform.
Here is my test you can refer to:
switch (Device.RuntimePlatform)
{
case Device.Android:
Children.Add(page1);
Children.Add(page2);
break;
case Device.iOS:
Children.Add(page1);
break;
case Device.UWP:
Children.Add(page1);
break;
}
When I use -
string result = await DisplayPromptAsync("Question 1", "What's your name?");
It shows only one textbox in the pop-up. But how to display two or more textboxes in the pop-up?
Any help would be greatly appreciated.
As IvanIčin said that you can use Rg.Plugins.Popup to create custom popup.
Firstly, install Rg.Plugins.Popup bu nuget package..., then creating popup page
<pages:PopupPage
x:Class="FormsSample.popup.popup2"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup">
<pages:PopupPage.Content>
<StackLayout
Padding="20,0"
BackgroundColor="CadetBlue"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center">
<Label Text="Question 1" />
<Label Text="this is one question!" />
<Entry />
<Entry />
<Button
x:Name="btnsub"
Clicked="btnsub_Clicked"
Text="subit" />
</StackLayout>
</pages:PopupPage.Content>
</pages:PopupPage>
public partial class popup2 : PopupPage
{
public popup2()
{
InitializeComponent();
}
private void btnsub_Clicked(object sender, EventArgs e)
{
}
}
To call this Popup Page from contentpage button.click event.
private async void btnPopupButton_Clicked(object sender, EventArgs e)
{
await PopupNavigation.Instance.PushAsync(new popup2());
}
You can see the screenshot:
You can't as it is not intended to. You can create a custom pop-up either by using some pop-up plug-in or by creating your custom code based on the native prompts (similar to what Xamarin.Forms do).
Just for the record having one input field is very generous from Xamarin as the native Android or iOS developers don't have such a prompt with the input field out of the box (though it isn't too hard to create it but still it goes much beyond one line of code).
I am trying to play a video using Plugin.MediaManager.Forms and I am referring to this blog.
Step 1: Added Plugin.MediaManager and Plugin.MediaManager.Forms.
Step 2: XAML code - Added a VideoView
<?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:VideoPlayerApp"
x:Class="VideoPlayerApp.MainPage"
xmlns:forms="clr-namespace:Plugin.MediaManager.Forms;assembly=Plugin.MediaManager.Forms"
Title="Video Player">
<ContentPage.Content>
<StackLayout>
<Label Text="Xamarin Forms"
FontSize="40"
TextColor="Azure"/>
<Label Text="Video Player Application"
FontSize="58"
TextColor="BlueViolet"/>
<Button x:Name="PlayStopButtonText"
Text="Play"
Clicked="PlayStopButton"
TextColor="BlueViolet"/>
<forms:VideoView HeightRequest="202"
WidthRequest="202"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Step 3: xaml.cs code
public partial class MainPage : ContentPage
{
private string videoUrl = "https://sec.ch9.ms/ch9/e68c/690eebb1-797a-40ef-a841-c63dded4e68c/Cognitive-Services-Emotion_high.mp4";
public MainPage()
{
InitializeComponent();
}
private void PlayStopButton(object sender, EventArgs e)
{
if (PlayStopButtonText.Text == "Play")
{
CrossMediaManager.Current.Play(videoUrl, MediaFileType.Video);
PlayStopButtonText.Text = "Stop";
}
else if (PlayStopButtonText.Text == "Stop")
{
CrossMediaManager.Current.Stop();
PlayStopButtonText.Text = "Play";
}
}
}
But getting error on this step:
Error CS0103 The name 'MediaFileType' does not exist in the current context
Step 4: Also added VideoViewRenderer.Init();in MainActivity.cs, AppDelegate.cs and MainPage.xaml.cs. But getting following error for this initialization.
The name 'VideoViewRenderer' does not exist in the current context
Am I missing something? I checked some other blogs but same error occuring. I have added a sample project here.
Android Options Screenshot:
The blog seems out of date. Part of APIs and methods were obsoleted . You should check the newest docs from https://github.com/martijn00/XamarinMediaManager#usage .
use the following code instead of VideoViewRenderer.Init() ;
CrossMediaManager.Current.Init();
And just need to call the method
CrossMediaManager.Current.Play(videoUrl);
And I checked your demo . You need to update the version of Xamarin.Forms to 4.2.x both in share project and specific platforms(Android and iOS) .Which will match to the version of the plugin.
Don't forget to set the Dex complier to d8 .
Right click your Android project -> Property-> Android Options.
I'm using Prism.Forms 6.3 in a Xamarin.Forms 2.3.4.247 project. I'm having a hard time tracking why the back arrow button isn't visible when I navigate to a details page within a Master/Detail setup.
I can navigate to the Pages just fine, but the back-button never shows up. Instead, the hamburger menu icon is always visible. This is my "Master" page.
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FloatSink.Apps.Mobile.Views.ValueConverters"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="FloatSink.Apps.Mobile.Views.MainPage"
BackgroundColor="Blue">
<MasterDetailPage.Master>
<ContentPage Title="Menu">
<StackLayout Padding="40">
<Label Text="Hello" />
<Button Text="Feed" Command="{Binding NavigateCommand}" CommandParameter="NavigationPage/FeedPage" />
<Button Text="Options" Command="{Binding NavigateCommand}" CommandParameter="NavigationPage/OptionsPage" />
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
</MasterDetailPage>
This is two of my Detail pages.
<?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="FloatSink.Apps.Mobile.Views.FeedPage">
<Label Text="Hello from Feed Page!" />
</ContentPage>
<?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="FloatSink.Apps.Mobile.Views.OptionsPage">
<Label Text="Hello from Options Page!" />
</ContentPage>
I navigate to it using the CommandParameter specified in my Master page, using the navigation service in the MainPageViewModel.
private void NavigateToPage(string uri)
{
this.navigationService.NavigateAsync(uri);
}
I've setup my NavigationPage like this during the startup of the app so I land on the FeedPage first, then I can navigate to the OptionsPage.
public class App : PrismApplication
{
public App(IPlatformInitializer dependencyRegister) : base(dependencyRegister) { }
protected override void OnInitialized()
{
base.NavigationService.NavigateAsync("MainPage/NavigationPage");
}
protected override void RegisterTypes()
{
var builder = new ContainerBuilder();
builder.RegisterModule<NavigationRegistration>();
builder.RegisterModule<ServicesRegistration>();
base.Container.RegisterTypeForNavigation<NavigationPage>();
// This is deprecated but we have to wait for Prism.Autofac to update itself
builder.Update(base.Container);
}
}
The DI registrations associated with navigation are done in this module:
internal class NavigationRegistration : Module
{
/// <summary>
/// Load the navigation related types into the given builder.
/// </summary>
/// <param name="builder">Container Builder that will be turned into the final IOC Container</param>
protected override void Load(ContainerBuilder builder)
{
// Register the NavigationPage in Xamarin with the Prism Navigation system.
//builder.RegisterType<NavigationPage>().AsSelf();
//PageNavigationRegistry.Register(nameof(NavigationPage), typeof(NavigationPage));
// Get all of the Types that represent a View in our assembly for DI and navigation registration
// If start-up time ever becomes an issue, we can replace this assembly scan with explicit registrations.
Type[] viewTypes = base.ThisAssembly.GetTypes().Where(type => type.IsAssignableTo<Page>()).ToArray();
// Iterate over each discovered View Type and register it with the navigation system and DI container.
for(int index = 0; index < viewTypes.Length; index++)
{
Type viewType = viewTypes[index];
builder.RegisterType(viewType).Named<Page>(viewType.Name);
// If we ever need to disconnect a view name from its Type name, we can do so here.
// We would build a custom attribute to decorate the view with, pull the attribute from the Type
// and register the Type with the attribute value.
PageNavigationRegistry.Register(viewType.Name, viewType);
}
}
}
Again I can each one of my detail pages without problem, the hamburger menu exists and I can open the Master page to view my buttons used for navigating. I just can't navigate backwards once I'm there. Is that something I'm supposed to wire up myself?
I'm not sure I'm reading your question right but it sounds like this is normal behavior. In my (short) experience with XF/Prism, every navigation from the master is a beginning of the stack. If you were to add another page, e.g. from Master->PageA->PageB, I would expect Page A to have the hamburger menu but going to PageB would give you the back arrow.
For using NavigationPage inside uri you should register it for navigation in the App.xaml.cs:
protected override void RegisterTypes()
{
// your registrations
Container.RegisterTypeForNavigation<NavigationPage>();
}
I most cases it is the reason.
To Navigate To Master Page
"/MasterPage/NavigationPage/ViewA"
To Navigate out of Master Page from ViewA and with back button
"ViewB"
You need to start your app with MainPage = new NavigationPage(new StartPage()); That is how it is solved in normal situation. So maybe try to register your MainPage wrapped in a NavigationPage.
My app has a WebView for displaying some contact information. It has a link to a website that I want to load externally using Device.OpenUri(). I'm using FreshMvvm and I want to intercept the Navigating event from the WebView in the ViewModel and cancel the default action which would load the external page into the WebView.
I've tried using the Corcav.Behaviors plugin which does call my ViewModel command:
<WebView
HorizontalOptions="Fill"
VerticalOptions="FillAndExpand"
Source="{Binding WebViewSource}">
<b:Interaction.Behaviors>
<b:BehaviorCollection>
<b:EventToCommand
EventName="Navigating"
Command="{Binding NavigatingCommand}"
CommandParameter="{Binding}"/> <!-- what goes here -->
</b:BehaviorCollection>
</b:Interaction.Behaviors>
</WebView>
But I'm not sure what the CommandParameter should be - I need the URI of the link that was tapped, and I don't know how to then prevent the default behaviour from occurring.
Is this the best approach or should I be looking at an alternative?
Having revisited this recently for another project I stumbled across the answer. The updated XAML is:
<WebView
x:Name="webView"
HorizontalOptions="Fill"
VerticalOptions="FillAndExpand"
Source="{Binding WebViewSource}">
<behaviors:Interaction.Behaviors>
<behaviors:BehaviorCollection>
<behaviors:EventToCommand
EventName="Navigating"
Command="{Binding NavigatingCommand}"
PassEventArgument="True" />
</behaviors:BehaviorCollection>
</behaviors:Interaction.Behaviors>
</WebView>
The code in the ViewModel, that matches the tapped url against a list of valid options before opening the link in the device's browser, is:
public Command<WebNavigatingEventArgs> NavigatingCommand
{
get
{
return navigatingCommand ?? (navigatingCommand = new Command<WebNavigatingEventArgs>(
(param) =>
{
if (param != null && -1 < Array.IndexOf(_uris, param.Url))
{
Device.OpenUri(new Uri(param.Url));
param.Cancel = true;
}
},
(param) => true
));
}
}
You can´t navigate with a WebView, you must use a custom render (hybridwebview).
Here is an explanation:
https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/hybridwebview/