Is there a way in Xamarin to avoid command repetition? - xamarin

I have 10 Xamarin Pages/Views, there's one command/code block that's repeated in the code-behind for all of them. And it does the same thing on all the pages, just pop the stack until you reach the root.
I have tried extending the base class (ContentPage) to make a custom class but got an error since it was a partial class?
Error Message :
"Partial declarations of 'TestPage' must not specify different base classes"
Is there a way to avoid rewriting the same Method in all 10 classes? Also, I'm using Xamarin with Prism

If you want to make a base page with xaml, you could use a base page.
BasePage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="App2.BasePage"
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"
BackgroundColor="AliceBlue"
mc:Ignorable="d" />
MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<app2:BasePage
x:Class="App2.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:app2="clr-namespace:App2"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<app2:BasePage.Content>
<StackLayout>
<Label Text="Wellcome to MainPage!!!" />
</StackLayout>
</app2:BasePage.Content>
</app2:BasePage>
MainPage.xaml.cs:
public partial class MainPage : BasePage
{
public MainPage()
{
InitializeComponent();
}
}

Related

How can programmatically change from Light Mode to Dark Mode in Xamarin.Forms Android and / or programmatically change the color of the scroll bar?

I want to allow my user to select the AppTheme as he wants. Following are the options I want to give :-
Automatic (As per the user selection in Settings).
Light Mode
Dark Mode
I found a solution to use the below code:-
if(theme == App.Theme.Light)
{
Delegate.SetLocalNightMode(AppCompatDelegate.ModeNightNo);
} else
{
Delegate.SetLocalNightMode(AppCompatDelegate.ModeNightYes);
}
But this code recreates the entire activity and on the click on the button, I am again taken to the LoginPage of my App.
Can anyone suggest me a way to programmatically change from Light Mode to Dark Mode in Xamarin.Forms Android?
Also does anyone know another way that I could programmatically change the color of the scroll bar.
Have two style files Light and Dark XAML and ThemeHelper class which switches the themes at run time
Refer this https://github.com/jamesmontemagno/Hanselman.Forms/tree/vnext/src/Hanselman/Styles
Update
As mentioned this sample project has great resources for theme switching.
Light Theme
<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestApp.LightTheme">
<Color
x:Key="TextColor">#ababab</Color>
<Color
x:Key="GenericBackground">#e3e3e3</Color>
<Color
x:Key="AppBackground">#FFFFFF</Color>
</ResourceDictionary>
Dark theme
<?xml version="1.0" encoding="UTF-8"?>
<ResourceDictionary
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestApp.DarkTheme">
<Color
x:Key="TextColor">#e3e3e3</Color>
<Color
x:Key="GenericBackground">#ababab</Color>
<Color
x:Key="AppBackground">#000000</Color>
</ResourceDictionary>
ThemeHelper
public class ThemeHelper
{
public static Theme CurrentTheme = Theme.Light;
public static void ChangeTheme(Theme theme, bool forceTheme = false)
{
// don't change to the same theme
if (theme == CurrentTheme && !forceTheme)
return;
//// clear all the resources
var applicationResourceDictionary = Application.Current.Resources;
ResourceDictionary newTheme;
if (theme == Theme.Default)
{
theme = AppInfo.RequestedTheme == AppTheme.Dark ? Theme.Dark : Theme.Light;
}
switch (theme)
{
case Theme.Light:
newTheme = new LightTheme();
break;
case Theme.Dark:
newTheme = new DarkTheme();
break;
case Theme.Default:
default:
newTheme = new LightTheme();
break;
}
ManuallyCopyThemes(newTheme, applicationResourceDictionary);
CurrentTheme = theme;
var background = (Color)App.Current.Resources["AppBackground"];
}
static void ManuallyCopyThemes(ResourceDictionary fromResource, ResourceDictionary toResource)
{
foreach (var item in fromResource.Keys)
{
toResource[item] = fromResource[item];
}
}
}
Theme enum
public enum Theme
{
Default,
Light,
Dark
}
Before starting the app initialize theme , you can retrieve it from Application settings or from DB like SQLite or API too
public App()
{
InitializeComponent();
ThemeHelper.ChangeTheme(Theme.Default);
MainPage = new NavigationPage(new MainPage());
}
Make sure you use DynamicResource in this approach
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
BackgroundColor="{DynamicResource AppBackground}"
x:Class="TestApp.MainPage">
<ContentPage.ToolbarItems>
<ToolbarItem
Text="Default"
Clicked="ToolbarItem_Clicked" />
<ToolbarItem
Text="Light"
Clicked="ToolbarItem_Clicked_1" />
<ToolbarItem
Text="Dark"
Clicked="ToolbarItem_Clicked_2" />
</ContentPage.ToolbarItems>
<StackLayout>
<Label
Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
TextColor="{DynamicResource TextColor}"
BackgroundColor="{DynamicResource GenericBackground}" />
<Label
x:Name="label"
TextColor="{DynamicResource TextColor}"
BackgroundColor="{DynamicResource GenericBackground}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
Screenshot
You may still need some changes in Android with respect to different API levels.
Other reference
Theming
Dark mode detection
Android Dark Theme
You can use DynamicResources for implement dynamic theme functionality without redirection. You can follow these documents.

