自定义Xamarin.Forms控件中的绑定问题

时间:2017-09-08 09:29:01

标签: binding xamarin.forms

我对自定义控件上的绑定有一个奇怪的问题。我创建了一个自定义工具栏:

public partial class TopToolbar
{
    public static readonly BindableProperty BackCommandProperty =
        BindableProperty.Create(nameof(BackCommand), typeof(ICommand), typeof(TopToolbar), propertyChanged: BackCommandChanged);

    public ICommand BackCommand
    {
        get => (ICommand) GetValue(BackCommandProperty);
        set => SetValue(BackCommandProperty, value);
    }

    public TopToolbar()
    {
        InitializeComponent();
    }

    // for debug purposes only
    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();
        Debug.WriteLine(BindingContext);
    }

    // for debug purposes only
    private static void BackCommandChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        Debug.WriteLine($"old: {oldvalue}, new: {newvalue}");
    }
}


<?xml version="1.0" encoding="UTF-8"?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Core.Controls.TopToolbar"
             x:Name="TopToolbarView"
             BindingContext="{x:Reference TopToolbarView}"
             Orientation="Vertical">
            <StackLayout Orientation="Horizontal"
                         HorizontalOptions="FillAndExpand"
                <Image Source="{StaticResource Image.Toolbar.LeftArrow}">
                    <Image.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding BackCommand}" />
                    </Image.GestureRecognizers>
                </Image>
            </StackLayout>

</StackLayout>

我以这种方式在页面上使用它:

<pages:ContentPage.Content>
        <StackLayout BackgroundColor="{StaticResource LightGrayColor}"
                     Spacing="0"
                     Padding="0">
            <controls:TopToolbar Title="Master Data" BackCommand="{Binding MyBackCommand}" />
该页面的

BindingContext是一个视图模型:

public class MyCustomersPageModel
{
    public RelayCommand MyBackCommand { get; set; }

    public MyCustomersPageModel()
    {
        MyBackCommand = // command creation;
    }
}

从调试中我知道控件的BindingContext被正确设置(OnBindingContextChanged}到自身(TopToolbar对象)两次 - 第一次没有子视图时第二次在他们被添加之后。我已检查BindingContext是否在所有子控件中正确传播。

不幸的是BackCommand根本没有绑定。 TopToolbar.BackCommand的setter甚至不会被调用一次。

有趣的是,当我将控件上的BindingContext替换为直接在绑定中设置Souce时,一切正常:

<?xml version="1.0" encoding="UTF-8"?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Core.Controls.TopToolbar"
             x:Name="TopToolbarView"
             Orientation="Vertical">
            <StackLayout Orientation="Horizontal"
                         HorizontalOptions="FillAndExpand"
                <Image Source="{StaticResource Image.Toolbar.LeftArrow}">
                    <Image.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding Source={x:Reference TopToolbarView}, Path=BackCommand}" />
                    </Image.GestureRecognizers>
                </Image>
            </StackLayout>

</StackLayout>

任何线索我做错了什么?

1 个答案:

答案 0 :(得分:2)

它按预期工作。我建议使用Source

在第一种情况下,当您在BindingContext上设置TopToolbar给自己时,我会想象这将是事件序列:

  1. 构建自定义控件,BindingContext通过引用分配给自我。

  2. 创建了页面实例,并添加了控件。

  3. 当设置页面BindingContext时,通过属性继承的强大功能,所有它的子控件“BindingContext都会更新。

  4. 此时,您的自定义控件BindingContext仍在引用自己as value-propagation doesn't override manually set context

  5. 因此,绑定<controls:TopToolbar BackCommand="{Binding MyBackCommand}"失败,因为此绑定将尝试在MyBackCommand的绑定上下文中查找TopToolbar

  6. 但是,在第二种情况下,当您在Source命令上指定绑定TopToolbarTapped时,这应该是事件序列:

    1. 构建自定义控件,BindingContext为空。

    2. 创建了页面实例,并添加了控件。

    3. 当设置页面BindingContext时,通过属性继承的强大功能,所有它的子控件“BindingContext都会更新,包括您的自定义控件。

    4. 此时,您的自定义控件BindingContext现在引用MyCustomersPageModel。因此适当地设置<controls:TopToolbar BackCommand="{Binding MyBackCommand}"中的绑定。

    5. 现在Tapped绑定不关心BindingContext,因为它是明确指定的源,即父控件TopToolbar - 其BackCommand依次为绑定到视图模型的命令。因此,view-model命令现在绑定到手势识别器的Tapped命令。它有效!