Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

I expect to get an infinite loop when I implement IValueConverter but there isn't.

EliranEliran USMember ✭✭✭

I have an Entry text which is binded in TwoWay mode to a simple string.
In Convert() I add the letter "S" ("for came from Source")
In ConvertBack() I add the letter "T" ("for came from Target").

Now, when I write something in the entry (lets say I write the string "A"), I expect the next phases to occur:
1. The Target was changed, so ConvertBack() should be called, and send the string AT to the source (Because the converter adds "T").
2. Therefore the Source was changed, so Convert() should be called and send the string ATS to the Target (Because the converter adds "S")..
3. Therefore the Target was changed, so ConvertBack() should be called, and send the string ATST to the source.
4. Therefore the Source was changed, so Convert() should be called and send the string ATSTS to the Target.
5. and on and on and on......Endlessly.

Of some reason It doesn't happen ( ConvertBack() is being called, then Convert() is being called and that's it).

Why isn't an infinite loop over here?

This is the converter:

public class NiceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string resultAfterConvert = value as string + "S";
            return resultAfterConvert;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string resultAfterConvert = value as string + "T";
            return resultAfterConvert;
        }
    }

Answers

  • LeonLuLeonLu Member, Xamarin Team Xamurai
    edited August 6

    When you used NiceConverter If you used StaticResource to executed local:NiceConverter like following code.`

      <ContentPage.Resources>
            <ResourceDictionary>
                <local:NiceConverter x:Key="MyNiceConverter" />
            </ResourceDictionary>
        </ContentPage.Resources>
    
            <Entry x:Name="Fields" Text="{Binding FieldValue1,Converter={StaticResource MyNiceConverter}, Mode=TwoWay}" 
    
    
    
      
    
    
    

    If so, becuase you use StaticResource

    A StaticResource will be resolved and assigned to the property during the loading of the XAML which occurs before the application is actually run. It will only be assigned once and any changes to resource dictionary ignored.

  • LeonLuLeonLu Member, Xamarin Team Xamurai
    edited August 6

    I make a test with the ListView ,I set the CachingStrategy="RecycleElement" to listivew, If the Item in the listview disappear to appear, the layout of will be reload. If I move the Entry from disappear to the appear, your infinite loop will be executed.

    Due to the StaticResource limiation, you got the result by design.

  • EliranEliran USMember ✭✭✭

    @LeonLu Hi, Thanks for your answer and for the Demo. Yes I use StaticResource:

     <ContentPage.Resources>
            <cnv:NiceConverter x:Key="myNiceConverter"/>
      </ContentPage.Resources>
    

    <Entry x:Name="myEntry" Text="{Binding Str,Converter= {StaticResource myNiceConverter}, Mode=TwoWay}"/>

    What I don't understand, is why does it make any difference, that staticResource is being loaded before the program runs.
    I don't change the resource at all during the runtime -I just use the converter's methods ( but I don't replace one converter with an other converter).
    So why does the fact that this is a static resource, avoid the infinite loop to occur?

  • EliranEliran USMember ✭✭✭

    @LeonLu said:
    and set the value to the Entry only once.

    Maybe I missed here something about converters.
    I have a class which implements IPropertyChanged. Everytime the binded property (The property of the object in the memory) is being changed, there is an event which is being fired, and the value in the entry is being changed accordingly.

    Now, due to the Converter, I expect that every time I change the value of this property in the memory, this value will go through the Converter, which will change it (In this example it adds 'S'), and the changed value will continue to the entry.
    It means the value of the entry will not be set only once, but will be set again and again and again...when ever
    the value of the object's property is being changed.

     public class MainPageViewModel : INotifyPropertyChanged
        {
            private string _str;
            public string Str {
                get => _str;
                set
                {
                    if(_str!=value)
                    {
                        _str = value;
                        OnPropertyChanged();
                    }
    
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
            {
                var changed = PropertyChanged;
                if (changed == null)
                    return;
    
                changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
    
    <ContentPage.Resources>
            <cnv:NiceConverter x:Key="myNiceConverter"/>
        </ContentPage.Resources>
        <StackLayout>
            <!-- Place new controls here -->
            <Label Text="Welcome to Xamarin.Forms!" 
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />
            <Entry x:Name="myEntry" Text="{Binding Str,Converter= {StaticResource myNiceConverter}, Mode=TwoWay}"/>
        </StackLayout>
    

    ```
    public partial class MainPage : ContentPage
    {
    public MainPageViewModel MainPageViewModel { get; set; }
    public MainPage()
    {
    InitializeComponent();
    MainPageViewModel = new MainPageViewModel();;

            BindingContext =  MainPageViewModel;
        }
    }
    

    ```

  • NicoleLuNicoleLu Member, Xamarin Team Xamurai
    edited August 18

    Hi @Eliran

    I made a demo with your code with the latest Xamarin.Forms, when I do the test, it is calling the ConvertBack and then Convert for one time every time I enter a letter into the Entry, I'm thinking that UI refreshing is blocking the infinite loop, or there's some mechanism code behind to prevent it from happening.

    Actually, there shouldn't be a infinite loop in Converter since it's desingned to format the data rather than change the value. If there's a infinite loop, I'd say it's likely a potential bug.

    Also, to help you better, may I know why do you need the infinite loop?

  • EliranEliran USMember ✭✭✭
    edited August 18

    Thanks for the answer @NicoleLu.

    @NicoleLu said:
    Actually, there shouldn't be a infinite loop in Converter since it's desingned to format the data rather than change the value. If there's a infinite loop, I'd say it's likely a potential bug.

    Well...the converter doesn't know what is the goal of the developer. It knows only two things:
    "If you change the source, Convert() will be activated"
    and
    "If you change the target, ConvertBack() will be activated:
    We as the developers decide what to do inside these methods (design, change...)

    @NicoleLu said:
    Also, to help you better, may I know why do you need the infinite loop?

    I don't need the infinite loop :smile: I've just noticed something, which is not logical because this explanation:

    Now, when I write something in the entry (lets say I write the string "A"), I expect the next phases to occur:
    1. The Target was changed, so ConvertBack() should be called, and send the string AT to the source (Because the converter adds "T").
    2. Therefore the Source was changed, so Convert() should be called and send the string ATS to the Target (Because the converter adds "S")..
    3. Therefore the Target was changed, so ConvertBack() should be called, and send the string ATST to the source.
    4. Therefore the Source was changed, so Convert() should be called and send the string ATSTS to the Target.
    5. and on and on and on......Endlessly.

    Of some reason It doesn't happen ( ConvertBack() is being called, then Convert() is being called and that's it).

  • NicoleLuNicoleLu Member, Xamarin Team Xamurai
    edited August 19

    Hi @Eliran yes you're right, I shouldn't have limit the Converter's usage, sorry about that. But still, there's not supposed to be any infinite loop in Converter since it'll cause the app not responding from the very start if it's doing the convert and convertback again and again.

    I digged a bit deeper into the Converter, here's something I found:

    I noticed that every time I enter a letter into the Entry, ConvertBack() will be called once and then the Convert() method, and it stops here. So why ConvertBack is not called again would be the reason why the infinite loop didn't happen. When I look into the call stack of ConvertBack() here is what it looks like:

    As you can see in the screenshot, when we trace the ConvertBack to the source, it's Android's native OnTextChange() event, so the question now becomes: why there's no textchange event when a different string was put into the EditText widget through Convert().

    Then I debug with Xamarin.Forms source code, trace from Convert() method, till the property is set, didn't find anywhere that fires a textchange event, since it's rather a complicated implementation in Xamarin.Forms, it's hard to explain why it act like this here, if you'd like to get a clearer answer, you can file a issue in github and check if anyone who knows the internal logic better can give you a answer.

    Thanks.

  • EliranEliran USMember ✭✭✭

    Thanks @NicoleLu for the screen shot . I don't use Call Stack. Maybe I should use this tool from time to time.
    It is nice to see that OnTextChange() is being called "under the hood".

    I opened an issue in GitHub. I of course will update if I get an answer.

    And yes, this is actually the question:

    @NicoleLu said:
    so the question now becomes: why there's no textchange event when a different string was put into the EditText widget through Convert().

  • NicoleLuNicoleLu Member, Xamarin Team Xamurai

    Hi @Eliran

    Also, if you have interest in debugging Xamarin.Forms source code, here's a blog that may help: A Beginner’s Guide for Contributing to Xamarin.Forms. Currently I'm using the Control Gallery to step into XF source code since no other effective available.

  • EliranEliran USMember ✭✭✭

    Thanks! I will check it.

Sign In or Register to comment.