Header and Footer in .NET MAUI - xaml

I wanted to do the same as this question, but for .NET MAUI:
Same header & footer in all WPF windows
It is same header and footer for all windows.

If you don't want to click the link #Jason provided in the comment, here is a shortened version.
PageLayout.xaml file:
<ContentPage ...>
<Grid RowDefinitions="Auto,*,Auto">
<!--Row 1 : Header-->
<Grid>
<!--header content-->
</Grid>
<!--Row 2 : Content-->
<Grid Grid.Row="1"
Name="PrimaryPageLayout">
</Grid>
<!--Row 3 : Footer-->
<Grid Grid.Row="2">
<!--footer content-->
</Grid>
</Grid>
</ContentPage>
PageLayout.xaml.cs file :
public partial class PageLayout : ContentPage
{
public IList<IView> PageLayoutContent => PrimaryPageLayout.Children;
public PageLayout()
{
InitializeComponent();
}
}
SubView.xaml file:
<PageLayout ... >
<views:PageLayout.PageLayoutContent>
<!--Main content-->
</views:PageLayout.PageLayoutContent>
</PageLayout>

I found a better way to do it using control templates.
PageLayout.xaml file:
<ContentPage ...>
<ContentPage.Resources>
<ControlTemplate x:Key="PageLayoutTemplate">
<Grid RowDefinitions="Auto,*,Auto">
<!--Header-->
<Grid>
<!--header content-->
</Grid>
<!--Content-->
<ContentPresenter Grid.Row="1"/>
<!--Footer-->
<Grid Grid.Row="2">
<!--footer content-->
</Grid>
</Grid>
</ControlTemplate>
</ContentPage.Resources>
</ContentPage>
SubView.xaml file:
<PageLayout ...
ControlTemplate="{StaticResource PageLayoutTemplate"}>
<Grid>
<!--Main content-->
</Grid>
</PageLayout>
Why this is better:
No code behind
Binding is easier thanks to template binding
A alternate template can be easily added
Reference:
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/controltemplate?view=net-maui-7.0

Related

reusable contentview .NET MAUI

Simply, I have a contentview such as;
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myapp.customstacklayout">
<StackLayout>
<StackLayout>
<StackLayout x:Name="header">
<!-- some title things here, such as a header label etc-->
</StackLayout>
<StackLayout x:Name="content">
<!--Contents should be added here when reused-->
</StackLayout>
<StackLayout x:Name="footer">
<!-- some footer things here, such as a summary label etc-->
</StackLayout>
</StackLayout>
<!--Not here-->
</StackLayout>
</ContentView>
and i want to reuse it in a ContentPage such as;
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mycontrols="clr-namespace:myapp"
x:Class="myapp.mainpage">
<StackLayout>
<mycontrols:customstacklayout>
<Button Text="TestButton"/>
<Entry Text="TestEntry"/>
<Label Text="TestLabel"/>
.... and etc..
</mycontrols:customstacklayout>
</StackLayout>
</ContentPage>
to create such a reusable item, i think, in xaml, there has to be something for contentview to point which IView item the children should be added in
Any idea or a piece of code for that?
Thanks in advance.
Ender
EDIT: i changed my contentview xaml for using ControlTemplate. Added Resources and a contentPresenter at the point where i want to show the children added. But still can not see the children
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myapp.customstacklayout">
<ContentView.Resources x:Key="template">
<ControlTemplate>
<StackLayout>
<StackLayout>
<StackLayout x:Name="header">
<!-- some title things here, such as a header label etc-->
</StackLayout>
<StackLayout x:Name="content">
<!--Contents should be added here when reused-->
<ContentPresenter/>
</StackLayout>
<StackLayout x:Name="footer">
<!-- some footer things here, such as a summary label etc-->
</StackLayout>
</StackLayout>
<!--Not here-->
</StackLayout>
</ControlTemplate>
</ContentView.Resources>
</ContentView>
Well, if you want to create a reusable ContentView .NET MAUI, you can create Control Templates with Content Presenter and then reuse it in the page you want.
You can refer to below detailed steps on how to use it.
1. Create a custom control: CustomControl.xaml that inherits from ContentView with a custom BindableProperty like below:
XAML:
<ContentView.ControlTemplate>
<ControlTemplate>
<Frame>
<VerticalStackLayout>
<Label Text="{TemplateBinding Title}"/>
<ContentPresenter/>
</VerticalStackLayout>
</Frame>
</ControlTemplate>
</ContentView.ControlTemplate>
Code-behind:
public partial class CustomControl : ContentView
{
public static readonly BindableProperty TitleProperty =
BindableProperty.Create(nameof(Title), typeof(string), typeof(CustomControl) );
public CustomControl()
{
InitializeComponent();
}
public string Title
{
get => GetValue(TitleProperty) as string;
set => SetValue(TitleProperty, value);
}
}
2. You can reuse it multiples in the MainPage.xaml like below:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:MauiAppCustomDemo.Controls"
x:Class="MauiAppCustomDemo.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<controls:CustomControl Title="Hello World">
<VerticalStackLayout>
<Label Text="Label 1"/>
<Label Text="Label 2"/>
</VerticalStackLayout>
</controls:CustomControl>
<controls:CustomControl Title="Hello again">
<HorizontalStackLayout>
<Label Text="Label 3"/>
<Label Text="Label 4"/>
</HorizontalStackLayout>
</controls:CustomControl>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
Microsoft Official reference link:https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/controltemplate?view=net-maui-7.0

