Search code examples
c#xamarinxamarin.formsmvvmbindableproperty

How to bind data to the bindableproperty in xamarin forms


I have two pages that use the same form so I created a content view of form and apply bindable properties like this:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ListContact.Extension.EditForm">
    <StackLayout Padding="20">
        <Label Text="Edit Contact Forms"
                   HorizontalOptions="Center"
                   FontSize="20"
                   TextColor="Blue"
                   VerticalOptions="Start"/>
        <Label Text="Name"/>
        <Entry x:Name="txtName" Text="{Binding NameText}" Placeholder="Name"/>
        <Label Text="Phone number"/>
        <Entry x:Name="txtPhoneNumber" Text="{Binding PhoneNumberText}" Placeholder="Phone number" Keyboard="Telephone"/>
        <Label Text="Email"/>
        <Entry x:Name="txtEmail" Text="{Binding EmailText}" Placeholder="Email" Keyboard="Email"/>
        <Label Text="Description"/>
        <Editor x:Name="txtDescription" Text="{Binding DescriptionText}" Placeholder="Description"
                    HeightRequest="70"/>
    </StackLayout>
</ContentView>

This is the code behind:

    using ListContact.ViewModel;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace ListContact.Extension
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class EditForm : ContentView
        {
            private static readonly BindableProperty NameProperty = BindableProperty.Create("NameText", typeof(object), typeof(EditForm));
    
            public string NameText { get => (string)GetValue(NameProperty); set => SetValue(NameProperty, value); }
    
            private static readonly BindableProperty PhoneProperty = BindableProperty.Create("PhoneNumberText", typeof(string), typeof(EditForm));
    
            public string PhoneNumberText { get => (string)GetValue(PhoneProperty); set => SetValue(PhoneProperty, value); }
    
            private static readonly BindableProperty EmailProperty = BindableProperty.Create("EmailText", typeof(string), typeof(EditForm));
    
            public string EmailText { get => (string)GetValue(EmailProperty); set => SetValue(EmailProperty, value); }
    
            private static readonly BindableProperty DescriptionProperty = BindableProperty.Create("DescriptionText", typeof(string), typeof(EditForm));
    
            public string DescriptionText { get => (string)GetValue(DescriptionProperty); set => SetValue(DescriptionProperty, value); }
    
            public EditForm()
            {
                InitializeComponent();
                BindingContext = this;
            }
        }
    }

And in the view I call back this form that I created before and bind data to the bindable property like below:

This is the xaml file:

<ContentPage xmlns:local="clr-namespace:ListContact.Extension"
             xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ListContact.View.ListContactAddingForm">
    
    <local:EditForm NameText="{Binding Name, Mode=TwoWay}" PhoneNumberText="egwebwev" EmailText="ưewevefqwf" DescriptionText="ewebe"/>
    
    <ContentPage.Content>
        <Button Text="Save"
                HorizontalOptions="Center"
                TextColor="White"
                Command="{Binding AddContactCommand}"
                BackgroundColor="#0A7CFF"/>
    </ContentPage.Content>
</ContentPage>

And this is the code behind:

namespace ListContact.View
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class ListContactAddingForm : ContentPage
        {
            private SQLiteAsyncConnection connection;
    
            public ListContactAddingForm()
            {
                connection = new SQLiteAsyncConnection(BaseConnection.DatabasePath);
                ViewModel = new ContactViewModel(new PageService());
                InitializeComponent();
            }
    
            private ContactViewModel ViewModel
            {
                get => BindingContext as ContactViewModel;
    
                set => BindingContext = value;
            }
        }
    }
