Xamarin 可绑定行为的 "PropertyChanged Event" 能否直接处理其视图的 属性?

Can "PropertyChanged Event" of Xamarin bindable behavior directly handle property of its view?

我想用可绑定行为处理视图的属性。

是否有任何方法可以直接访问事件处理程序中连接的视图?

将 "AssociatedObject" 更改为静态会导致绑定问题。

我该怎么办?

public class CustNameBehavior : Behavior<Label>
{
    public Label AssociatedObject { get; private set; }

    public static readonly BindableProperty MyNameProperty = BindableProperty.Create(nameof(MyName), typeof(string), typeof(CustNameBehavior), null, BindingMode.OneWay, propertyChanged: OnMyNameChanged);
    private static void OnMyNameChanged(BindableObject bindable, object oldValue, object newValue)
    {
        CustNameBehavior target = (CustNameBehavior)bindable;

        if (target != null)
        {
            var value = (string)newValue;

            if (string.IsNullOrWhiteSpace(value))
            {
                AssociatedObject.Text = "Empty!";   => this is inaccessible. because of non static.
            }
            else
            {
                AssociatedObject.Text = value;   => this is inaccessible. because of non static.
            }
        }
    }

    protected override void OnAttachedTo(Label bindable)
    {
        base.OnAttachedTo(bindable);
        AssociatedObject = bindable;
        bindable.BindingContextChanged += Bindable_BindingContextChanged;
    }

    protected override void OnDetachingFrom(Label bindable)
    {
        base.OnDetachingFrom(bindable);
        bindable.BindingContextChanged -= Bindable_BindingContextChanged;
        AssociatedObject = null;
    }

    private void Bindable_BindingContextChanged(object sender, EventArgs e)
    {
        base.OnBindingContextChanged();
        BindingContext = AssociatedObject.BindingContext;
    }

    public string MyName
    {
        get => (string)GetValue(MyNameProperty);
        set
        {
            SetValue(MyNameProperty, value);
        }
    }
}

OnMyNameChanged 处理程序必须是静态的,因为 MyNameProperty 也是静态的(好吧,从技术上讲,这也许是可行的,但它会很脆弱并且会跳过箍)。无论如何,您希望 AssociatedObject 是非静态的,因为每个 CustNameBehavior 实例都需要一个,其他任何东西都可能不会按预期运行。

但是,您可以OnMyNameChanged访问非静态方法和属性,因为CustNameBehavior的实例是通过bindable传递的范围。此外,此实例通过 AssociatedObject 持有对 Label 的引用。因此,您可以通过 target 访问 AssociatedObject(已经转换为 CustNameBehaviorbindable),因此您的方法将变为

private static void OnMyNameChanged(BindableObject bindable, object oldValue, object newValue)
{
    CustNameBehavior target = (CustNameBehavior)bindable;

    if (target != null && target.AssociatedObject != null)
    {
        var value = (string)newValue;

        if (string.IsNullOrWhiteSpace(value))
        {
            target.AssociatedObject.Text = "Empty!"; 
        }
        else
        {
            target.AssociatedObject.Text = value;
        }
    }
}

请注意 我添加了一个检查 AssociatedObject 是否为 null,因为我假设 OnMyNameChanged 在行为之前被调用被绑定。

恕我直言,如果您将实例逻辑单独移动到一个方法中会更好

private static void OnMyNameChanged(BindableObject bindable, object oldValue, object newValue)
{
    CustNameBehavior target = (CustNameBehavior)bindable;
    target?.SetLabelName((string)newValue);
}

private void SetLabelName(string name)
{
    if(AssociatedObject != null)
    {
        AssociatedObject.Text = string.IsNullOrEmpty(name) ? "Empty!" : name;
    }
}

你还必须从OnAttachedTo设置AssociatedObject.Text,因为MyNameProperty暂时不会设置,这会导致AssociatedObject.Text不正在更新。您可以为此目的重用 SetLabelName,这样,您就不必重复检查字符串是否为空的逻辑。

protected override void OnAttachedTo(Label bindable)
{
    base.OnAttachedTo(bindable);
    AssociatedObject = bindable;
    SetLabelName((string)GetValue(MyNameProperty));
    bindable.BindingContextChanged += Bindable_BindingContextChanged;
}