un_cafeinoman
un_cafeinoman

Reputation: 77

Custom control Bindable property don't work with ViewModel

I encounter an issue while I'm creating a Custom control in my Xamarin forms app.

I create a bindable property for the label label called "NameLabel" in this xaml file :

<Grid
    x:Class="Kwikwink.Controls.SelectedGatewayFrame"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:xct="http://xamarin.com/schemas/2020/toolkit">
    <StackLayout>
        <Frame >
            <StackLayout Orientation="Horizontal">
                <StackLayout>
                    ...
                    <Label
                        x:Name="NameLabel"
                        FontFamily="InterMed"
                        TextColor="{StaticResource DetailTextColor}"
                        VerticalOptions="Center" />
                </StackLayout>
                ...
            </StackLayout>
        </Frame>
    </StackLayout>
</Grid>

with this code in my xaml.cs file

[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class SelectedGatewayFrame : Grid
    {

        public static readonly BindableProperty GatewayNameProperty = BindableProperty.Create(nameof(Name),
                    typeof(string),
                    typeof(SelectedGatewayFrame),
                    defaultValue: string.Empty,
                    propertyChanged: GatewayNamePropertyChanged);

        private static void GatewayNamePropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (SelectedGatewayFrame)bindable;
            control.NameLabel.Text = newValue?.ToString();
        }

        public string Name
        {
            get => GetValue(GatewayNameProperty)?.ToString();
            set => SetValue(GatewayNameProperty, value);
        }


        public SelectedGatewayFrame()
        {
            InitializeComponent();
        }
    }

And finaly a use it like this in other views:

<controls:SelectedGatewayFrame Name="Test string" />

With the string set here it work perfectly !! My problem come when I try to bound this property to somthing in a viewmodel (public string tmp { get; set; } = "meph"; for exemple) : like so :

<controls:SelectedGatewayFrame Name="{Binding tmp}" />

or so :

<controls:SelectedGatewayFrame Name="{Binding Source={RelativeSource AncestorType={x:Type viewmodels:NewAccessTypeViewModel}}, Path=tmp}" />

Then I got this error that show :

No property, BindableProperty, or event found for "Name", or mismatching type between value and property.

I hope this is clear enough, thanks in advance for any help

Upvotes: 0

Views: 831

Answers (1)

Jessie Zhang -MSFT
Jessie Zhang -MSFT

Reputation: 13803

As a summary, I posted an answer.

For the first problem:

No property, BindableProperty, or event found for "Name", or mismatching type between value and property.

From document Create a property,we know that

Important

The naming convention for bindable properties is that the bindable property identifier must match the property name specified in the Create method, with "Property" appended to it.

So, you need to change GatewayNameProperty to NameProperty or change public string Name to public string GatewayName.

For example:

public static readonly BindableProperty GatewayNameProperty = BindableProperty.Create(nameof(GatewayName),
                    typeof(string),
                    typeof(SelectedGatewayFrame),
                    defaultValue: string.Empty,
                    propertyChanged: GatewayNamePropertyChanged);


        public string GatewayName
        {
            get => GetValue(GatewayNameProperty)?.ToString();
            set => SetValue(GatewayNameProperty, value);
        }

For the second problem:

when have a default value in the view model it perfectly work but if i try to set it later it stay null.

You need to implement interface INotifyPropertyChanged for your view model.

The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.

For exmaple:

   public class NewAccessTypeViewModel: INotifyPropertyChanged
    {   
        Gateway currentGateway;
        public Gateway CurrentGateway
        {
            get
            {
                return currentGateway;
            }
            set
            {
                if (currentGateway != value)
                {
                    currentGateway = value;
                    OnPropertyChange(nameof(CurrentGateway));
                }
            }
        }


        public NewAccessTypeViewModel() { CurrentGateway = null; }
        public NewAccessTypeViewModel(Gateway _gateway)
        { CurrentGateway = _gateway; Console.WriteLine($"Current gateway name => {CurrentGateway.Name}"); 
        
        
        }


        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

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

And you also need to implement interface INotifyPropertyChanged for your Gateway .

public class Gateway: INotifyPropertyChanged
{

    // public string Name { get; set; }

    private string _name ;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            OnPropertyChange(nameof(Name));
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChange(string propName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

Upvotes: 2

Related Questions