maui

Class Property value is not retained when Property is set using CommandParameter


I have a small example below in .net maui 9, where I am setting a property in my view model via a CommandParameter value that is being set when a button is clicked in the view.

This property is then used as a BindingContext for the PlaceHolderText property for an Entry view. So the text that should be displayed in Entry when Set PropertyOne Button is clicked is Text from Property.

Code below for View:

<?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"
             x:Class="MauiBindingExample.MainPage"
             xmlns:local="clr-namespace:MauiBindingExample.ViewModels">

    <ScrollView>
        <VerticalStackLayout 
            Padding="30,0"
            Spacing="25">
            <Image
                Source="dotnet_bot.png"
                HeightRequest="185"
                Aspect="AspectFit"
                SemanticProperties.Description="dot net bot in a hovercraft number nine" />
            <Button
                BindingContext="{local:ExampleViewModel}"
                x:DataType="local:ExampleViewModel"
                Text="Set PropertyOne" 
                Command="{Binding SetPropertyOneCommand}"
                CommandParameter="Text from Property"/>
            <Entry
                BindingContext="{local:ExampleViewModel}"
                x:DataType="local:ExampleViewModel"
                PlaceholderColor="AliceBlue"
                Placeholder="{Binding EntryPlaceholderText}"/>
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

Code below for View Model:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;

namespace MauiBindingExample.ViewModels
{
    public class ExampleViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private string entryPlaceholderText;

        public string EntryPlaceholderText
        {
            get => entryPlaceholderText;
            set
            {
                entryPlaceholderText = value;
                OnPropertyChanged();
            }
        }
        
        public ICommand SetPropertyOneCommand { get; set; }

        public ExampleViewModel()
        {
            SetPropertyOneCommand = new Command<string>(
                (string arg) =>
                {
                    EntryPlaceholderText = arg;
                });
        }
        public void OnPropertyChanged([CallerMemberName] string propertyName="")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

When I set a debugger, I can confirm that the property EntryPlaceholderText is set correctly when Set PropertyOne button is clicked, but then after the Command execution is complete the property EntryPlaceholderText is reset so there is no Entry placeholder text displayed.

What could be the issue?

Help is much appreciated.


Solution

  • You're currently setting two different instances of ExampleViewModel as BindingContext for the two components (Button and Entry).

    You should set BindingContext only at the page level and let it be inherited by all the controls you put in that page. So:

    <Button
        Text="Set PropertyOne" 
        Command="{Binding SetPropertyOneCommand}"
        CommandParameter="Text from Property"/>
    <Entry
        PlaceholderColor="AliceBlue"
        Placeholder="{Binding EntryPlaceholderText}"/>
    
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="MauiBindingExample.MainPage"
                 BindingContext="{local:ExampleViewModel}"
                 x:DataType="local:ExampleViewModel"
                 xmlns:local="clr-namespace:MauiBindingExample.ViewModels">
    ...
    </ContentPage>
    

    Just a note outside the scope of the question. Consider using the MVVM Community Toolkit. You'll get rid of a lot of boilerplate code.