xamarin.formsbindingstacklayout

Binding StackLayout to ViewModel doens't work


I would like to have a property in my ViewModel that is linked to my StackLayout. I tried this by Binding my StackLyout to the ViewModel.

When I click on a button, this layout should be made invisible.

When I do this with the code below, my program crashes with a NulReferenceObject: Object Reference not set to an instance of an object. The StackLayout that i am talking about is the first one in the code below.

<FlexLayout>
    <StackLayout BindableLayout.ItemTemplate="{Binding CreateQuizPageQuizNameSL}"> // This StackLayout should be bind to the ViewModel
        <Label Text="Create New Quiz" />
        <StackLayout >
            <Entry Text="{Binding QuizNameInput}" Placeholder="Enter quiz name"/>
        </StackLayout>
    </StackLayout>
    <Button Command="{Binding SubmitCreateQuizCommand}" Text="Create my quiz now!"></Button>
</FlexLayout>

ViewModel

internal class CreateQuizPageViewModel
{
    // Quiz Name Input
    public String QuizNameInput { get; set; }

    // Command submit creating a quiz
    public Command SubmitCreateQuizCommand { get; set; }

    public StackLayout CreateQuizPageQuizNameSL { get; set; } = new StackLayout();

    public CreateQuizPageViewModel()
    {
        // Declaring a new command, giving the OnSubmitCreateNewQuizClick to the delegate
        SubmitCreateQuizCommand = new Command(OnSubmitCreateNewQuizClick);
    }

    // When a user submit the creation of new quiz
    public void OnSubmitCreateNewQuizClick()
    {
        CreateQuizPageQuizNameSL.IsVisible = false;
    }
}

Solution

  • Here is how to switch two layouts using IsVisible binding.

    FIRST Add Nuget Xamarin.CommunityToolkit to your Xamarin Forms project. (The one that is "MyProjectName", without ".iOS" or ".Android" at end.)

    TwoLayoutPage.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:xct="http://xamarin.com/schemas/2020/toolkit"
                     xmlns:local="clr-namespace:TestBugs"
                     x:Class="TestBugs.TwoLayoutPage">
        <ContentPage.BindingContext>
            <local:TwoLayoutViewModel/>
        </ContentPage.BindingContext>
        <ContentPage.Resources>
            <ResourceDictionary>
                <xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
            </ResourceDictionary>
        </ContentPage.Resources>
        <ContentPage.Content>
            <StackLayout>
                <StackLayout 
                        IsVisible="{Binding UseSecondLayout, Converter={StaticResource InvertedBoolConverter}}"
                        VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
                    <Label Text="First Layout" FontSize="20" />
                    <Button Text="To Second" Command="{Binding SwitchToSecondLayoutCommand}" />
                </StackLayout>
                <StackLayout IsVisible="{Binding UseSecondLayout}"
                        VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
                    <Label Text="Second Layout!" FontSize="32" />
                </StackLayout>
            </StackLayout>
        </ContentPage.Content>
    </ContentPage>
    

    TwoLayoutViewModel.cs:

    using Xamarin.Forms;
    
    namespace TestBugs
    {
        public class TwoLayoutViewModel : BindableObject
        {
            private bool _usesecondLayout = false;
            public bool UseSecondLayout {
                get => _usesecondLayout;
                set {
                    _usesecondLayout = value;
                    OnPropertyChanged();
                }
            }
    
    
            public TwoLayoutViewModel()
            {
                SwitchToSecondLayoutCommand = new Command(SwitchToSecondLayout);
            }
    
    
            public Command SwitchToSecondLayoutCommand { get; set; }
    
    
            private void SwitchToSecondLayout()
            {
                UseSecondLayout = true;
            }
        }
    }