Как привязать данные к свойству bindableproperty в формах xamarin

#c# #xamarin #xamarin.forms #mvvm #bindableproperty

Вопрос:

У меня есть две страницы, которые используют одну и ту же форму, поэтому я создал представление содержимого формы и применил привязываемые свойства, подобные этому:

 <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>
 

Это код, лежащий в основе:

     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;
            }
        }
    }
 

И в представлении я вызываю эту форму, созданную ранее, и привязываю данные к свойству привязки, как показано ниже:

Это файл xaml:

 <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();
        }
    }
}
 

Но когда я запускаю этот код, я получаю ошибку:

Не найдено свойства, связываемого свойства или события для «NameText» или несоответствующего типа между значением и свойством

Мой код был в порядке, прежде чем я разделил форму на другое представление содержимого и вызвал его из представления, и возникла эта проблема

Итак, мои вопросы таковы: Правильно ли я создаю форму и связываемое свойство? Могу ли я привязать данные к свойству привязки в форме? Как это сделать, если бы это было возможно?. И как исправить вышеуказанную ошибку?

Я использую MVVM для создания этого кода

Кстати, извините за мой плохой английский

Комментарии:

1. Как насчет BindableProperty.Create("NameText", typeof(object), typeof(EditForm), BindingMode.TwoWay); этого ?

2. Спасибо за ваш комментарий, но ваше решение в данном случае не работает. Не могли бы вы предложить мне другое решение? Спасибо 🙂

Ответ №1:

Из Xamarin.Формирует привязываемые свойства, привязываемое свойство может быть создано путем объявления общедоступного статического свойства только для чтения типа BindableProperty.

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

Затем вам нужно позаботиться об имени BindableProperty, например, свойстве propertyName

  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);
    }
 

Наконец, вам не нужно использовать привязку в форме редактирования, просто используйте событие PropertyChanged для уведомления об изменении значения свойства.

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

Сделайте один образец, на который вы можете взглянуть:

Отредактируйте 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");
        }
    }
}
 

База данных ViewModel, реализующая 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));
        }
    }
}
 

Скриншот:

введите описание изображения здесь