Show both Tab bar and Toolbar in Xamarin forms - xaml

I'm a beginner in mobile app development and I'm developing an android app using Xamarin forms for a project.
it has 3 tabbed pages.
each page needs some actions to be done when a user clicks on something.
since there are few actions in one tab to be clicked and actions are different for each tab, I want to show both toolbar and tab bar at the same time just like "Whatsapp" shows in there interface.
I have 3 pages named Farm, Treatments, and Autopsy.
all 3 pages are showing as tabbed pages in the tabbedPageContainer XAML.
this is the tabbedPageContainer XAML Code
<TabbedPage 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"
x:Class="TabbedForms.TabbedPages.TabbedPageContainer"
xmlns:pages="clr-namespace:TabbedForms.TabbedPages"
>
<pages:page_Farm />
<pages:Page_Treatments/>
<pages:Page_Autopsy/>
</TabbedPage>
and this TabbedPageContainer is my Main page.
What I have so far is something like this
but what I want is something like this
how can I achieve this in Xamarin forms XAML?
This is the App.XAML
using System;
using TabbedForms.TabbedPages;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TabbedForms
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new TabbedPageContainer();
//Background color
//MainPage.SetValue(NavigationPage.BarBackgroundColorProperty, Color.Black);
//Title color
// MainPage.SetValue(NavigationPage.BarTextColorProperty, Color.White);
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
}
I'm using VS 2019 Enterprise edition and testing on android 8.1

App.Xaml:
public App()
{
InitializeComponent();
MainPage = new NavigationPage( new TabbedPage1());
}
Tab page:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage 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:page="clr-namespace:XamarinControls"
x:Class="XamarinControls.TabbedPage1">
<!--Pages can be added as references or inline-->
<page:Page4 Title="Tab 1" />
<ContentPage Title="Tab 2" />
<ContentPage Title="Tab 3" />
</TabbedPage>
Content Page:
<?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"
NavigationPage.HasNavigationBar="True"
x:Class="XamarinControls.Page4">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Priority="0"/>
<ToolbarItem Text="Noti" Priority="1"/>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
It worked for me.

You could put the child ContentPage of the Tabbed in a NavigationPage like
<TabbedPage.Children>
<NavigationPage Title="xxx" IconImageSource="xxx">
<x:Arguments>
<views:ItemsPage1 />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="xxx" IconImageSource="xxx">
<x:Arguments>
<views:ItemsPage2 />
</x:Arguments>
</NavigationPage>
</TabbedPage.Children>

Related

Xamarin forms Add button in TabbedPage

I have a question. I created the following TabbedPage:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage 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:views="clr-namespace:MyApp.Views"
mc:Ignorable="d"
x:Class="MyApp.Views.MainPage"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:TabbedPage.ToolbarPlacement="Bottom"
BarBackgroundColor="White"
BarTextColor="Black"
android:TabbedPage.BarItemColor="#B2B2B2"
android:TabbedPage.BarSelectedItemColor="#56D7A5"
android:TabbedPage.IsSwipePagingEnabled="False">
<TabbedPage.Children>
<NavigationPage Title="page1" IconImageSource="navbar_page1">
<x:Arguments>
<views:page1 NavigationPage.HasNavigationBar="False" />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="page2" IconImageSource="navbar_page2">
<x:Arguments>
<views:page2 NavigationPage.HasNavigationBar="False" />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="page3" IconImageSource="navbar_page3">
<x:Arguments>
<views:page3 NavigationPage.HasNavigationBar="False" />
</x:Arguments>
</NavigationPage>
</TabbedPage>
Now on every page I have added this custom FabMenu like this:
<c:FloatingMenu Margin="0, 0, 10, 10" BGColor="#56D7A5" OpenIcon="openFab_icon" CloseIcon="closeFab_icon"
AbsoluteLayout.LayoutBounds=".95,.95" AbsoluteLayout.LayoutFlags="PositionProportional">
<c:FloatingButton x:Name="btnAddHomework" BGColor="#59E1FF" IconSrc="add_homework_icon" OnClickCommand="{Binding btnAddHomeworkCommand}" />
<c:FloatingButton x:Name="btnAddDeadline" BGColor="#0FF1A0" IconSrc="add_deadline_icon"/>
<c:FloatingButton x:Name="btnAddTest" BGColor="#5988FF" IconSrc="add_test_icon"/>
</c:FloatingMenu>
The problem is that every page has his own FabMenu, so you see it dissapear and reappear on every page, so my question is: Is there some kind of root view that overlays all the tabs in the TabbedPage?
Please let me know how I do that!
Disclaimer
I came up with a way to create the effect wanted using only pure Xamarin.Forms. Read along and pay attention to the tricky parts of the solution.
Abstract
This solution is achieved implementing AbsoluteLayout, CarouselView, IndicatorView and DataTemplateSelector. Xamarin.Forms 4.8 is supposed in what follows. If a lower version is used, please take into account that features like CarouselView or IndicatorView could be in Preview status.
DataTemplateSelector, CarouselView and IndicatorView are used to simulate a TabbedPage, and AbsoluteLayout is used to provide the Overlay.
So, now with the solution:
Create your Views
Here you create a view for each of the pages you want. In this example i want my application to consist of two pages, so i create two views (code behind remains untouched):
View1.xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="overlayTest.View1"
BackgroundColor="Black">
<ContentView.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms 1!"
TextColor="White"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentView.Content>
</ContentView>
View2.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="overlayTest.View2">
<ContentView.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms 2!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentView.Content>
</ContentView>
Create a DataTemplateSelector
This will be used by the CarouselView in order to select one view or the other depending on the current Position.
using System;
using Xamarin.Forms;
namespace overlayTest
{
class MyTemplateSelector : DataTemplateSelector
{
readonly DataTemplate view1, view2;
public MyTemplateSelector()
{
view1 = new DataTemplate(typeof(View1));
view2 = new DataTemplate(typeof(View2));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
String s = item.ToString();
if(s == "1")
{
return view1;
}
return view2;
}
}
}
Create your Main Page
Page1.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:t="clr-namespace:overlayTest"
x:Class="overlayTest.Page1">
<ContentPage.Resources>
<ResourceDictionary>
<t:MyTemplateSelector x:Key="templateSelector"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<AbsoluteLayout>
<StackLayout AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
Padding="0"
Spacing="0">
<CarouselView ItemTemplate="{StaticResource templateSelector}"
IndicatorView="indicatorView">
<CarouselView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>1</x:String>
<x:String>2</x:String>
</x:Array>
</CarouselView.ItemsSource>
</CarouselView>
<IndicatorView x:Name="indicatorView">
<IndicatorView.IndicatorTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand">
<Frame Margin="10">
<Label/>
</Frame>
</StackLayout>
</DataTemplate>
</IndicatorView.IndicatorTemplate>
</IndicatorView>
</StackLayout>
<ContentView
IsVisible="True" VerticalOptions="Start"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
BackgroundColor="Transparent">
<Frame CornerRadius="10"
Margin="20"
VerticalOptions="StartAndExpand"
HorizontalOptions="CenterAndExpand" InputTransparent="False">
<StackLayout Padding="0">
<Label
FontSize="Medium"
TextColor="Black"/>
<StackLayout Orientation="Horizontal"
HorizontalOptions="CenterAndExpand">
<Label Text="I am floating here"/>
<Switch IsToggled="True" />
</StackLayout>
<Button Text="Save"
BackgroundColor="Accent"/>
</StackLayout>
</Frame>
</ContentView>
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
And in the code behind we set the name of the tabs. Here please put attention in the fact that i am supposing an element tree of a StackLayout -> Frame -> Label. If you change the IndicatorTemplate, you will have to also modify this part of the code!
Page1.xaml.cs
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace overlayTest
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
indicatorView.PropertyChanged += (s, a) =>
{
if (a.PropertyName == IndicatorView.HeightProperty.PropertyName)
{
var indicators = indicatorView.IndicatorLayout.Children.ToList();
int counter = 0;
foreach(var indicator in indicators)
{
var indicatorBaseStack = (StackLayout)indicator;
var indicatorFrame = (Frame)indicatorBaseStack.Children[0];
var indicatorFrameLabel = (Label)indicatorFrame.Content;
indicatorFrameLabel.Text = counter == 0 ? "View1" : "View2";
counter++;
}
}
};
}
}
}
Finally set that Page to the MainPage property of App:
public App()
{
InitializeComponent();
MainPage = new Page1();
}
The final result looks like this:
As a workaround, you could set ToolbarItem of each ContentPage (or you can define a base ContentPage).
<ContentPage.ToolbarItems>
<ToolbarItem Text="Example Item"
IconImageSource="xxx.png"
Order="Secondary"
Clicked="{Binding xx}"
Priority="0" />
<ToolbarItem Text="Example Item"
IconImageSource="xxx.png"
Order="Secondary"
Priority="1" />
<ToolbarItem Text="Example Item"
IconImageSource="xxx.png"
Order="Secondary"
Priority="2" />
</ContentPage.ToolbarItems>
I recommend creating a BaseContentPage that includes a static FloatingButton. This allows every page to inherit from BaseContentPage and use the same FloatingButton.
Code
BaseContentPage
abstract class BaseContentPage : ContentPage
{
protected static Button Button { get; } = new Button { Text = $"This button was created at {DateTimeOffset.UtcNow}" }.Invoke(button => button.Clicked += HandleButtonClicked);
static async void HandleButtonClicked(object sender, EventArgs e) =>
await Application.Current.MainPage.DisplayAlert("Button Clicked", "This is the same button on both pages", "OK");
}
Example LabelPage
class LabelPage : BaseButtonPage
{
public LabelPage()
{
Title = "LabelPage";
Content = new StackLayout
{
Children =
{
new Label { Text = "Label Page" }.TextCenter().Center(),
Button
}
}
}
}
Example ButtonPage
class ButtonPage : BaseButtonPage
{
public ButtonPage()
{
Title = "ButtonPage";
Content = Button;
}
}
Example App
public class App : Application
{
public App()
{
Device.SetFlags(new[] { "Markup_Experimental" });
MainPage = new TabbedPage
{
Children =
{
new ButtonPage(),
new LabelPage()
}
};
}
}
Sample App
Here is the sample app used to create the attached GIF:
https://github.com/brminnick/TabbedPageButton/

