Forum Xamarin Xamarin.Forms

What technique is best for multiple Entries evaluating each other's values?

Hello, if I were to want 3 entries that all measured the same object differently, as in "Weight, Volume, Count," what technique should I look into to allow a user to enter into any of the three, and the other 2 update? Like, if I enter a Weight, the Volume and Count adjust, or you could enter a Count which recalculates the other values. The objects are being displayed in a CollectionView with a DataTemplate.

Initially, I called methods in property setters... but it causes an infinite loop. Is Multi-Binding relevant? I didn't really get the gist of what that meant. I'd be glad to share a test project if I got a tip on which way to begin it.

Thanks!

Answers

  • stXamDevstXamDev Member ✭✭✭
    edited October 17

    Each entry has a text changed event , capture that and accordingly change the text of the other two entries.

    View

    <StackLayout BackgroundColor="White" Padding="20,10,20,10">
                <StackLayout Orientation="Horizontal">
                    <Label Text="Entry One(x)" VerticalOptions="Center"/>
                    <Entry x:Name="XEntry" HorizontalOptions="FillAndExpand" TextChanged="Entry_TextChanged"/>
                </StackLayout>
                <StackLayout Orientation="Horizontal">
                    <Label Text="Entry Two(x square)" VerticalOptions="Center"/>
                    <Entry x:Name="XSquareEntry" HorizontalOptions="FillAndExpand" TextChanged="Entry_TextChangedSquare"/>
                </StackLayout>
                <StackLayout Orientation="Horizontal">
                    <Label Text="Entry three(x cube)" VerticalOptions="Center"/>
                    <Entry x:Name="XCubeEntry" HorizontalOptions="FillAndExpand" TextChanged="Entry_TextChangedCube"/>
                </StackLayout>
            </StackLayout>
    

    CodeBehind

    private void Entry_TextChanged(object sender, TextChangedEventArgs e)
            {
                if (sender is Entry entry && entry.IsFocused)
                {
                    if (double.TryParse(e.NewTextValue, out double number))
                    {
                        XSquareEntry.Text = (number * number).ToString();
                        XCubeEntry.Text = (number * number * number).ToString();
                    }
                    else
                    {
                        DisplayAlert("Alert", "Enter numeric val", "ok");
                    }
                }
            }
    
            private void Entry_TextChangedSquare(object sender, TextChangedEventArgs e)
            {
                if (sender is Entry entry && entry.IsFocused)
                {
                    if (double.TryParse(e.NewTextValue, out double numberSquare))
                    {
                        double number = Math.Sqrt(numberSquare);
                        XEntry.Text = number.ToString();
                        XCubeEntry.Text = (number * number * number).ToString();
                    }
                    else
                    {
                        DisplayAlert("Alert", "Enter numeric val", "ok");
                    }
                }
            }
    
            private void Entry_TextChangedCube(object sender, TextChangedEventArgs e)
            {
                if (sender is Entry entry && entry.IsFocused)
                {
                    if (double.TryParse(e.NewTextValue, out double numberCube))
                    {
                        double number = Math.Ceiling(Math.Pow(numberCube, (double)1 / 3)); ;
                        XEntry.Text = number.ToString();
                        XSquareEntry.Text = (number * number).ToString();
                    }
                    else
                    {
                        DisplayAlert("Alert", "Enter numeric val", "ok");
                    }
                }
            }
    

    Demo

  • JohnHardmanJohnHardman GBUniversity admin
    edited October 17

    @ScumSprocket said:
    Initially, I called methods in property setters... but it causes an infinite loop.

    There are various approaches, but the key thing is to break the infinite loop.

    One way is to do a recursion check in your setters, so that if setter B or setter C is called from setter A, they still do their work, but if setter A calls setter B (or C) which then calls setter A again, the nested setter A will not do anything.

    I haven't tested this, so forgive any typo's,

    This would be the code for the Weight property. Do the equivalent for Volume and Count.

    public int _weight;
    public bool _weightCalled;
    public int Weight
    {
        get => _weight;
        set
        {
            if ((_weight != value) && (!_weightCalled))
            {
                _weightCalled = true;
                _weight = value;
                // Raise PropertyChanged event as normal here, using base class or Invoke
                Volume = CalculateVolume();
                Count = CalculateCount();
                _weightCalled = false;
            }
        }
    }
    

    The post by @stXamDev above similarly breaks the infinite loop, although this time in the code-behind of the View, with a nice use of IsFocused breaking the loop.

    if (sender is Entry entry && entry.IsFocused)
    

    @ScumSprocket said:
    Is Multi-Binding relevant?

    I don't think so, but I understand why the question arises. I'd stick with having three properties.

  • ScumSprocketScumSprocket Member ✭✭

    Thanks I have been fighting some other buggy stuff, but I can't wait to try these out!

  • ScumSprocketScumSprocket Member ✭✭

    I think I'm going for the model code, because all my entries are in DataTemplates, so code-behind doesn't know where these entries are... is there a way to let the code-behind know?

  • jezhjezh Member, Xamarin Team Xamurai

    is there a way to let the code-behind know?

    You can try to use Data Binding to achieve this.

    1.Create a viewmode (e.g. TestModel.cs)

        public class TestModel : INotifyPropertyChanged
        {
            string _entry1Word;
    
            public string Entry1Word
            {
                set { 
                    SetProperty(ref _entry1Word, value);
    
                    try
                    {
                        Entry2Word = (float.Parse(_entry1Word.ToString()) * 2).ToString();
                        Entry3Word = (float.Parse(_entry1Word.ToString()) * 3).ToString();
                    }
                    catch (Exception e)
                    {
                    }
                    finally {
                    }
    
                }
    
                get { return _entry1Word; }
            }
    
    
            string _entry2Word;
    
            public string Entry2Word
            {
                set { 
                    SetProperty(ref _entry2Word, value);
                }
    
                get { return _entry2Word; }
            }
    
            string _entry3Word;
    
            public string Entry3Word
            {
                set { 
                    SetProperty(ref _entry3Word, value);
                }
    
                get { return _entry3Word; }
            }
    
            public TestModel()
            {
                Entry1Word = "1";
                Entry2Word = "2";
                Entry3Word = "3";
            }
    
            bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
            {
                if (Object.Equals(storage, value))
                    return false;
    
                storage = value;
                OnPropertyChanged(propertyName);
                return true;
            }
    
            protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
        }
    

    Note:When you change the first entry's value(Entry1Word), you can also change the binded value of other two entries.
    Of course, you can also change other two binded value like this.

            public string Entry1Word
            {
                set { 
                    SetProperty(ref _entry1Word, value);
    
                    try
                    {
                        Entry2Word = (float.Parse(_entry1Word.ToString()) * 2).ToString();
                        Entry3Word = (float.Parse(_entry1Word.ToString()) * 3).ToString();
                    }
                    catch (Exception e)
                    {
                    }
                    finally {
                    }
                }
                get { return _entry1Word; }
            }
    

    2.Usage sample:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:multipleapp1="clr-namespace:MultipleApp1"
                 x:Class="MultipleApp1.MainPage">
    
    
        <ContentPage.BindingContext>
            <multipleapp1:TestModel>
            </multipleapp1:TestModel>
        </ContentPage.BindingContext>
    
        <StackLayout BackgroundColor="White" Padding="20,10,20,10">
            <StackLayout Orientation="Horizontal">
                <Label Text="Entry One(x)" VerticalOptions="Center"/>
                <Entry x:Name="XEntry" HorizontalOptions="FillAndExpand" Text="{Binding Entry1Word}" />
            </StackLayout>
            <StackLayout Orientation="Horizontal">
                <Label Text="Entry Two(x square)" VerticalOptions="Center"/>
                <Entry x:Name="XSquareEntry" HorizontalOptions="FillAndExpand"  Text="{Binding Entry2Word}"/>
            </StackLayout>
            <StackLayout Orientation="Horizontal">
                <Label Text="Entry three(x cube)" VerticalOptions="Center"/>
                <Entry x:Name="XCubeEntry" HorizontalOptions="FillAndExpand" Text="{Binding Entry3Word}"/>
            </StackLayout>
        </StackLayout>
    
    </ContentPage>
    
Sign In or Register to comment.