How to set default value to false when using converter with Label's visibility Xamarin forms

Viewed 525

I am using converter with Label's IsVisible property.

<Label IsVisible="{Binding products, Converter={StaticResource EmptyCollectionToBoolConverter}}" Text="No data found">  

If products is empty EmptyCollectionToBoolConverter returns true otherwise false. When screen is loading first time "No data found" message appears for fraction of seconds and then data is getting loaded.

I want to fix it, I need to show Label only if when products is empty. How can I do it?

4 Answers

If you are using a CollectionView you can use the EmptyView , it will display whatever you put in that XAML when the collection is empty.

Or you can implement bindablelayout that also implements the emptyViewTemplate.

Or you will have to create another binding or another converter.

Something like public bool MyBinding{get=> myList!=null | myList.Count != | isLoadingFlag } . But you will have to call the propertychanged event if you modify your collection

You can overwrite IsVisible value in the code behind.

<Label x:Name="MyLabel" IsVisible="{Binding products, Converter={StaticResource EmptyCollectionToBoolConverter}}" Text="No data found">

Code behind

// probably ctor
MyLabel.IsVisible = false;

Second option can be to use a DataTrigger

<Label Text="No data found" IsVisible="false">
    <Label.Triggers>
        <DataTrigger TargetType="Label" Binding="{Binding products, Converter={StaticResource EmptyCollectionToBoolConverter}}" Value="True">
            <Setter Property="IsVisible" Value="True" />
        </DataTrigger>
    </Label.Triggers>
</Label>

I am not sure if this could be your problem, but i would check a couple of things:

  1. You could set the binding not in XAML but in code behind, after data is loaded.
  2. You might need to set the BindingContext to products, if not yet done so.
  3. Also, maybe it is better if you set the binding path of the label to Count property of your collection.
  4. Finally, set IsVisible to false (default) in XAML. This hard code will be overriden by the binding that is set when the data is loaded.

Anyway it is, i worked out a minimal complete working sample that does the job.

It works bassically as follows: Application starts and displays nothing... then goes on to load the data. When data is loaded label appears showing the number of items in collection. Two buttons are also available in Toolbar: Add Item and Remove Item. If you remove all items, No data found label appears.

See comments in code

Page1.xaml.cs

using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace scrollviewPrompt
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Page1 : ContentPage
    {
        // products is an observable collection so that 
        // it notifies when it changes.
        public ObservableCollection<string> products { get; set; }

        public Page1()
        {
            InitializeComponent();
        }

        protected override async void OnAppearing()
        {
            // Initialize your products collection
            products = new ObservableCollection<string>();

            // Set binding context of the whole Page1 
            // to your products collection
            BindingContext = products;

            // Load data asynchronously.
            // At this point the data is already bound to 
            // our collection, so when data is loaded 
            // "No data found" label will dissapear.
            await LoadDataAsync();

            base.OnAppearing();

        }


        private async Task LoadDataAsync()
        {

            await Task.Delay(4000);

            products.Add("Toks");

            noDataLabel.SetBinding(Label.IsVisibleProperty, new Binding()
            {
                Path="Count",
                Converter = new EmptyCollectionToBoolConverter()
            });

        }


        // Add items to collection.
        private void AddClicked(object sender, EventArgs e)
        {
            products.Add("locs");
        }

        // If collection not empty, remove first item.
        private void RemoveClicked(object sender, EventArgs e)
        {
            if (products.Count>0)
                products.RemoveAt(0);
        }
    }

    public class EmptyCollectionToBoolConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var count = (int)value;

            return count==0;

        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }


    public class Not_EmptyCollectionToBoolConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var count = (int)value;

            return count > 0;

        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

}

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:local="clr-namespace:scrollviewPrompt"
             x:Class="scrollviewPrompt.Page1">
    
    <ContentPage.Resources>
        <ResourceDictionary>
            <local:EmptyCollectionToBoolConverter x:Key="EmptyCollectionToBoolConverter"/>
            <local:Not_EmptyCollectionToBoolConverter x:Key="Not_EmptyCollectionToBoolConverter"/>
        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentPage.ToolbarItems>
        <ToolbarItem Text="Add Item"
                     Clicked="AddClicked"/>
        <ToolbarItem Text="Remove Item"
                     Clicked="RemoveClicked"/>
    </ContentPage.ToolbarItems>
    
    <ContentPage.Content>
        
        
        
        <StackLayout>
            <!--No data found is bound to Count property of BindingContext (products). 
                when products change, Count changes and IsVisible is updated.-->
            <Label x:Name="noDataLabel" 
                   Text="No data found"
                   IsVisible="false"
                   VerticalOptions="CenterAndExpand" 
                   HorizontalOptions="CenterAndExpand" />
            <Label Text="{Binding Path=Count, StringFormat='{0} items'}"
                   IsVisible="{Binding Path=Count, Converter={StaticResource Not_EmptyCollectionToBoolConverter}}"
                   VerticalOptions="CenterAndExpand" 
                   HorizontalOptions="CenterAndExpand" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Consider using binding fallbacks, which allow you to specify "default" values when either the binding context is not set, or the binding target is null.

<Label IsVisible="{Binding products, Converter={StaticResource EmptyCollectionToBoolConverter}, FallbackValue='False', TargetNullValue='False'}" Text="No data found">  

You may not need both, but it will ensure that the Label is always hidden until both the binding context (ViewModel) can be resolved and the binding target (products) is not null so the converter can properly evaluate whether to show the Label.

Related