Georgi Koemdzhiev
Georgi Koemdzhiev

Reputation: 11931

My binded Label is not changed [INotifyPropertyChanged]

I recently started learning Xamarin and I stumbled across the following problem. I have a single label in my XAML file which is bound to a ViewModel property. I am using the ICommand interface to bind a tap gesture to a method in my ViewModel which is supposed to update the label's text. However, it is not updating the "Please touch me once!". I am just wondering what I am doing wrong here?

MainPage xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:App1"
         x:Class="App1.MainPage">

<Label Text="{Binding MessageContent, Mode=TwoWay}"
       VerticalOptions="Center"
       HorizontalOptions="Center">
    <Label.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding OnLabelTouchedCmd}" />
    </Label.GestureRecognizers>

</Label>

</ContentPage>

Code-behind:

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();

        BindingContext = new MainPageViewModel();
    }
}

ViewModel:

class MainPageViewModel : INotifyPropertyChanged
{
    private string _messageContent;

    public MainPageViewModel()
    {
        MessageContent = "Please touch me once!";

        OnLabelTouchedCmd = new Command(() => { MessageContent = "Hey, stop toutching me!"; });
    }

    public ICommand OnLabelTouchedCmd { get; private set; }

    public string MessageContent
    {
        get => _messageContent;
        set
        {
            _messageContent = value;
            OnPropertyChanged(value);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;


    protected virtual void OnPropertyChanged(string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Upvotes: 1

Views: 853

Answers (3)

Joagwa
Joagwa

Reputation: 309

Also look at the ViewModelBase located here, have all your ViewModels inherit from it. You can call just OnPropertyChanged, in either of the two ways below. The first of which will just take the name of the calling member, in this case your public property.

OnPropertyChanged();

OnPropertyChanged("MyProperty");

Edit- this is in extension to Brandon's correct answer

Upvotes: 0

Brandon Minnick
Brandon Minnick

Reputation: 15420

Explaination

The current code isn't working because it is passing the value of the property into OnPropertyChanged.

Instead, we need to pass the name of the property as a string into OnPropertyChanged.

Answer

We can take advantage of the CallerMemberName attribute to make the code more concise and to avoid hard-coding strings when calling OnPropertyChanged.

Adding [CallerMemberName] to the parameter of OnPropertyChanged allows you to call OnPropertyChanged() from the setter of the property, and the property name is automatically passed into the argument.

Updated Method

protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Updated ViewModel

class MainPageViewModel : INotifyPropertyChanged
{
    private string _messageContent;

    ...

    public string MessageContent
    {
        get => _messageContent;
        set
        {
            _messageContent = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Upvotes: 1

Timo Salom&#228;ki
Timo Salom&#228;ki

Reputation: 7189

You're calling OnPropertyChanged with a wrong argument as seen here:

protected virtual Void OnPropertyChanged ([System.Runtime.CompilerServices.CallerMemberName] String propertyName)

It expects the name of the property instead of the value you're passing now. Try this instead:

public string MessageContent
{
    get => _messageContent;
    set
    {
        _messageContent = value;
         OnPropertyChanged("MessageContent");
    }
}

Upvotes: 3

Related Questions