简体   繁体   中英

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

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, PropertyName Property

 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:

在此处输入图片说明

When we want to Extend any Control we use Bindable Properties . For example for Label Control we need Border and Padding . In Xamarin 4.+ we do not have it. So we can go with Bindable Properties .

I will show you this with Padding . Create a class ExLabel in Shared project.

public class ExLabel : Label
 {
      public ExLabel()
      {
      }

     public static readonly BindableProperty TitlePaddingProperty = BindableProperty.CreateAttached("Padding", typeof(Thickness), typeof(ExLabel), default(Thickness));

      public Thickness TitlePadding
      {
          get { return (int)base.GetValue(TitlePaddingProperty); }
          set { base.SetValue(TitlePaddingProperty, value); }
      }

      public static Thickness GetTitlePadding(BindableObject view)
      {
          return (Thickness)view.GetValue(TitlePaddingProperty);
      }
}

In android level project implement Custom renderer to use ExLabel class .

[assembly: ExportRenderer(typeof(ExLabel), typeof(ExLabelRenderer))]
namespace Chemicals.Droid.Renderers
{
    public class ExLabelRenderer : LabelRenderer
    {
        Context context;
        public ExLabelRenderer(Context context) : base(context)
        {
            this.context = context;
        }
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);
            if (Control != null)
            {
                ExLabel exLabel = (ExLabel)Element;

                var padding = ExLabel.GetTitlePadding(exLabel);
                Control.SetPadding((int)padding.Left, (int)padding.Top, (int)padding.Right, (int)padding.Bottom);
            }
        }
    }
}

In xaml use padding like this

<local:ExLabel IsBorder="False"
               TitlePadding="40,0,0,0"
               Text="TR01"/>

DONE.

Similarly we can add other Bindable Properties , say along with padding you want border also for Label and need border for few Labels , not for all the ExLabel . Then we can have Bindable Properties like

public static readonly BindableProperty IsBorderProperty
public bool IsBorder
{
    get { return (bool)GetValue(IsBorderProperty); }
    set { SetValue(IsBorderProperty, value); }
}

public static readonly BindableProperty ShowBorderProperty
public Color ShowBorder
{
    get { return (bool)GetValue(ShowBorderProperty); }
    set { SetValue(ShowBorderProperty, value); }
}

From xaml we can pass IsBorder true false and ShowBorder Color . In custom renderer file we can check if IsBorder is true then set Color for border otherwise skip it.

if(ExLabel.GetBorder(exLabel))
Control.SetBackground(set resource here);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM