为啥在 ObservableCollection 中引用该属性时,此绑定不起作用?

Posted

技术标签:

【中文标题】为啥在 ObservableCollection 中引用该属性时,此绑定不起作用?【英文标题】:Why does this Binding not work, when the property is referenced in a ObservableCollection?为什么在 ObservableCollection 中引用该属性时,此绑定不起作用? 【发布时间】:2021-06-03 21:51:54 【问题描述】:

我有以下问题,我无法理解。我对 Xamarin 很陌生,而且我已经研究这个特殊问题很长一段时间了。

TL;DR: 我有一个 MyGroups[] 数组,由 XAML 中的中继器使用。中继器使用的 DataTemplate 有一个 ObservableCollection,在 XAML 中填充。将 MyGroups 的属性之一与 Description.Title 绑定不起作用,但是在描述集合之外使用相同的属性就可以了。

长篇大论:

假设我们有一个中继器,它由 VM 的 MyGroups[] 属性填充。 Repeater 和 Models:Group 看起来像这样:

<Grial:Repeater ItemTemplate = " DynamicResource GroupDataTemplate "
                           ItemsSource  = " Binding MyGroups "/>
namespace MyApp.Models

   public class Group
   
      public string MyProperty
      
         get;
         set;
      

      public string MyOtherProperty
      
         get;
         set;
      
   

此中继器获取以下 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>

Cards:CardTemplate 看起来像这样:

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

描述是这样的

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

我不明白为什么 Binding Path=MyProperty 适用于标签,但不适用于描述。问题不在于 CardTemplate,因为每个硬编码或资源绑定字符串都被分配给适当的元素。唯一的问题是绑定本身!

我尝试过的事情:

BindingList 代替 ObservableCollections 描述继承自 INotifyPropertyChanged/BindableObject 分配“Source=RelativeSource AncestorType= x:Type Models:Group ”有或没有级别 Binding .MyProperty、Binding Models:Groups.MyProperty、Binding /MyProperty 在组中实现 INotifyPropertyChanged

似乎没有任何效果,我不明白为什么?为什么 Label.Text 的值设置正确,但 Description.Title 没有。有什么我遗漏的吗?

【问题讨论】:

可能存在绑定错误。请查看输出以搜索任何可疑内容。 不幸的是,输出很清晰。我已经为 iosandroid 编译了 [ assembly: Xamarin.Forms.Xaml.XamlCompilation( Xamarin.Forms.Xaml.XamlCompilationOptions.Compile ) ]Xamarin.Forms.Internals.Log.Listeners.Add(new DelegateLogListener((arg1, arg2) =&gt; Debug.WriteLine(arg2))); 没有任何成功。 您检查属性是否得到正确的值?你是什​​么视图模型?你能给我一个完整的代码片段让我重现吗? 该属性确实包含正确的值,如 Label 所示。 Here是重现问题的demo... 我下载了示例,但我无法在 VS 中打开它。你能再检查一下吗? 【参考方案1】:

我找到了解决问题的方法/解决方法。这不是我想要的 100%,但是,嗯,我想已经足够接近了。

我无法实现的是使用 MultiBindings,但我并不严格需要在我的用例中连接多个字符串,所以我选择了 StringFormat。

<Cards:CardTemplate x:Name = "template">
    <Cards:CardTemplate.Descriptions>
        <Models:Description Title = " Binding Path=BindingContext.MyProperty, Source= x:Reference Name=template , StringFormat=... "/>
    </Cards:CardTemplate.Descriptions>
</Cards:CardTemplate>

两个重点是Path=BindingContext.MyPropertyBindingContext.和引用容器View的Source。由于MyProperty 仅在CardTemplate 级别上可用,CardTemplate 的所有子级都不知道它的存在。为了让他们可以使用,我们使用Source 属性来引用模板,然后使用BindingContext 来引用ViewModel 容器的当前对象(在本例中为MyGroup)。由于我们现在可以访问每个MyGroup 对象,我们可以调用它的属性(在本例中为MyProperty),从而达到我们的目标。

为什么这不适用于MultiBinding 我无法理解,但它已经足够了。

【讨论】:

以上是关于为啥在 ObservableCollection 中引用该属性时,此绑定不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥分配给 ObservableCollection 不会破坏 CollectionChanged 订阅者列表?

为啥滚动这个 xamarin 页面这么慢?

WPF ObservableCollection 异步调用问题

联盟两个ObservableCollection列表

在 ObservableCollection 上使用 Lambda 表达式

合并的 ObservableCollection