Xamarin Forms - negate bool binding values

Viewed 24018

I am learning the xamarin forms and mvvm pattern. I am wondering, if is it possible to negate binding bool value. What I mean is:

I have, let's say Entry with isVisible Binding:

<Entry
    x:Name="TextEntry"
    IsVisible="{Binding IsVisibleEntry}"
/>

and Label which i want to hide when TextEntry is visible.

<Label x:Name="MainLabel" 
       isVisible="!{Binding IsVisibleEntry}"/> //ofc it is not working

Is it possible to do without making a new variable for MainLabel in ViewModel?

5 Answers

Option One: Converter

Define the converter:

    public class InverseBoolConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return !((bool)value);
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
            //throw new NotImplementedException();
        }
    }

Usage in XAML:

    <Label x:Name="MainLabel" 
           isVisible="{Binding IsVisibleEntry, Converter={Helpers:InverseBoolConverter}}"/>

XAML Header

    xmlns:Helpers="clr-namespace:HikePOS.Helpers"

Option Two: Trigger

    <Label x:Name="MainLabel" isVisible="{Binding IsVisibleEntry}">
        <Label.Triggers>
            <DataTrigger TargetType="Label" Binding="{Binding IsVisibleEntry}" Value="True">
                <Setter Property="IsVisible" Value="False" />
            </DataTrigger>
        </Label.Triggers>
    </Label>

You will need to create an Invert converter so your binding would look somewhat like this:

public class InverseBoolConverter : IValueConverter
{
    public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return !(bool)value;
    }
    public object ConvertBack (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return !(bool)value;
    }
}

In your XAML

<local:InverseBoolConverter x:Key="inverter"/>
<Entry
x:Name="TextEntry"
IsVisible="{Binding IsVisibleEntry, Converter={StaticResource inverter}}"
/>

Instead of writing your own converter, you could use Xamarin Community Toolkit which now has a converter invertedboolconverter that can be used out of the box. This example (taken from Microsoft's documentation) shows how it works:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             x:Class="MyLittleApp.MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>

        <Label IsVisible="{Binding MyBooleanValue, Converter={StaticResource InvertedBoolConverter}}" />

    </StackLayout>
</ContentPage>

I like the answers that are provided already, but I thought I'd add my own version that I've been using sometimes. With the MVVM architecture, the View Model's properties are all calling SetProperty() in the object setters, which raises the INotifyPropertyChanged event. What I've been doing is adding a second property to the view model, and setting its value to !value in the setter of the first property.

For example, I want a button to be button.IsEnabled = false whenever the property ViewModel.IsBusy = true is the case. This is a reverse bool example, just like you're asking about.

ViewModel.cs properties:

public bool EnableButton
{
    get { return enableButton; }
    set { SetProperty(ref enableButton, value); }
}
private bool isBusy;
bool IsBusy
{
    get { return isBusy; }
    set
    {
        SetProperty(ref isBusy, value);
        EnableButton = !value;
    }
}

XAML:

<ActivityIndicator IsRunning="{Binding IsBusy}"/>

<Button Text="Start Sync"
        Command="{Binding SyncCommand}"
        IsEnabled="{Binding EnableButton}"/>

This way, anytime in my code that I call IsBusy = true, it will simultaneously set button.IsEnabled = false, solving the reverse bool problem.

Although @praty's solution works, if you are using a Custom Control/View or XCT's Popup like myself. Before using converters in our XAML, we need to specify like this:

<ContentView.Resources>
    <converters:InverseBoolConverter x:Key="invrerseBoolConverter" />
</ContentView.Resources>

Hence the final code will be:

InverseBoolConverter.cs

// Under namespace MyProject.Converters

public class InverseBoolConverter : IValueConverter
{
    public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return !(bool)value;
    }
    public object ConvertBack (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return !(bool)value;
    }
}

XAML Code

<xct:Popup ...
           xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
           xmlns:converters="clr-namespace:MyProject.Converters"
           ...>

    <ContentView>
        <ContentView.Resources>
            <converters:InverseBoolConverter x:Key="inverseBoolConverter" />
        </ContentView.Resources>

        <StackLayout>
            <Label IsVisible="{Binding IsVisibleLabel, Converter={StaticResource inverseBoolConverter}}" />
        </StackLayout>
    </ContentView>
</xct:Popup>

NB: Although we can use Xamarin Community Toolkit's InvertedBoolConverter, I have described manual solution so that we can add custom value converters other than the built-in converters.

Related