`

This is my view model:

namespace ListContact.ViewModel
{
    public class ContactViewModel : BaseViewModel
    {
        public int Id { get; set; }

        private string name;

        public string Name
        {
            get => name;
            set
            {
                SetValue(ref name, value);
            }
        }

        private string description;

        public string Description
        {
            get => description;
            set
            {
                SetValue(ref description, value);
            }
        }

        private string phoneNumber;

        public string PhoneNumber
        {
            get => phoneNumber;
            set
            {
                SetValue(ref phoneNumber, value);
            }
        }

        private string email;
        public string Email
        {
            get => email;
            set
            {
                SetValue(ref email, value);
            }
        }

        public ICommand AddContactCommand { get; private set; }
        private IPageService pageService;
        public object Alert { get; private set; }

        public ContactViewModel(IPageService pageService)
        {
            this.pageService = pageService;
            AddContactCommand = new Command(async () => await AddContacts());
        }

        private async Task AddContacts()
        {
            var newContact = new Contact()
            {
                Name = Name,
                PhoneNumber = PhoneNumber,
                Email = Email,
                Description = Description
            };

            var result = await connection.InsertAsync(newContact);

            if (result == 1)
                await App.Current.MainPage.DisplayAlert("Successfully", "", "OK");

            await pageService.PopAsycn();
        }
    }
}

But when I run this code I got the error:

No property, bindable property, or event found for "NameText", or mismatching type between value and property

My code was okay before I separated the form into another content view and call it back from the view and got this problem

So my questions are that: Is the way that I create form and bindable property correct? Could I bind data to the bindable property in the form? How to do it if it could?. And how to fix the above error?

I use MVVM to build this code

Btw, sorry for my bad English


Solution

  • From Xamarin.Forms Bindable Properties, a bindable property can be created by declaring a public static readonly property of type BindableProperty.

     public static readonly BindableProperty NameTextProperty = BindableProperty.Create
                 ("NameText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:NamePropertychanged);
    

    Then you need take care of BindableProperty Name, for example, PropertyNameProperty

     public static readonly BindableProperty NameTextProperty = BindableProperty.Create
                 ("NameText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:NamePropertychanged);
      
        public string NameText 
        { 
            get => (string)GetValue(NameTextProperty);
            set => SetValue(NameTextProperty, value);
        }
    

    Finally, you don't need to use Binding in EditForm, just using propertyChanged event to notify property value has changed.

    private static void NamePropertychanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (EditForm)bindable;
            control.txtName.Text = (string)newValue;
            
        }
    

    Do one sample that you can take a look:

    EditForm xaml.

    <ContentView.Content>
        <StackLayout Padding="20">
            <Label
                FontSize="20"
                HorizontalOptions="Center"
                Text="Edit Contact Forms"
                TextColor="Blue"
                VerticalOptions="Start" />
            <Label Text="Name" />
            <Entry x:Name="txtName" Placeholder="Name" />
            <Label Text="Phone number" />
            <Entry
                x:Name="txtPhoneNumber"
                Keyboard="Telephone"
                Placeholder="Phone number" />
            <Label Text="Email" />
            <Entry
                x:Name="txtEmail"
                Keyboard="Email"
                Placeholder="Email" />
            <Label Text="Description" />
            <Editor
                x:Name="txtDescription"
                HeightRequest="70"
                Placeholder="Description" />
        </StackLayout>
    </ContentView.Content>
    
     public partial class EditForm : ContentView
    {
        public static readonly BindableProperty NameTextProperty = BindableProperty.Create
                 ("NameText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:NamePropertychanged);
      
        public string NameText 
        { 
            get => (string)GetValue(NameTextProperty);
            set => SetValue(NameTextProperty, value);
        }
    
        public static readonly BindableProperty PhoneNumberTextProperty = BindableProperty.Create
            ("PhoneNumberText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:PhoneNumberchanged);
    
        public string PhoneNumberText 
        { 
            get => (string)GetValue(PhoneNumberTextProperty); 
            set => SetValue(PhoneNumberTextProperty, value); 
        }
    
        public static readonly BindableProperty EmailTextProperty = BindableProperty.Create
            ("EmailText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:Emailpropertychanged);     
    
        public string EmailText 
        { 
            get => (string)GetValue(EmailTextProperty); 
            set => SetValue(EmailTextProperty, value); 
        }
    
        public static readonly BindableProperty DescriptionTextProperty = BindableProperty.Create
            ("DescriptionText", typeof(string), typeof(EditForm), "", BindingMode.OneWay, propertyChanged: Descriptionchanged);
    
    
        public string DescriptionText 
        { 
            get => (string)GetValue(DescriptionTextProperty); 
            set => SetValue(DescriptionTextProperty, value); 
        }
        private static void NamePropertychanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (EditForm)bindable;
            control.txtName.Text = (string)newValue;
            
        }
        private static void PhoneNumberchanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (EditForm)bindable;
            control.txtPhoneNumber.Text = (string)newValue;
        }
        private static void Emailpropertychanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (EditForm)bindable;
            control.txtEmail.Text = (string)newValue;
        }
        private static void Descriptionchanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (EditForm)bindable;
            control.txtDescription.Text = (string)newValue;
        }
    
        public EditForm()
        {
            InitializeComponent();
        }
    }
    
    <StackLayout>
            <local:EditForm
                DescriptionText="{Binding option}"
                EmailText="{Binding Email}"
                NameText="{Binding Name}"
                PhoneNumberText="{Binding Phone}" />
        </StackLayout>
    
     public partial class Page5 : ContentPage
    {
       public Contact contact1 { get; set; }
        public Page5()
        {
            InitializeComponent();
            contact1 = new Contact() { Name = "cherry", Phone = "1234567", Email = "xxxxxxxx", option = "this is test" };
            this.BindingContext = contact1 ;
    
        }    
    }
    
    public class Contact:ViewModelBase
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
        private string _phone;
        public string Phone
        {
            get { return _phone; }
            set
            {
                _phone = value;
                RaisePropertyChanged("Phone");
            }
        }
        private string _email;
        public string Email
        {
            get { return _email; }
            set
            {
                _email = value;
                RaisePropertyChanged("Email");
            }
        }
        private string _option;
        public string option
        {
            get { return _option; }
            set
            {
                _option = value;
                RaisePropertyChanged("option");
            }
        }
    }
    

    The ViewModelBase implementing INotifyPropertyChanged

    public class ViewModelBase : INotifyPropertyChanged
    {
       
        public event PropertyChangedEventHandler PropertyChanged;
    
        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    The screenshot:

    enter image description here