How to nest Tab views in Xamarin.Forms

I am trying to create this layout in Xamarin XAML but I cannot figure out how to combine TabView within a TabView. I want 3 main tabs in the bottom and for each page 1-2 subtabs. On each subtab I will need to have a ScrollView(I think thats the right element to use) with list items, which makes it even more complex. Like this picture:
Any idea or guidance of how to achieve this?
I am trying to create this layout in Xamarin XAML but I cannot figure out how to combine TabView within a TabView.
If you want to do that, you could nest a TabView in Tabbed page.
TabView:https://github.com/chaosifier/TabView
Create three tab pages in views folder.
Tabbed Page:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage
x:Class="TabbedPageDemo.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Views="clr-namespace:TabbedPageDemo.Views"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
BarBackgroundColor="Blue"
BarTextColor="White"
mc:Ignorable="d">
<Views:Tab1_Page Title="TAB1" />
<Views:Tab2_Page Title="TAB2" />
<Views:Tab3_Page Title="TAB3" />
</TabbedPage>
And then use TabView in you tab1 page.
Tab1_Page:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="TabbedPageDemo.Views.Tab1_Page"
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:local="clr-namespace:Xam.Plugin.TabView;assembly=Xam.Plugin.TabView"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<ContentPage.Content>
<local:TabViewControl>
<local:TabViewControl.ItemSource>
<local:TabItem HeaderText="SUBTAB1">
<StackLayout VerticalOptions="Start" Padding="10">
<Button
BackgroundColor="White"
Text="List Item"
TextColor="Black"/>
</StackLayout>
</local:TabItem>
<local:TabItem HeaderText="SUBTAB2">
<Image Source="pink.jpg" />
</local:TabItem>
</local:TabViewControl.ItemSource>
</local:TabViewControl>
</ContentPage.Content>
</ContentPage>
Please note, if you want to make the tabs in tabbed page in the bottom. Add the code below in your MainPage.
On<Android>().SetToolbarPlacement(ToolbarPlacement.Bottom);
You could download the code sample from GitHub in TabbedPage_NestedTabView/TabbedPageDemo
https://github.com/WendyZang/Test.git

