Xamarin自定义控件:BindableProperty计算属性绑定的空值与更新问题
问题
在Xamarin中开发自定义控件TabbedLayout时,遇到了计算属性绑定失效的问题:通过BindableProperty绑定的TabContents视图数组,其对应的计算属性DisplayedElement始终显示null。原因是计算属性在BindableProperty未赋值时就已初始化,且没有属性变更通知机制,无法在TabContents或选中索引变化时自动更新视图。
具体实现细节:
- XAML中绑定视图数组到控件的
TabContents属性:
<controls:TabbedLayout.TabContents> <x:Array Type="{x:Type View}"> <Button Text="Hello world 1!"/> <Button Text="Hello world 2!"/> <Button Text="Hello world 3!"/> <Button Text="Hello world 4!"/> <Button Text="Hello world 5!"/> </x:Array> </controls:TabbedLayout.TabContents>
- 后台定义的
TabContentsBindableProperty:
public static readonly BindableProperty TabContentsProperty = BindableProperty.Create( nameof(TabContents), typeof(View[]), typeof(TabbedLayout), null, BindingMode.OneWay); public View[] TabContents { get => (View[])GetValue(TabContentsProperty); set => SetValue(TabContentsProperty, value); }
- 为了显示单个视图,定义了
SelectedIndex和计算属性DisplayedElement:
public int SelectedIndex = 0; public View DisplayedElement = TabContents?.ElementAt(SelectedIndex);
- 控件XAML中绑定
DisplayedElement:
<ContentView Content="{Binding DisplayedElement, Source={x:Reference TheControl}, Mode=OneWay}"/>
解决方案
要解决这个问题,需要给BindableProperty添加变更回调、让计算属性支持通知机制,确保DisplayedElement能在依赖属性变化时自动更新并触发视图刷新。
1. 把SelectedIndex改为BindableProperty(推荐)
如果需要外部绑定或响应SelectedIndex的变化,将其定义为BindableProperty并添加变更回调:
public static readonly BindableProperty SelectedIndexProperty = BindableProperty.Create( nameof(SelectedIndex), typeof(int), typeof(TabbedLayout), 0, propertyChanged: OnSelectedIndexChanged); public int SelectedIndex { get => (int)GetValue(SelectedIndexProperty); set => SetValue(SelectedIndexProperty, value); }
2. 为TabContentsProperty添加变更回调
修改TabContentsProperty的定义,加入propertyChanged参数,监听属性值的变化:
public static readonly BindableProperty TabContentsProperty = BindableProperty.Create( nameof(TabContents), typeof(View[]), typeof(TabbedLayout), null, BindingMode.OneWay, propertyChanged: OnTabContentsChanged); public View[] TabContents { get => (View[])GetValue(TabContentsProperty); set => SetValue(TabContentsProperty, value); }
3. 让控件实现INotifyPropertyChanged接口
自定义控件需要实现该接口,才能让DisplayedElement的变化被绑定系统感知:
public partial class TabbedLayout : ContentView, INotifyPropertyChanged { // 已有的BindableProperty定义... public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
4. 重构DisplayedElement为可通知属性
将原来的字段改为属性,并在值变化时触发PropertyChanged事件:
private View _displayedElement; public View DisplayedElement { get => _displayedElement; private set { if (_displayedElement != value) { _displayedElement = value; OnPropertyChanged(); } } }
5. 实现变更回调与更新逻辑
添加回调方法,在TabContents或SelectedIndex变化时更新DisplayedElement:
private static void OnTabContentsChanged(BindableObject bindable, object oldValue, object newValue) { var control = (TabbedLayout)bindable; control.UpdateDisplayedElement(); } private static void OnSelectedIndexChanged(BindableObject bindable, object oldValue, object newValue) { var control = (TabbedLayout)bindable; control.UpdateDisplayedElement(); } private void UpdateDisplayedElement() { if (TabContents != null && SelectedIndex >= 0 && SelectedIndex < TabContents.Length) { DisplayedElement = TabContents[SelectedIndex]; } else { DisplayedElement = null; } }
6. 初始化时触发一次更新
在控件构造函数中调用更新方法,确保初始状态下DisplayedElement能正确赋值:
public TabbedLayout() { InitializeComponent(); UpdateDisplayedElement(); }
内容的提问来源于stack exchange,提问作者Silverman
相关产品推荐
相关产品推荐

