我对自定义控件上的绑定有一个奇怪的问题。我创建了一个自定义工具栏:
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>
任何线索我做错了什么?
答案 0 :(得分:2)
它按预期工作。我建议使用Source
。
在第一种情况下,当您在BindingContext
上设置TopToolbar
给自己时,我会想象这将是事件序列:
构建自定义控件,BindingContext
通过引用分配给自我。
创建了页面实例,并添加了控件。
当设置页面BindingContext
时,通过属性继承的强大功能,所有它的子控件“BindingContext
都会更新。
此时,您的自定义控件BindingContext
仍在引用自己as value-propagation doesn't override manually set context。
因此,绑定<controls:TopToolbar BackCommand="{Binding MyBackCommand}"
失败,因为此绑定将尝试在MyBackCommand
的绑定上下文中查找TopToolbar
。
但是,在第二种情况下,当您在Source
命令上指定绑定TopToolbar
为Tapped
时,这应该是事件序列:
构建自定义控件,BindingContext
为空。
创建了页面实例,并添加了控件。
当设置页面BindingContext
时,通过属性继承的强大功能,所有它的子控件“BindingContext
都会更新,包括您的自定义控件。
此时,您的自定义控件BindingContext
现在引用MyCustomersPageModel
。因此适当地设置<controls:TopToolbar BackCommand="{Binding MyBackCommand}"
中的绑定。
现在Tapped
绑定不关心BindingContext
,因为它是明确指定的源,即父控件TopToolbar
- 其BackCommand
依次为绑定到视图模型的命令。因此,view-model命令现在绑定到手势识别器的Tapped
命令。它有效!