How to show ToolbarItem only for iOS in Xamarin.Forms?

I'm devolpin an app with Xamarin.Forms for iOS and Android and I have a page that I want show ToolbarItem only for iOS app. In Android, I want use a button inside of page. How can I do that? I made it adding a ToolbarItem with blank text in Android, but I believe that is not the right way to do that.
Here is my page xaml code:
<ContentPage 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="VFood.Views.Garcons">
<ContentPage.Content>
<StackLayout>
<Label Text="Garçons"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
<ContentPage.ToolbarItems>
<OnPlatform x:TypeArguments="ToolbarItem">
<OnPlatform.iOS>
<ToolbarItem Text="Adicionar"/>
</OnPlatform.iOS>
<OnPlatform.Android>
<ToolbarItem Text=""/>
</OnPlatform.Android>
</OnPlatform>
</ContentPage.ToolbarItems>
don't specify anything for the platform you DON'T want to have it
<ContentPage.ToolbarItems>
<OnPlatform x:TypeArguments="ToolbarItem">
<OnPlatform.iOS>
<ToolbarItem Text="Adicionar"/>
</OnPlatform.iOS>
</OnPlatform>
</ContentPage.ToolbarItems>
I too got null reference exception doing that in XAML, what worked for me however is doing that in code-behind:
public partial class Garcons
{
public Garcons()
{
InitializeComponent();
if (Device.RuntimePlatform == Device.iOS)
{
var myToolbarItem = new ToolbarItem()
{
Icon = "myIcon.png",
Order = ToolbarItemOrder.Primary,
Priority = 0,
Text = "MyToolbarItem"
};
myToolbarItem.SetBinding(MenuItem.CommandProperty, "MyToolbarItemCommand");
ToolbarItems.Insert(0, myToolbarItem);
}
}
}
The above code is equivalent of this XAML version:
<ToolbarItem Icon="myIcon.png" Order="Primary" Priority="0"
Text="MyToolbarItem" Command="{Binding MyToolbarItemCommand}"/>

Xamarin Forms - How to display content from one xaml inside another

