为啥在 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 没有。有什么我遗漏的吗?
【问题讨论】:
可能存在绑定错误。请查看输出以搜索任何可疑内容。 不幸的是,输出很清晰。我已经为 ios 和 android 编译了[ assembly: Xamarin.Forms.Xaml.XamlCompilation( Xamarin.Forms.Xaml.XamlCompilationOptions.Compile ) ]
和 Xamarin.Forms.Internals.Log.Listeners.Add(new DelegateLogListener((arg1, arg2) => 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.MyProperty
的BindingContext.
和引用容器View的Source
。由于MyProperty
仅在CardTemplate
级别上可用,CardTemplate
的所有子级都不知道它的存在。为了让他们可以使用,我们使用Source
属性来引用模板,然后使用BindingContext
来引用ViewModel 容器的当前对象(在本例中为MyGroup
)。由于我们现在可以访问每个MyGroup
对象,我们可以调用它的属性(在本例中为MyProperty
),从而达到我们的目标。
为什么这不适用于MultiBinding
我无法理解,但它已经足够了。
【讨论】:
以上是关于为啥在 ObservableCollection 中引用该属性时,此绑定不起作用?的主要内容,如果未能解决你的问题,请参考以下文章
为啥分配给 ObservableCollection 不会破坏 CollectionChanged 订阅者列表?
WPF ObservableCollection 异步调用问题