Add Shell Navigation templates for various pages Xamarin.Forms Shell

I have multi-page app, which has preLogin section and postLogin section.
Which is the best approach to make shell templates for these two sections?
preLogin pages doesnt have backbutton in navigationBar. I tried in xaml page NavigationPage.HasNavigationBar="false" (not AppShell.xaml, either in my page xaml) as well as in code behind NavigationPage.SetHasNavigationBar(this, false);
example xaml page which regardless on my NavigationPage.HasBackButton="false"
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:customViewEntry="clr-namespace:App.CustomViews.Entries"
xmlns:header="clr-namespace:App.CustomViews.CustomHeaders"
mc:Ignorable="d"
x:Class="App.Views.Login.EnterEmailAndPasswordPage"
NavigationPage.HasBackButton="false"
BackgroundImageSource="Login_Screen_Green.png">
<ContentPage.Content>
<StackLayout>
<header:HeaderWithTopRightIcon ImageUri="greenIcon.png"/>
<StackLayout VerticalOptions="EndAndExpand" Padding="0,50">
<customViewEntry:EvEntry
Placeholder="Enter your email"/>
<customViewEntry:EvEntry
Placeholder="Enter your password"
IsPassword="True"/>
<Button
Text="LOGIN"
Margin="25,0"
CornerRadius="7"
BackgroundColor="{StaticResource DarkGrayColor}"
TextColor="White"
Command="{Binding OpenEmailVerificationPageCommand}"/>
<Label
Padding="0,25,0,0"
Text="Don't have an account?"
FontSize="Small"
Style="{StaticResource WhiteSmallLabelStyle}"
HorizontalOptions="Center"
VerticalOptions="Start"/>
<Label
Text="SIGN UP"
TextDecorations="Underline"
FontSize="Small"
Style="{StaticResource WhiteSmallBoldLabelStyle}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding SignUpClickCommand}"/>
</Label.GestureRecognizers>
</Label>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
my AppShell looks like:
<?xml version="1.0" encoding="UTF-8"?>
<Shell 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"
mc:Ignorable="d"
xmlns:views="clr-namespace:App.Views"
xmlns:loginViews="clr-namespace:App.Views.Login"
x:Class="App.AppShell"
FlyoutBehavior="Disabled">
<!--Styles and Resources-->
<Shell.Resources>
<ResourceDictionary>
...removed due simplicity...
</ResourceDictionary>
</Shell.Resources>
<ShellContent ContentTemplate="{DataTemplate loginViews:EnterEmailPage}"/>
</Shell>
So, my questions are
1. How to dynamically hide back button on some pages and on some not? For navigation Im using await Shell.Current.GoToAsync(route)
2. Also I need a redirection when user leaves application, and again enter it based on elapsed time, to redirect to preLogin page (login) or to let him directly to postLogin (full access to the app) so I need maybe two AppShell classes, and to call it different shells OnResume() ?
How to dynamically hide back button on some pages and on some not? For navigation Im using await Shell.Current.GoToAsync(route)
About this question , here is a Workaround for you .
Shell application has Back button behavior to ovveride Back Button dynamically , even can Hide/Show it .
If using Shell.Current.GoToAsync(route) to navigate to destination page , you can follow the below code to use in your destination page.
// Button click to show Back Button
private void Button_Clicked_Show(object sender, EventArgs e)
{
Shell.SetBackButtonBehavior(this, new BackButtonBehavior
{
IsEnabled = true
});
}
// Button click to hide Back Button
private void Button_Clicked_Hide(object sender, EventArgs e)
{
Shell.SetBackButtonBehavior(this, new BackButtonBehavior
{
IconOverride = "null.png", // null.png not exists in project ,just want
the program to show a blank Back button
IsEnabled = false
});
}
I will show the effect with a Gif :
Also I need a redirection when user leaves application, and again enter it based on elapsed time, to redirect to preLogin page (login) or to let him directly to postLogin (full access to the app) so I need maybe two AppShell classes, and to call it different shells OnResume() ?
About this question , here is a Suggestion for you , you can conside that whether fit your needs .
You can use Navigation.PushModalAsync(new LoginPage()) to redirect to preLogin page based on elapsed time . Otherwise using Shell.Current.GoToAsync(route) to Navigate . You can deal with them in OnResume Method .
For example as follow :
protected override void OnResume()
{
if(time > xxx)
{
Navigation.PushModalAsync(new LoginPage());
}
else
{
Shell.Current.GoToAsync(route);
}
}
Here using PushModalAsyncto Login Page that is a Model Page . After using Navigation.PopModalAsync() can dismiss LoginPage and redirect to other Page with Shell.Current.GoToAsync(route).