I'm used to Android development, and am having some difficulty accomplishing what I would think is a simple task.
I have a MasterDetailPage (called ContainerView.xaml).
The Master is my navigation bar (called NavbarView.xaml).
The Details is supposed to be a page with a fixed title bar, and a "view" I can can swap per user choices.
The Details page is called MainView.xaml.
The Title I'd like to display at the top and is called TitleBarView.xaml.
Then I have a number of content pages such as Page1View.xaml.
in my ContainerView.xaml:
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.ContainerView"
IsGestureEnabled="True"
MasterBehavior="Popover"
Title="MasterDetail Page">
<MasterDetailPage.Master>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
</MasterDetailPage.Detail>
</MasterDetailPage>
in my NavbarView.xaml - this is the Master
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.NavBarView"
Title="Nav Bar">
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Item1}"/>
<Button Text="Options" Command="{Binding Option1Command}"/>
</StackLayout >
</ContentPage.Content>
</ContentPage>
in my MainView.xaml - this is the details
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.MainView"
Title="Main View">
<ContentPage.Content>
// what to put here to show the TitleBarView.xaml?
// what to put here to show my content xaml pages?
</ContentPage.Content>
</ContentPage>
in my TitleBarView.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.TitleBarView">
<ContentView.Content>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Item1}"/>
<Button Text="Options" Command="{Binding OptionsCommand}"/>
</StackLayout>
</ContentView.Content>
</ContentView>
and a generic content view, of course there will be many others I want to switch between
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Page1View">
<ContentView.Content>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Info}"/>
<Button Text="Log In" Command="{Binding GoToPage2Command}"/>
</StackLayout >
</ContentView.Content>
</ContentView>
I am using the MVVM model and have this code, but can't seem to get just the basics working.
The Master page displays fine.
If the Details page is just a simple page, it works, but I can't figure out how to insert the TitleBar and swap out the "Content".
ContainerView containerPage = new ContainerView();
ContainerViewModel containerVM = new ContainerViewModel();
containerPage.BindingContext = containerVM;
NavBarView navigationBar = new NavBarView();
navigationBar.Title = "Navigation Bar"; // required, otherwise I get an exception.
NavBarViewModel navigationBarVM = new NavBarViewModel();
navigationBar.BindingContext = navigationBarVM;
MainView mainView = new MainView();
mainView.Title = "MainView";
MainViewModel mainViewVM = new MainViewModel();
mainView.BindingContext = mainViewVM;
TitleBarView titleBar = new TitleBarView();
TitleBarViewModel titleBarVM = new TitleBarViewModel();
titleBar.BindingContext = titleBarVM;
Page1View page1 = new Page1View();
Page1ViewModel page1VM = new Page1ViewModel();
page1.BindingContext = page1VM;
mainView.Content = new StackLayout()
{
Orientation = StackOrientation.Vertical,
Children =
{
new Label { Text = "I'm Content!" },
new Label { Text = "I'm Content!" },
//titleBar.Content,
//loginView.Content
}
};
containerPage.MasterBehavior = MasterBehavior.Popover;
containerPage.Master = navigationBar;
containerPage.Detail = new NavigationPage(mainView);
I'm sure I'm missing a fundamental concept. Any help would be appreciated
Xaml can instantiate any control, defined in code or in Xaml, so in case of TitleBarView, you can instantiate it in any Xaml by
<xmlnsprefix:TitleBarView />
The problem is to set the right xmlnsprefix. Every xaml file define a few xmlns and you can add your own, like this:
xmlns:local="clr-namespace:MyApp"
and it'll mean that the Xml namespace 'local' will reference the clr namespace 'MyApp' in the current assembly.
So you MainView becomes:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyApp"
x:Class="MyApp.MainView"
Title="Main View">
<ContentPage.Content>
<local:TitleBarView />
</ContentPage.Content>
</ContentPage>

Circular referenced IValueConverter in Application.Resources

