xamlxamarin.formsbindableproperty

Xamarin Froms Entry Custom Control does not work


I'm trying to make a custom control to use it in multiple places. the thing is it works fine with the label but when it comes to entry it won't even run and it gives me

No property, BindableProperty, or event found for "EntryText"

here is my custom control Xaml:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

    <ContentView.Content>
        <StackLayout>
            <Label x:Name="MyLabel" />
            <Entry x:Name="MyEntry" />
        </StackLayout>
    </ContentView.Content>
</ContentView>

and its code behind

   public partial class MyCustomControl : ContentView
    {
        public static readonly BindableProperty LabelTextProperty = BindableProperty.Create(
            nameof(LabelText),
            typeof(string),
            typeof(MyCustomControl),
            propertyChanged: LabelTextPropertyChanged);

        private static void LabelTextPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (MyCustomControl)bindable;
            control.MyLabel.Text = newValue?.ToString();
            Debug.WriteLine("LabelTextPropertyChanged: " + control.MyEntry);
            Debug.WriteLine("LabelTextPropertyChanged: new value" + newValue);
        }

        public string LabelText 
        {
            get => base.GetValue(LabelTextProperty)?.ToString();
            set
            {
                base.SetValue(LabelTextProperty, value);
            }
        }


        public static  BindableProperty EntryBindableProperty = BindableProperty.Create(
            nameof(EntryText),
            typeof(string),
            typeof(MyCustomControl),
            propertyChanged:EntryBindablePropertyChanged
            );

        private static void EntryBindablePropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (MyCustomControl)bindable;
            Debug.WriteLine("EntryBindablePropertyChanged: " + control.MyEntry);
            Debug.WriteLine("EntryBindablePropertyChanged: new value" + newValue);
        }

        public string EntryText
        {
            get => base.GetValue(EntryBindableProperty)?.ToString();
            set
            {
                base.SetValue(EntryBindableProperty, value);
            }
        }
        public MyCustomControl()
        {
            InitializeComponent();
        }
    }

and its Usage

<StackLayout>
        <local:MyCustomControl 
            LabelText="{Binding BindingLabel}" 
            EntryText="{Binding BindingEntry}"/>
    </StackLayout>

NOTE: I tried to remove the

 </ContentView.Content>

from my xaml because i've seen some example like that, and also i've tried to set binding in the code behind constructor

 public MyCustomControl()
        {
            InitializeComponent();
            MyEntry.SetBinding(Entry.TextProperty, new Binding(nameof(EntryText) , source: this));
        }

but neither did work for the entry . so how can i resolve this and does it make any additional setting if i want to bind the value to a view model. thanks in advance.

Update: @Jessie Zhang -MSFT thanks for your help I really appreciate it, however I discovered a bug in MyCustomControl code => EntryTextProperty is that i have to declare the defaultBindingMode to be able to get the data passed to a ViewModel Property. so i changed the BindableProperty code to:

public static  BindableProperty EntryTextProperty = BindableProperty.Create(
            nameof(EntryText),
            typeof(string),
            typeof(MyCustomControl),
            defaultBindingMode: BindingMode.OneWayToSource,
            propertyChanged:EntryBindablePropertyChanged
            );

Solution

  • You didn't bind the Entry.Text and EntryText property in MyCustomControl.xaml:

    Please refer the following code:

    <?xml version="1.0" encoding="UTF-8"?>
    <ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="CardViewDemo.Controls.MyCustomControl"
                 
                 x:Name="this"
                 >
      <ContentView.Content>
            <StackLayout BindingContext="{x:Reference this}">
                <Label x:Name="MyLabel" Text="{Binding LabelText}" />
                <Entry x:Name="MyEntry" Text="{ Binding EntryText}"  />
            </StackLayout>
      </ContentView.Content>
    </ContentView>
    

    And I used the following code to did a test, it works properly.

            <local:MyCustomControl 
            LabelText="test1" 
            EntryText="test2"/>
    

    Update:

    From document Create a property,we know that:

    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 can change your code EntryBindableProperty to EntryTextProperty, just as follows:

        public static readonly  BindableProperty EntryTextProperty = BindableProperty.Create(nameof(EntryText), typeof(string),typeof(MyCustomControl),string.Empty, BindingMode.TwoWay, null, EntryBindablePropertyChanged);
    
        public string EntryText
        {
            get => base.GetValue(EntryTextProperty)?.ToString();
            set
            {
                base.SetValue(EntryTextProperty, value);
            }
    
        }
    

    And the xaml code is:

    <?xml version="1.0" encoding="UTF-8"?>
    <ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="CardViewDemo.Controls.MyCustomControl"
                 
                 x:Name="template"
                 >
      <ContentView.Content>
            <StackLayout >
                <Label x:Name="MyLabel"  Text="{Binding  Source={x:Reference template},Path=LabelText}"/>       
    
                <Entry x:Name="MyEntry"  Text="{Binding EntryText, Source={x:Reference template}}"  >
                </Entry>
           
            </StackLayout>
      </ContentView.Content>
    </ContentView>