Stumped on Xamarin databinding cast exception

I've followed examples and worked with XAML for WPF and this never happens so I'm totally confused about why Xamarin Forms is complaining.
Here's a simple form where I'm trying to use a ListView. The general structure is what was generated by Visual Studio but I've put in the ListView with an template for drawing two Labels:
<?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="PrivateDiary.Views.AboutPage"
xmlns:vm="clr-namespace:PrivateDiary.ViewModels"
Title="{Binding Title}">
<ContentPage.BindingContext>
<vm:AboutViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="Accent">#96d1ff</Color>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Orientation="Vertical">
<ListView x:Name="AboutListView"
ItemsSource="{Binding Items}"
SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate>
<Grid VerticalOptions="Center" HorizontalOptions="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" Grid.Column="0" />
<Label Text="{Binding Detail}" Grid.Column="1" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
The code behind:
namespace PrivateDiary.Views
{
using Xamarin.Forms;
public partial class AboutPage : ContentPage
{
public AboutPage()
{
InitializeComponent();
}
}
}
and my View Model:
namespace PrivateDiary.ViewModels
{
using System.Collections.ObjectModel;
public class AboutViewModel : BaseViewModel
{
public ObservableCollection<AboutItem> Items { get; set; } = new ObservableCollection<AboutItem>();
public AboutViewModel()
{
Title = "About";
Items.Add(new AboutItem {Name = "Version", Detail = "0.3"});
Items.Add(new AboutItem {Name = "Privacy Policy", Detail = "#privacy"});
}
}
public class AboutItem
{
public string Name { get; set; }
public string Detail { get; set; }
}
}
BaseViewModel is the stock one generated by Visual Studio 2019 (16.10.3) so I won't list it here. It implements the details for INotifyPropertyChanged, a page Title property and IsBusy property.
When I run the app I get this:
If I removed the Items.Add(...) lines there's no problems.
Any ideas why it fails to cast?
Please add element ViewCell outside of element Grid in your xaml
You can refer to the following code:
<ContentPage.BindingContext>
<listviewapp1:AboutViewModel></listviewapp1:AboutViewModel>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<ListView x:Name="AboutListView"
ItemsSource="{Binding Items}"
SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate>
<!--add ViewCell here-->
<ViewCell>
<Grid VerticalOptions="Center" HorizontalOptions="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" Grid.Column="0" />
<Label Text="{Binding Detail}" Grid.Column="1" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>

Xamarin Forms - My ContentPresenter won't show its Content

I'm still getting used to Xamarin Forms, so I have the following control called PopupFrame:
PopupFrame.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class PopupFrame : ContentView
{
public static readonly BindableProperty PopupContentProperty =
BindableProperty.Create(nameof(PopupContent), typeof(View), typeof(PopupFrame));
public View PopupContent
{
get { return (View)GetValue(PopupContentProperty); }
set { SetValue(PopupContentProperty, value); }
}
public PopupFrame()
{
InitializeComponent();
}
}
PopupFrame.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="TestApp.Core.Controls.PopupFrame">
<Frame>
<StackLayout>
<Label Text="--- TEST TITLE ---" />
<ContentPresenter Content="{TemplateBinding PopupContent}" />
</StackLayout>
</Frame>
</ContentView>
In my view:
<popCtl:PopupFrame HorizontalOptions="Center"
VerticalOptions="Center">
<popCtl:PopupFrame.PopupContent>
<ListView x:Name="ListUsers">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Label Text="{Binding Name}"
HorizontalOptions="CenterAndExpand"
VerticalOptions="Center" />
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</popCtl:PopupFrame.PopupContent>
</popCtl:PopupFrame>
So what's happening is that When the ContentView control shows, only the Label (with the text -- TEST TITLE -- is displayed, but not the ListView).
I've also tried replacing the ContentPreseter with a ContentView, but same result: my ListView doesn't not show. And I made sure that data does in fact exist in the ItemsSource of the ListView (set in code-behind).
Is my ContentView setup wrong??
TemplateBinding can only be used to bind from inside a control-template. In order for your binding to work - you can use ReferenceExtension to refer to parent control.
For ex, update your binding as following:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestApp.Core.Controls.PopupFrame"
x:Name="_parent">
<Frame>
<StackLayout>
<Label Text="--- TEST TITLE ---" />
<ContentPresenter
Content="{Binding Path=PopupContent, Source={x:Reference _parent}}" />
</StackLayout>
</Frame>
</ContentView>

