2

I have the following problem, which I cannot comprehend. I'm quite new to Xamarin, and I've been working on this particular problem for quite some time now.

TL;DR: I have an MyGroups[] array, which is used by a Repeater in XAML. The DataTemplate, used by the Repeater, has an ObservableCollection, which is populated in XAML. Binding one of MyGroups's properties with Description.Title does not work, however using the same property outside of the Description Collection works just fine.

Long story:

Let's say we have a Repeater, which is populated by the VM's MyGroups[] property. The Repeater and Models:Group look something like this:

<Grial:Repeater ItemTemplate = "{ DynamicResource GroupDataTemplate }"
                           ItemsSource  = "{ Binding MyGroups }"/>
namespace MyApp.Models
{
   public class Group
   {
      public string MyProperty
      {
         get;
         set;
      }

      public string MyOtherProperty
      {
         get;
         set;
      }
   }
}

This Repeater gets the following DataTemplate:

<DataTemplate x:Key = "GroupDataTemplate">
   <Cards:CardTemplate x:DataType = "Models:Group">

      <StackLayout HorizontalOptions = "Center"
                   VerticalOptions   = "Center">
         <Label>
            <Label.Text>
               <MultiBinding StringFormat="{} {0} {1}">     //<---- this works
                  <Binding Path = "MyProperty" />
                  <Binding Source = "{ x:Static Strings:AppResources.MyString }"/>
               </MultiBinding>
            </Label.Text
         </Label>
      </StackLayout>


      <Cards:CardTemplate.Descriptions>
         <Models:Description>
            <Models:Description.Title>
               <MultiBinding StringFormat="{} {0} {1}">     //<---- this value is always null
                  <Binding Path = "MyProperty" />
                  <Binding Source = "{ x:Static Strings:AppResources.MyString }"/>
               </MultiBinding>
            </Models:Description.Title>
         </Models:Description>


         <Models:Description>
            <Models:Description.Title>
               <MultiBinding StringFormat="{} {0} {1}">     //<---- this value is always null
                  <Binding Path = "MyOtherProperty" />
                  <Binding Source = "{ x:Static Strings:AppResources.MyString }"/>
               </MultiBinding>
            </Models:Description.Title>
         </Models:Description>

      </Cards:CardTemplate.Descriptions>
   </Cards:CardTemplate>
</DataTemplate>

The Cards:CardTemplate looks something like this:

<Grial:CardView ...>

   ...

   <Grial:GridView RowSpacing  = "16"
                   Grid.Row    = "1"
                   ColumnCount = "2"
                   ItemsSource = "{ Binding Source={ RelativeSource AncestorType={ x:Type Cards:CardTemplate } }, Path=Descriptions }">

      <Grial:GridView.ItemTemplate>
         <DataTemplate x:Name = "x_cardItemsDescriptionTemplate">
            <StackLayout Spacing     = "5"
                         Orientation = "Horizontal">

               <Label VerticalOptions = "Center"
                      Text            = "{ Binding Source={ RelativeSource AncestorType={ x:Type Models:Description } }, Path=Icon }"/>

               <Label VerticalOptions = "Center"
                      Text            = "{ Binding Source={ RelativeSource AncestorType={ x:Type Models:Description } }, Path=Description }"/>
            </StackLayout>
         </DataTemplate>
      </Grial:GridView.ItemTemplate>
   </Grial:GridView>
</Grial:CardView>

The Description looks like this

using System;

using Xamarin.Forms;


public class Description
   : BindableObject
{
   public static readonly BindableProperty IconProperty = BindableProperty.Create( propertyName: nameof( Description.Icon ),
                                                                                   returnType: typeof( string ),
                                                                                   declaringType: typeof( Description ),
                                                                                   defaultBindingMode: BindingMode.TwoWay,
                                                                                   propertyChanged: Description.OnIconChanged );

   public static readonly BindableProperty TitleProperty = BindableProperty.Create( propertyName: nameof( Description.Title ),
                                                                                    returnType: typeof( string ),
                                                                                    declaringType: typeof( Description ),
                                                                                    defaultBindingMode: BindingMode.TwoWay,
                                                                                    propertyChanged: Description.OnTitleChanged );

   public string Icon
   {
      get => (string)GetValue( IconProperty );
      set => SetValue( property: IconProperty, value: value );
   }

   public string Title
   {
      get => (string)GetValue( TitleProperty );
      set => SetValue( property: TitleProperty, value: value );
   }


   private static void OnIconChanged( BindableObject bindable, object old_value, object new_value )
   {
      try
      {
         ( (Description)bindable ).Icon = new_value as string;
      }
      catch( Exception e )
      {
         ...
      }
   }

   private static void OnTitleChanged( BindableObject bindable, object old_value, object new_value )
   {
      try
      {
         ( (Description)bindable ).Title = new_value as string;
      }
      catch( Exception e )
      {
         ...
      }
   }
}

What I don't understand is why {Binding Path=MyProperty} works for the Label, but not for the Description. The problem is not the CardTemplate, because every hard-coded or resource-bound string gets assigned to the appropriate element. The only problem is the binding itself!

Things I've tried:

  • BindingList instead of ObservableCollections
  • Description inherits from INotifyPropertyChanged/BindableObject
  • Assigning "Source={ RelativeSource AncestorType={ x:Type Models:Group } }" with or without levels
  • {Binding .MyProperty}, {Binding Models:Groups.MyProperty}, {Binding /MyProperty}
  • Implementing INotifyPropertyChanged in Group

Nothing seems to work, and I cannot understand why? Why does the Label.Text has its value set correctly, but Description.Title not. Is there something that I'm missing?

Panos
  • 21
  • 2
  • There may be a binding error. Please look at the output to search for anything suspicious. – Ackdari Mar 05 '21 at 08:07
  • Unfortunately, the output is clear. I've compiled for iOS and Android, with `[ assembly: Xamarin.Forms.Xaml.XamlCompilation( Xamarin.Forms.Xaml.XamlCompilationOptions.Compile ) ]`, and with `Xamarin.Forms.Internals.Log.Listeners.Add(new DelegateLogListener((arg1, arg2) => Debug.WriteLine(arg2)));` without any success. – Panos Mar 05 '21 at 21:43
  • Have you check the property get the correct value? What is you viewmodel? Could you a completed code sanippet for me to reproduce? – Wendy Zang - MSFT Mar 09 '21 at 09:34
  • The property does contain the correct value, as demostrated by the `Label`. [Here](https://github.com/pgiokoglou/BlankApp1) is a demo that reproduces the problem... – Panos Mar 19 '21 at 17:18
  • I downloaded the sample, but i could not open it in VS. Could you check it again? – Wendy Zang - MSFT Mar 23 '21 at 09:10
  • I just downloaded directly from github, and I was able to open and compile it – Panos Mar 23 '21 at 10:34
  • I checked the code again. Maybe there is something wrong with my VS. Now, i could open it. Could you give more details about how to reproduce? Where you used `Binding Path= Description` for? – Wendy Zang - MSFT Mar 24 '21 at 11:54
  • In MainPage.xaml there is an assignment `` (l: 32) which is the problem. Same assignment in the label above (commented out) works just fine. Further experimentation showed me that the `PropertyChanged` event is not getting triggered for the elements in the array. Similar to [this](https://stackoverflow.com/a/3478581/9071302). This is the part that I don't understand. – Panos Mar 25 '21 at 07:38

0 Answers0