Is there a way to dynamically show/hide the header of a ListView based on a condition at runtime.
You should use FooterTemplate and Footer properties. It works as ItemTemplate and ItemSource:
<ListView Footer="{Binding IsFooterVisible}">
<ListView.FooterTemplate>
<DataTemplate>
<!-- Footer content. Always visible -->
</DataTemplate>
<ListView.FooterTemplate>
</ListView>
And bind to Footer property something nullable (object for example). Or you can use converter to convert: true -> new object() and false -> null
Also it is possible to create a subclass of ListView. My example (IsLoading property is what you searching for):
Xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ListView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Panor.Views.LoadableListView"
x:Name="element"
>
<ListView.FooterTemplate>
<DataTemplate>
<ContentView>
<ActivityIndicator IsRunning="true"
Margin="0, 5"
Color="{StaticResource mainColor}"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"
>
<ActivityIndicator.Scale>
<OnPlatform x:TypeArguments="x:Double" iOS="2" Android="1" />
</ActivityIndicator.Scale>
</ActivityIndicator>
</ContentView>
</DataTemplate>
</ListView.FooterTemplate>
</ListView>
Code-behind:
public partial class LoadableListView : ListView
{
public LoadableListView()
{
InitializeComponent();
this.ItemAppearing += OnItemAppearing;
}
public static readonly BindableProperty IsLoadingProperty = BindableProperty.Create(
nameof(IsLoading),
typeof(bool),
typeof(LoadableListView),
false,
propertyChanged: (bindable, oldValue, newValue) =>
{
var element = (LoadableListView)bindable;
element.Footer = (bool)newValue ? new object() : null;
}
);
public bool IsLoading
{
set => SetValue(IsLoadingProperty, value);
get => (bool)GetValue(IsLoadingProperty);
}
public static readonly BindableProperty ScrolledDownCommandProperty = BindableProperty.Create(
nameof(ScrolledDownCommand),
typeof(ICommand),
typeof(LoadableListView)
);
public ICommand ScrolledDownCommand
{
set => SetValue(ScrolledDownCommandProperty, value);
get => (ICommand)GetValue(ScrolledDownCommandProperty);
}
void OnItemAppearing(object sender, ItemVisibilityEventArgs e)
{
if (ItemsSource == null) return;
if (ScrolledDownCommand == null) return;
object last = null;
if (ItemsSource is IList)
{
var length = (ItemsSource as IList).Count;
last = (ItemsSource as IList)[length - 1];
}
else
{
foreach (var item in ItemsSource)
last = item;
}
if (e.Item == last && ScrolledDownCommand.CanExecute(null))
ScrolledDownCommand.Execute(null);
}
Consuming:
<views:LoadableListView ItemsSource="{Binding ItemSource}"
RowHeight="120"
SeparatorColor="#c7c8c9"
IsLoading="{Binding IsMoreLoading}"
ScrolledDownCommand="{Binding ScrolledDownCommand}"
IsPullToRefreshEnabled="true"
IsRefreshing="{Binding IsRefreshing}"
RefreshCommand="{Binding RefreshCommand}"
>
<views:LoadableListView.ItemTemplate>
<DataTemplate>
<cells:MagazinesListCell Name="{Binding Name}"
Publisher="{Binding Publisher}"
Price="{Binding Price}"
Image="{Binding Converter={StaticResource UriToImageSourceConvertor}, Path=Image}"
Commands="{Binding Commands}"
/>
</DataTemplate>
</views:LoadableListView.ItemTemplate>
</views:LoadableListView>
You can wrap your footer template in a ViewCell
, then set the ViewCell
's Height
to 0 when you don't want it to show. If you do this, make sure to set the ListView.HasUnevenRows
property to True
:
<ListView x:Name="ListViewChallenges" Header="{Binding .}" HasUnevenRows="True">
<ListView.FooterTemplate>
<DataTemplate>
<ViewCell Height={Binding FooterHeight}>
<Label Text="No Elements found." IsVisible="{Binding FooterIsVisible}" />
</ViewCell>
</DataTemplate>
</ListView.FooterTemplate>
<!-- ... -->
</ListView>
If you want to hide the footer because the listview is empty just set
listview.footer = null
I tried all variants with:
1) Header
property binding new object()
or null
2) Two stacklayouts with not converter (https://stackoverflow.com/a/53197844/7429947)
3) DataTrigger
and Trigger
on change IsVisible
set HeightRequest
This is doesn't help me, and then I replaced MinimumHeightRequest
on 'HeightRequest' and second variant help me for iOS and Android!
Xamarin.Forms version: 4.6.0.726
This works for me without temlpate(template will not work if you need refer elements from the header in the parent .cs code). So have to containers with 0 and not 0 height and switch their visibility.
<ListView.Header>
<StackLayout Style="{StaticResource DefaultStackLayout}">
<StackLayout x:Name="ZeroHeaderContainer"
HeightRequest="0"
IsVisible="{Binding SomeBooleanField}"/>
<StackLayout x:Name="NormalHeaderContainer"
Style="{StaticResource DefaultStackLayout}"
HorizontalOptions="Fill"
IsVisible="{Binding SomeBooleanField,Converter={StaticResource InverseBooleanConverter}}">
</StackLayout>
</StackLayout>
Here is the solution:
<ListView.FooterTemplate>
<DataTemplate>
<!--VIEW-->
<ActivityIndicator IsRunning="true" IsVisible="{Binding IsLoadingOnDemand}" HorizontalOptions="CenterAndExpand" Margin="15">
<ActivityIndicator.Triggers>
<Trigger TargetType="ActivityIndicator" Property="IsVisible" Value="False">
<Setter Property="HeightRequest" Value="0" />
</Trigger>
</ActivityIndicator.Triggers>
</ActivityIndicator>
</DataTemplate>
</ListView.FooterTemplate>