Do Yun Kim
Do Yun Kim

Reputation: 27

Xamarin Forms Bindable Property with OneWay is not working

I want to bind a CustomLabel to a VM by creating a new bindable property.

In OneWay mode, the first VM data has properly changed the property of the CustomLabel. but It didn't work from second time.

Although The VM event has occur, the Bindable Property of CustomView has not fired its PropertyChanged event.

It works properly in TwoWay mode though.

I've been testing for two days and searching for the cause, but I coudn't find it well.

Anybody tell me how to do?

// HomeViewModel.cs
public class HomeViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string _customName = "-";
    
    public string CustomName
    {
        get
        {
            Debug.WriteLine("Get_CustomName");
            return _customName;
        }
        set
        {
            if (value != _customName)
            {
                Debug.WriteLine("Set_CustomName");
                _customName = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CustomName)));
            }
        }
    }
}

// MainPage.cs
public partial class MainPage : ContentPage
{
    HomeViewModel Vm = new HomeViewModel();

    public MainPage()
    {
        InitializeComponent();
        BindingContext = Vm;
    }

    void ButtonTrue_Clicked(object sender, EventArgs e)
    {
        Vm.CustomName = "True";
    }

    void ButtonFalse_Clicked(object sender, EventArgs e)
    {
        Vm.CustomName = "False";
    }
}

<!-- MainPage.xaml -->
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Ex_Binding"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="Ex_Binding.MainPage">
    <StackLayout Padding="50,0" VerticalOptions="Center">
        <StackLayout Orientation="Horizontal" HorizontalOptions="Center">
            <Label Text="Custom Result : " />
            <local:CustomLabel x:Name="lbCustom" MyText="{Binding CustomName}" HorizontalOptions="Center" />
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Button Text="TRUE" BackgroundColor="LightBlue" HorizontalOptions="FillAndExpand" Clicked="ButtonTrue_Clicked" />
            <Button Text="FALSE" BackgroundColor="LightPink" HorizontalOptions="FillAndExpand" Clicked="ButtonFalse_Clicked" />
        </StackLayout>
    </StackLayout>
</ContentPage>

// CustomLabel.cs
public class CustomLabel : Label
{
    public static readonly BindableProperty MyTextProperty = BindableProperty.Create(nameof(MyText), typeof(string), typeof(CustomLabel), null, BindingMode.OneWay, propertyChanged: OnMyTextChanged);
    private static void OnMyTextChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var thisBindable = (CustomLabel)bindable;

        if (thisBindable != null)
        {
            thisBindable.MyText = (string)newValue;
        }
    }

    public string MyText
    {
        get => (string)GetValue(MyTextProperty);
        set
        {
            SetValue(MyTextProperty, value);
            Text = value;
        }
    }
}

Upvotes: 2

Views: 598

Answers (1)

Lucas Zhang
Lucas Zhang

Reputation: 18861

Cause :

 thisBindable.MyText = (string)newValue;

Because you set the value of MyText when its value changed . So it will never been invoked next time (in TwoWay the method will been invoked multi times).

Solution:

You should set the Text in OnMyTextChanged directly .

 private static void OnMyTextChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var thisBindable = (CustomLabel)bindable;

        if (thisBindable != null)
        {
            thisBindable.Text = (string)newValue;
        }
    }

    public string MyText
    {
        get => (string)GetValue(MyTextProperty);
        set
        {
            SetValue(MyTextProperty, value);
            //Text = value;
        }
    }

Upvotes: 2

Related Questions