微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

当在 ObservableCollection 中引用该属性时,为什么此 Binding 不起作用?

如何解决当在 ObservableCollection 中引用该属性时,为什么此 Binding 不起作用?

我有以下问题,我无法理解。我是 Xamarin 的新手,我已经研究这个特定问题有一段时间了。

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

长话:

假设我们有一个中继器,它由虚拟机的 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 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 ),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 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 没有。有什么我遗漏的吗?

解决方法

我找到了解决我的问题的方法。这不是我想要的 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>

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。