XAML: cannot bind entry with a Xamarin.Essentials Preferences item

jddjdd ✭✭✭USMember ✭✭✭
edited February 14 in Xamarin.Forms

When I open my view, the entry is not set correctly.
The Entry should show "4.0" but is empty.
After setting the Entry, the Value (dip) should be saved in the preferences but is not shown again.
Any help please?

.xaml.cs

using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Xamarin.Essentials;

namespace AlmicantaratXF.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class SettingsPageXAML : ContentPage
    {
        public float dip
        {
            get => Preferences.Get("dip", 4.0f);
            set
            {
                Preferences.Set("dip", value);
                OnPropertyChanged("dip");
            }
        }
        public SettingsPageXAML()
        {
            InitializeComponent();
        }
        public void Dip_completed(object sender, EventArgs e)
        {
            dip = float.Parse(((Entry)sender).Text, System.Globalization.CultureInfo.CurrentUICulture);
        }
    }
}

.xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="AlmicantaratXF.Views.SettingsPageXAML"
                 xmlns:i18n="clr-namespace:AlmicantaratXF.Views;assembly=AlmicantaratXF">
        <ContentPage.Content>
            <StackLayout>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="3*" />
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="2*" />
                    </Grid.ColumnDefinitions>
                    <Label Text="{i18n:Translate dip}"

                           Grid.Row="0" Grid.Column="0" />
                    <Entry Text="{Binding Path=dip, StringFormat='{0:N1}'}" 
                           Keyboard="Numeric"
                           MaxLength="4"
                           Completed="Dip_completed"
                           Grid.Row="0" Grid.Column="1" />
                </Grid>
            </StackLayout>
        </ContentPage.Content>
    </ContentPage>
Tagged:

Best Answer

