首页 > 解决方案 > Xamarin Forms 自定义控件 BindableProperty propertyChanged not Firing

问题描述

在标签中使用项目符号点时,尝试设置一个非常简单的自定义控件来处理对齐。问题是,传递到自定义控件的 Spans 没有被识别,并且内容没有被呈现(并且 propertyChanged 函数没有被触发)。我错过了什么?

XAML:

<?xml version="1.0" encoding="UTF-8" ?>
<ContentView
    x:Class="Sample.Controls.BulletPointItem"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <ContentView.Content>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="10" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Label x:Name="BulletPoint" Text="&#x2022;" />
            <Label x:Name="LabelContent" Grid.Column="1" />
        </Grid>
    </ContentView.Content>
</ContentView>

代码隐藏:

[ContentProperty(nameof(Spans))]
public partial class BulletPointItem : ContentView
{
    // todo add bindable props for bullet point size and color, or even which character to use as the bullet point!
    public static readonly BindableProperty SpansProperty =
        BindableProperty.Create(
            nameof(Spans),
            typeof(ObservableCollection<Span>),
            typeof(BulletPointItem),
            new ObservableCollection<Span>(),
            BindingMode.OneWay,
            propertyChanged: OnSpansChanged);

    public BulletPointItem()
    {
        InitializeComponent();
    }

    public ObservableCollection<Span> Spans
    {
        get => (ObservableCollection<Span>)GetValue(SpansProperty);
        set => SetValue(SpansProperty, value);
    }

    private static void OnSpansChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var control = (BulletPointItem)bindable;
        var newSpansValue = (ObservableCollection<Span>)newValue;
        var formattedString = new FormattedString();

        if (control == null)
            return;

        if (newSpansValue == null || newSpansValue.Count == 0)
        {
            control.LabelContent.FormattedText = formattedString;
            return;
        }

        foreach (var span in newSpansValue)
        {
            formattedString.Spans.Add(span);
        }

        control.LabelContent.FormattedText = formattedString;
    }
}

尝试使用:

                            <controls:BulletPointItem>
                                <Span Text="Span 1." />
                                <Span FontAttributes="Italic" Text="Span 2." />
                            </controls:BulletPointItem>

标签: c#xamlxamarin.formscontrols

解决方案


原因: Spans是一个ObservableCollection。所以如果你想设置它的值,你应该在xaml中定义一个Collection,而不是直接列出Span。

解决方案:

我仍然建议您应该使用数据绑定,因为您已将其设置为BindableProperty

<controls:BulletPointItem BackgroundColor="LightBlue" Spans="{Binding Spans}">

在代码后面或 ViewModel

public static readonly BindableProperty SpansProperty =
        BindableProperty.Create(
        nameof(Spans),
        typeof(ObservableCollection<Span>),
        typeof(BulletPointItem),
        defaultValueCreator: bindable => new ObservableCollection<Span>(),
        propertyChanged: OnSpansChanged);
public ObservableCollection<Span> spans { get; set; }
spans = new ObservableCollection<Span>() {

      new Span(){Text="Span1" },
      new Span(){Text="Span2",FontAttributes=FontAttributes.Italic }
};

当您想从中添加或删除项目时,这将更加灵活。

更新

当您完全从 XAML 设置 Spans 时。它会将项目添加到Spans中。但是只有在您为其设置新值时才会调用OnSpansChanged方法。因此,您可以在方法CollectionChanged中处理逻辑。每次将新项目添加到 Spans 时都会调用它。

public BulletPointItem()
{
  InitializeComponent();
 
  Spans.CollectionChanged += Spans_CollectionChanged;
}

// this method will been invoked 2 times if you add two span in xaml
private void Spans_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
   var collection = sender as ObservableCollection<Span>;
  //...  
}

推荐阅读