I'm getting the error :
StaticResource not found for key maxLength
the setup is as follows:
Converter setup in app.xaml, which also contains a datatemplate
<Application.Resources>
<ResourceDictionary>
<ext:MaxLengthStringConverter x:Key="maxLength"/>
....
<DataTemplate x:Key="HotelViewModel">
<tripSegmentPartViews:HotelView
Padding="0"
HeightRequest="60"
BorderWidth="1"
BorderColor="{ext:ColourResource Divider}"
BordersToDraw="{x:Static controls:Borders.Top}"
BackgroundColor="Transparent"/>
</DataTemplate>
....
view in the HotelView.xaml which is in the datatemplate, uses the converter
....
<Label Text="{Binding HotelName, Converter={StaticResource maxLength}, ConverterParameter=10}"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="1,0.2,0.62,0.5"
VerticalOptions="End"
HorizontalOptions="Start"
FontSize="20"
/>
....
If I move the converter to HotelView.xaml resource dictionary it works
If I change the reference to a DynamicResource it is not used
Obviously with something as basic as max length (which shortens the string and adds '...' if its over the required length) I want to be able to use it through out the application, and not have to reference it in multiple resource dictionaries.
Is this a bug?
----------------- edit ------------------
OK I have reproduced this errror with a minimum app consisting of:
App1.xaml
<?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="App1.App"
xmlns:ext="clr-namespace:App1.Extensions;assembly=App1"
xmlns:local="clr-namespace:App1;assembly=App1">
<Application.Resources>
<ResourceDictionary>
<ext:MyConverter x:Key="conv"></ext:MyConverter>
<DataTemplate x:Key="dt">
<local:View1></local:View1>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
Page1.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="App1.Page1"
xmlns:local="clr-namespace:App1;assembly=App1">
<StackLayout>
<Label Text="Page1" VerticalOptions="Center" HorizontalOptions="Center" TextColor="White" />
<ListView ItemTemplate="{StaticResource dt}" ItemsSource="List">
</ListView>
</StackLayout>
</ContentPage>
View1.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App1.View1"
xmlns:ext="App1.Extensions">
<Label Text="{Binding MainText, Converter={StaticResource conv}" VerticalOptions="Center" HorizontalOptions="Center" TextColor="White"/>
</ContentView>
App1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Forms;
namespace App1
{
public partial class App : Application
{
public App()
{
InitializeComponent();
// The root page of your application
Page1 p = new Page1();
p.BindingContext = new {
MainText = "test",
List = new List<string>() { "test"}
};
var navContainer = new NavigationPage(p);
navContainer.BarBackgroundColor = Color.Red;
navContainer.BarTextColor = Color.White;
MainPage = navContainer;
}
}
}
The error is thrown at runtime using the VS emulator
From what you mention you want to be able to define a IValueConverter the once, and use it from any Xamarin.Forms ContentPage, without the need to keep specifying the converter in the local XAML page.
This can be achieved by doing the following:-
In your PCL you normally have App.cs.
You will need to delete this, and add a new Forms Xaml Page called App.cs.
This will generate both the App.xaml and related App.cs files.
In this question, (How can I databind an image?), there is a converter called MyByteToImageSourceConverter.
I will illustrate using this:-
App.xaml
<?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="SampleApp.App"
xmlns:local="clr-namespace:{namespace reference goes here to the converter}"
>
<Application.Resources>
<ResourceDictionary>
<local:MyByteToImageSourceConverter x:Key="kyByteToImageSourceConverter"/>
</ResourceDictionary>
</Application.Resources>
</Application>
So in the above we have defined our converter with a key, that we will then be able to reference from all other Xamarin.Forms ContentPage's.
App.cs
namespace SampleApp
{
public partial class App
: Xamarin.Forms.Application
{
public App()
{
InitializeComponent();
//
this.MainPage = new ByteToImageExample2();
}
}
}
In the code-behind we need to change the default inheritance from ContentPage and specify Xamarin.Forms.Application.
We also specify our launch page, via the this.MainPage = ...
ByteToImageExample2.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="SampleApp.ByteToImageExample2"
>
<StackLayout>
<Image Source="{Binding MyImageAsBytes, Converter={StaticResource kyByteToImageSourceConverter}}"/>
</StackLayout>
</ContentPage>
In our ContentPage we can see above we are referencing the converter that we specified in App.xaml via the StaticResource kyByteToImageSourceConverter.
As that was definied in App.xaml, we can re-use this in all our pages without the need to specify the location of the converter locally.
For completeness the code-behind is:-
ByteToImageExample2.cs:-
public partial class ByteToImageExample2 : ContentPage
{
public ByteToImageExample2()
{
InitializeComponent();
//
byte[] bytImage = { your image as a byte collection }
//
this.BindingContext = new MyImageViewModel()
{
MyImageAsBytes = bytImage
};
}
}
Update 1:-
You can have the following in your App.xaml:-
<DataTemplate x:Key="kyByteToImage3ExampleDataTemplate2">
<ViewCell>
<local2:MyCustomView1/>
</ViewCell>
</DataTemplate>
with local2:MyCustomView1 referencing your custom view, which for this example is defined as:-
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SampleApp.MyCustomView1">
<Image Source="{Binding MyImage, Converter={StaticResource kyByteToImageSourceConverter}}" Aspect="AspectFit" />
</ContentView>
This custom view still uses a Converter as specified from the App.xaml and does still render, without the need to specify the Converter locally within the ContentView class.