How to create Footer which can be used for all the xamarin Form pages?

I am creating an app in which i want the footer which will be common for all xaml pages.I tried solution given on this link xamarin forum
but it is showing error for partial class bcoz I'm using xamarin xaml form page while inheriting the class PageToInherit.If I just use page (only .cs file) then there is no error.
public partial class TodoList : PageToInherit
{
public TodoList()
{
InitializeComponent();
this.Title = "TodoList page";
Button button = new Button();
button.Text = "BUTTON 1";
MainStackLayout.Children.Add(button);
Content = MainStackLayout;
}
}
I think you can take a look to TemplatedPage
Create a ControlTemplate
<?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>
Apply 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>
Use TemplateBinding
<ControlTemplate x:Key="MainPageTemplate">
<StackLayout>
<Label Text="{TemplateBinding BindingContext.HeadingText}" FontSize="24" />
<ContentPresenter />
</StackLayout>
</ControlTemplate>
Here the Blog
For those who are still looking for this.
Use ContentView for creating reusable xaml views.
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="xxx.FooterView">
<ContentView.Content>
<RelativeLayout VerticalOptions="End" HorizontalOptions="Fill" HeightRequest="42" RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}">
<Label HeightRequest="42" VerticalTextAlignment="Center" Text="Copyright © 2017. All rights reserved." HorizontalTextAlignment="Center" RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}" />
</RelativeLayout>
</ContentView.Content>
</ContentView>
and use it in your page like any control
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="xxx.Page1" xmlns:local="clr-namespace:xxx;assembly=xxx" >
<local:FooterView VerticalOptions="End" HeightRequest="45"/>
//other views and layouts
</ContentPage>
Hope it helps.

How to bind by element name when control is inside a UserControl?

I am trying to bind the Fill property of a Rectangle to a property in the page's DataContext by using the element name, but because the Rectangle is in a UserControl the page is not found so binding does not occur.
Simplified example:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Converters_Theme="using:ProjectName.Common.Converters"
xmlns:CustomControls="using:ProjectName.CustomControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:ProjectName.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModel="using:ProjectName.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ProjectName.Views.TestPage"
x:Name="pageName"
mc:Ignorable="d">
<Page.Resources>
<Converters_Theme:ThemeColorToSolidColorBrushConverter x:Key="ThemeColorToSolidColorBrushConverter"/>
<DataTemplate x:Key="ItemDataTemplate">
<StackPanel Orientation="Vertical">
<!-- ... -->
<Rectangle Fill="{Binding DataContext.Theme.Color, Converter={StaticResource ThemeColorToSolidColorBrushConverter}, ElementName=pageName}" MinHeight="40" MinWidth="40" />
<!-- ... -->
</StackPanel>
</DataTemplate>
</Page.Resources>
<Page.DataContext>
<ViewModel:PageViewModel />
</Page.DataContext>
<Grid>
<CustomControls:Tab>
<CustomControls:Tab.LeftContent>
<ItemsControl ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ItemDataTemplate}" />
</CustomControls:Tab.LeftContent>
<CustomControls:Tab.RightContent>
<!-- ... -->
</CustomControls:Tab.RightContent>
</CustomControls:Tab>
</Grid>
</Page>
Simple way to accomplish it is to make your ViewModel a StaticResource, e.g. in App.xaml (may work in your Page.xaml):
<App.Resources>
<ViewModel:PageViewModel x:Key="MyPageViewModel" />
</App.Resources>
And then reference to it in your Page.xaml:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Converters_Theme="using:ProjectName.Common.Converters"
xmlns:CustomControls="using:ProjectName.CustomControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:ProjectName.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModel="using:ProjectName.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ProjectName.Views.TestPage"
x:Name="pageName"
DataContext="{StaticResource MyPageViewModel}"
mc:Ignorable="d">
<Page.Resources>
<Converters_Theme:ThemeColorToSolidColorBrushConverter x:Key="ThemeColorToSolidColorBrushConverter"/>
<DataTemplate x:Key="ItemDataTemplate">
<StackPanel Orientation="Vertical">
<!-- ... -->
<Rectangle Fill="{Binding Theme.Color, Source={StaticResource MyPageViewModel}, Converter={StaticResource ThemeColorToSolidColorBrushConverter}}" MinHeight="40" MinWidth="40" />
<!-- ... -->
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<CustomControls:Tab>
<CustomControls:Tab.LeftContent>
<ItemsControl ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ItemDataTemplate}" />
</CustomControls:Tab.LeftContent>
<CustomControls:Tab.RightContent>
<!-- ... -->
</CustomControls:Tab.RightContent>
</CustomControls:Tab>
</Grid>
</Page>
To do it in much more cleaner way, I advise you to use ViewModelLocator as a static resource in app, which will properties to all ViewModels and make it StaticResource in App.xaml. It is much more cleaner way to instantiate one class in App.xaml than every ViewModel you have.