0

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

un_cafeinoman
  • 67
  • 1
  • 7
  • 1
    Change `public string Name` to `public string GatewayName` and or change `GatewayNameProperty` to `NameProperty`. Then adjust your xaml code to target that property; there are a few things to change. [Have a look at](https://stackoverflow.com/questions/17629945/how-to-create-a-bindable-property-in-wpf) this post, it will be helpful. – Trevor Mar 09 '22 at 16:54
  • 1
    It perfectly work thanks I doesn't know there is a specific naming convention for that. Thanks a lot for your quick answer @Trevor – un_cafeinoman Mar 09 '22 at 18:06
  • You're welcome, glad to be of help. – Trevor Mar 09 '22 at 18:11
  • But know i have another issue and mybe you'll have a clue on what happen know when have a default value in the view model it perfectly work but if i try to set it later it stay null. My viewmodel is like so : ` public Gateway CurrentGateway { get; set; } public NewAccessTypeViewModel() { CurrentGateway = null; } public NewAccessTypeViewModel(Gateway _gateway) { CurrentGateway = _gateway; Console.WriteLine($"Current gateway name => {CurrentGateway.Name}"); } ` @Trevor – un_cafeinoman Mar 09 '22 at 18:11
  • And I try to use the Gateway.Name property that exit. The console.writline work fine – un_cafeinoman Mar 09 '22 at 18:13
  • If you have other question, please open a new question. – vernou Mar 09 '22 at 22:29
  • The ViewModel need to implement INotifyPropertyChanged to notify the vue when a property's value is changed. – vernou Mar 09 '22 at 22:30

1 Answers1

2

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));
        }
    }
}
Jessie Zhang -MSFT
  • 9,830
  • 1
  • 7
  • 19