Can't create ListViewLinearLayout

I have the following in my 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:telerikInput="clr-namespace:Telerik.XamarinForms.Input;assembly=Telerik.XamarinForms.Input"
xmlns:telerikDataControls="clr-namespace:Telerik.XamarinForms.DataControls;assembly=Telerik.XamarinForms.DataControls"
xmlns:listView="clr-namespace:Telerik.XamarinForms.DataControls.ListView;assembly=Telerik.XamarinForms.DataControls"
xmlns:this="clr-namespace:Namespace123"
x:Class="MyClass"
BackgroundColor="#F7F7F7">
And...
<telerikDataControls:RadListView.LayoutDefinition>
<listView:ListViewLinearLayout>
<listView:ListViewLinearLayout.ItemLength>
<OnPlatform x:TypeArguments="x:Double"
iOS="48"
Android="48"
WinPhone="48" />
</listView:ListViewLinearLayout.ItemLength>
<listView:ListViewLinearLayout.VerticalItemSpacing>
<OnPlatform x:TypeArguments="x:Double"
iOS="10"
Android="10"
WinPhone="2" />
</listView:ListViewLinearLayout.VerticalItemSpacing>
</listView:ListViewLinearLayout>
</telerikDataControls:RadListView.LayoutDefinition>
...
And I get the following exception...
Exception is: XamlParseException - Position 50:12. Type
listView:ListViewLinearLayout not found in xmlns
clr-namespace:Telerik.XamarinForms.DataControls.ListView;assembly=Telerik.XamarinForms.DataControls
I've cleaned the solution and deleted obj/bin folders. Why can't it find the type?
This runs fine in the code behind..
var list = new RadListView();
list.LayoutDefinition = new ListViewLinearLayout();
Today when I ran the app for the first time, it worked.

Hamburger menu not showing on iOS . Prism issue, Xamarin issue or my issue