Answers

  • Bernhard_PollerspöckBernhard_Pollerspöck ✭✭ Member ✭✭

    A Entry itself should only be able to bind a string Property(for obvious reasons).

    The normal behaviour is to sanitize and parse the string before you use it as float (and the other way around you convert your float property on load to a correct formated string).
    If you want to prevent wrong input, you can set the keyboard typ to numeric and/or sanitize the string on each change manually and remove not allowed characters manually

  • jddjdd ✭✭✭ USMember ✭✭✭
    edited February 14

    So I binded it with a string but it does not work better:

    xaml:

        <Entry Text="{Binding Path=strDip}" 
                FontSize="Medium"
                VerticalOptions="Center"
                Keyboard="Numeric"
                MaxLength="4"
                Completed="Dip_completed"
                Grid.Row="0" Grid.Column="1" />
    

    xaml.cs:

        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class SettingsPageXAML : ContentPage
            {
                            public string strDip
                            {
                                get
                                {
                                    return string.Format("{0:N1}", Preferences.Get("dip", 4.0f),
                                        System.Globalization.CultureInfo.CurrentUICulture);
                                }
                                set
                                {
                                    float dip = float.Parse(value, System.Globalization.CultureInfo.CurrentUICulture);
                                    Preferences.Set("dip", dip);
                                    OnPropertyChanged("strDip");
                                }
                            }
                            public SettingsPageXAML()
                            {
                                InitializeComponent();
                            }
                            public void Dip_completed(object sender, EventArgs e)
                            {
                                strDip = ((Entry)sender).Text;
                            }
            }
    
  • jddjdd ✭✭✭ USMember ✭✭✭
    edited February 14

    Sorry, I try to format code but it doesn't work, don't know why...
    OK, found the solution for formatting code there: https://forums.xamarin.com/discussion/52992/formatting-code-blocks-in-your-posts

  • Bernhard_PollerspöckBernhard_Pollerspöck ✭✭ Member ✭✭

    Your ViewModel:

    private string _field;
        public string MyInput
        {
            get { return _field; }
            set
            {
                if (_field != value)
                {
                    _field = value;
                    this.OnPropertyChanged();
                    this.OnPropertyChanged(nameof(MyFloat));
                }
            }
        }
    
        public float MyFloat
        {
            get
            {
                return float.Parse(_field);
            }
        }
    

    Then just use your entry binding like:

    <Entry Text="{Binding MyInput}" />
    

    You can use your Float property to show in a view or handle otherwise..

    IMPORTANT: this is not a propper way to savely sanitize and convert user input!!!

  • jddjdd ✭✭✭ USMember ✭✭✭

    OK, let's do things more simple, just saving a string in the Xamarin.Essentials preferences. I still have a binding problem. Please note that it worked on my previous view but I decided to convert it to XAML and can't make it.

    .xaml.cs

    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    using Xamarin.Essentials;
    
    namespace AlmicantaratXF.Views
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class SettingsPageXAML : ContentPage
        {
            public string dip_input
            {
                get => Preferences.Get("dip", "4.0");
                set
                {
                        Preferences.Set("dip", value);
                        this.OnPropertyChanged();
                }
            }
            public SettingsPageXAML()
            {
                InitializeComponent();
            }
    
        }
    }
    

    .xaml:

                    <Entry Text="{Binding dip_input}" 
                           FontSize="Medium"
                           VerticalOptions="Center"
                           Keyboard="Numeric"
                           MaxLength="4"
                           Grid.Row="0" Grid.Column="1" />
    
  • Bernhard_PollerspöckBernhard_Pollerspöck ✭✭ Member ✭✭

    Can you provide me with your log if there is any error messages and details what is shown?

    Did you test to use your entry with a normal string and got all workin correctly(to eliminiate other problems beside the useage of methods in properties(i did never do that to be honest)

  • jddjdd ✭✭✭ USMember ✭✭✭
    edited February 14

    Please find attached the build output.
    isn't a normal string in my last example?
    Unfortunately, I have to start my application several times (5 to 20 times) is VS emulator until it opens correctly. So that I cannot use the debbuger...
    I would like t o compile the xaml file to check the binding, but I don't know which BindingContext to set...

  • Bernhard_PollerspöckBernhard_Pollerspöck ✭✭ Member ✭✭

    You use a method to store and receive the value. id have no expirience in that regard if that matters.

    You have to restart multible times? you probably should fix that before anything other. this should not happen. and maybe this cause is causing you other unexpected behaviour.

  • jddjdd ✭✭✭ USMember ✭✭✭

    Even like that, the Entry is not initiated properly. Should I add a line of code to get the binding initiated:

        public partial class SettingsPageXAML : ContentPage
        {
            private string str_dip = "4";
            public string dip_input
            {
                get { return str_dip; }
                set
                {
                        str_dip = value;
                        this.OnPropertyChanged();
                }
            }
            public SettingsPageXAML()
            {
                InitializeComponent();
            }
        }
    

    It seems I should:
    In this video at 29:45 He says we need to set initial value:
    https://docs.microsoft.com/fr-fr/xamarin/xamarin-forms/xaml/

  • jddjdd ✭✭✭ USMember ✭✭✭

    No worries, thank you for taking time to solve my issue.
    First, I need to start multiple times your android project too. So there is a configuration issue with my emulator. I should start another question for that (I attached my debug output anyway).
    Second, I guess I have to study MVVM to understand everything about binding. I look at your code this afternoon and get back to you.
    Regards,

  • jddjdd ✭✭✭ USMember ✭✭✭

    Works well.
    Now I need to add my Xamarin.Essentials Preferences... I guess in SettingsViewModel.cs

    BaseViewModel.cs

        public class BaseViewModel : INotifyPropertyChanged
        {
            #region INotifyPropertyChanged
            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
            #endregion
        }
    

    SettingsViewModel.cs

        public class SettingsViewModel : BaseViewModel
        {
            private string _dip = "4.0";
    
            public string dip_input
            {
                get { return _dip; }
                set
                {
                    _dip = value;
                    this.OnPropertyChanged();
                }
            }
            public SettingsViewModel()
            {
            }
        }
    

    and finally SettingsPageXAML.xaml.cs

        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class SettingsPageXAML : ContentPage
        {
            public SettingsViewModel ViewModel
            {
                get => this.BindingContext as SettingsViewModel;
                set => this.BindingContext = value;
            }
            public SettingsPageXAML()
            {
                InitializeComponent();
                this.ViewModel = new SettingsViewModel();
            }
        }
    
Sign In or Register to comment.