I am trying to implement a masterDetail app with hamburger menu.
It works in android but crashes on iOS with "FileNotFoundException" and shows default instead the icon.
I have added an icon to iOS. Resources project but still does not show and both the master and navigation page have the icon.
Am I doing something wrong?
Any workaround?
thanks a lot
app.xaml
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
try
{
InitializeComponent();
NavigationService.NavigateAsync("MainMasterDetail/MyNavigationPage/MainPage",animated:false);
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<MainMasterDetail,MainMasterDetailViewModel>();
Container.RegisterTypeForNavigation<MyNavigationPage,MyNavigationPageViewModel>();
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<ViewA,ViewAViewModel>();
Container.RegisterTypeForNavigation<ViewB,ViewBViewModel>();
}
}
MainMasterDetails.xaml
<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="HelloBurgerMenu.Views.MainMasterDetail"
Title="MainMasterDetail" Icon="humburger.png">
<MasterDetailPage.Master>
<ContentPage Title="Default">
<StackLayout>
<Button Text="MainPage" Command="{Binding NavigateCommand}" CommandParameter="MyNavigationPage/MainPage" />
<Button Text="ViewA" Command="{Binding NavigateCommand}" CommandParameter="MyNavigationPage/ViewA" />
<Button Text="ViewB" Command="{Binding NavigateCommand}" CommandParameter="MyNavigationPage/ViewB" />
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
</MasterDetailPage>
MyNavigationPage
<?xml version="1.0" encoding="utf-8" ?>
<NavigationPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="HelloBurgerMenu.Views.MyNavigationPage"
Icon="humburger.png">
</NavigationPage>
Try specify the Icon in MasterPage not MasterDetail
public MasterPage()
{
InitializeComponent();
if (Device.RuntimePlatform == Device.iOS)
{
Icon = "menu.png";
}
}
You can't have content in your NavigationPage. Delete all the XAML in your NavigationPage.
Look at this sample: https://github.com/xamarin/xamarin-forms-samples/tree/master/Navigation/MasterDetailPage

Xamarin.Forms - Is there a mechanism to "include" a partial view?

Does Xamarin.Forms have a concept of includes?
I'm creating an app that has a shared header across all pages. Is there a way to create the header once and include it on all pages? Better yet, is there a way to create a template or a reusable layout that you can put all content inside for each page? It would be a similar concept to .NET MVC's _Layout file.
What you need is the ControlTemplate introduced in 2.1.0.
Create a control template in your ResourceDictionary in Application.Resources.
<?xml version="1.0" encoding="utf-8" ?>
<Application
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Mobile.App">
<Application.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="MainPageTemplate">
<StackLayout>
<Label Text="Header Content" FontSize="24" />
<ContentPresenter />
</StackLayout>
</ControlTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
Then in your ContentPage, assigned the ControlTemplate
<?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="Mobile.MainPage"
ControlTemplate="{StaticResource MainPageTemplate}">
<Label Text="Main Page Content" FontSize="18" />
</ContentPage>
Then you end up with
Referenced from: http://www.xamarinhelp.com/xamarin-forms-page-templates/
Yes. You can use User Controls for this. You can Use XAML or code only. I'll explain the XAML way.
Just add a new XAML Page and change the root type from ContentPage to StackLayout. The root type can be every other layout or control. You have to decide what fits best.
MyControl.xaml
<?xml version="1.0" encoding="utf-8" ?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App6.MyControl">
<Label Text="{Binding Name}" />
<Label Text="{Binding Age}" />
<Label Text="{Binding CatAmount}" />
</StackLayout>
We bind the properties Name, Age, CatAmount to three different labels. We assume, that the BindingContext of this control is an object of type PersonData (see below).
In your Code behind, you have to change the type as well.
MyControl.xaml.cs
public partial class MyControl : StackLayout
{
public MyControl()
{
InitializeComponent();
}
}
In your page, you have to add a new namespace (e.g. local that points to you assembly, e.g. App6 or MyApp.Whatever). Then you can use it via local:MyControl. In our example control, we bind the BindingContext to Person, wich is a Property of our Page's BindingContext, that is (in our case) the page itself. If your control is in a sub namespace, you have to change the namespace part accordingly.
Page2.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:local="clr-namespace:App6;assembly=App6"
x:Class="App6.Page2">
<local:MyControl BindingContext="{Binding Person}"></local:MyControl>
</ContentPage>
Page2.xaml.cs
public class PersonData
{
public string Name { get; set; }
public int Age { get; set; }
public int CatAmount { get; set; }
}
public partial class Page2 : ContentPage
{
public PersonData Person { get; set; }
public Page2()
{
Person = new PersonData {Age = 28, Name = "Sven", CatAmount = 2};
InitializeComponent();
BindingContext = this;
}
}
And in your mentioned Scenario, you can simply inherit from ContentPage and add your common elements and use you inherited Page as base class of your pages.
TemplatedPage - Xamarin.Forms 2.1
With Xamarin.Forms 2.1 they introduced TemplatedPage. You find the example here: http://xfcomplete.net/general/2016/01/20/control-templates/ . The LoginView example with the ContentPresenter fits your scenario exactly.

Resources