Xamarin 自定义控件绑定不起作用

Xamarin Custom Control Binding Not Working

我创建了一个名为 Switch 的 ContentView,主要将此滑块作为自定义控件托管,这样我就可以重复使用打开和关闭的视觉状态。

ContentView 包含一个 Label(仅用于调试)和一个 Syncfusion Switch 以及一些 VisualStateManager 内容。 Label和Switch在后面的代码中绑定到BindableProperies,Switch使用EventToCommandBehavior,所以我怀疑这是一个Syncfusion问题。

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:d="http://xamarin.com/schemas/2014/forms/design"
         xmlns:core="clr-namespace:FEOD.Core;assembly=FEOD"
         xmlns:buttons="clr-namespace:Syncfusion.XForms.Buttons;assembly=Syncfusion.Buttons.XForms"
         mc:Ignorable="d"
         x:Class="FEOD.Views.Components.Switch">
<ContentView.Content>
    <StackLayout>
        <Label Text="{Binding LabelText}" TextColor="Black" />
        <buttons:SfSwitch VisualType="Cupertino" WidthRequest="50" HeightRequest="25">
            <buttons:SfSwitch.Behaviors>
                <core:EventToCommandBehavior EventName="StateChanged" Command="{Binding StateChangedCommand}"/>
            </buttons:SfSwitch.Behaviors>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup  x:Name="CommonStates">
                    <VisualState x:Name="On">
                        <VisualState.Setters>
                            <Setter Property="SwitchSettings">
                                <Setter.Value>
                                    <buttons:DefaultSwitchSettings 
                                    x:TypeArguments="buttons:OnState" 
                                    ThumbBorderColor="#039CDE" ThumbColor="#FFFFFF" 
                                    TrackBorderColor="#039CDE" TrackColor="#039CDE"/>
                                </Setter.Value>
                            </Setter>
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState x:Name="Off">
                        <VisualState.Setters>
                            <Setter Property="SwitchSettings">
                                <Setter.Value>
                                    <buttons:DefaultSwitchSettings 
                                    x:TypeArguments="buttons:OffState" 
                                    ThumbBorderColor="#E77C21" ThumbColor="#FFFFFF" 
                                    TrackBorderColor="#E77C21" TrackColor="#E77C21"/>
                                </Setter.Value>
                            </Setter>
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </buttons:SfSwitch>
    </StackLayout>
</ContentView.Content>

Bindable 背后的代码

using System.Windows.Input;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace FEOD.Views.Components
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Switch : ContentView
    {
        public static readonly BindableProperty StateChangedCommandProperty = BindableProperty.Create(
        propertyName: nameof(StateChangedCommand), 
            returnType:typeof(ICommand), 
            declaringType:typeof(Switch),
            defaultValue:default(ICommand),
            defaultBindingMode: BindingMode.OneWay
        );
            
        public ICommand StateChangedCommand
        {
            get => (ICommand) GetValue(StateChangedCommandProperty);
            set => SetValue(StateChangedCommandProperty, value);
        }

        public static readonly BindableProperty LabelTextProperty = BindableProperty.Create(nameof(LabelText), typeof(string), typeof(Switch), string.Empty, BindingMode.TwoWay);
        public string LabelText
        {
            get => (string) GetValue(LabelTextProperty);
            set => SetValue(LabelTextProperty, value);
        }

        public Switch()
        {
            InitializeComponent();
        }
    }
}

ContentView 托管在 ContentPage 中。它的两个属性设置如下:

<components:Switch Grid.Column="1" LabelText="MY LABel" StateChangedCommand="{Binding CreatorOrContributorFinderCommand}" />

其中 LabelText 设置为“MY LABel”,StateChangedCommand 设置为视图模型命令“CreatorOrContributorFinderCommand”。页面的 BindingContext 设置为视图模型的一个实例

public ICommand CreatorOrContributorFinderCommand { get; private set; }

private async Task OnCreatorOrContributorFinderChanged()
{
    var a = 1;
}

constructor has this line:
CreatorOrContributorFinderCommand = new AsyncCommand(OnCreatorOrContributorFinderChanged);

注意:AsyncCommand 来自 https://johnthiriet.com/mvvm-going-async-with-async-command

我不明白为什么 Label 和命令都没有做任何事情。调试期间,LabelText 未显示

当我滑动开关时,EventToCommandBehavior 中的这个断点显示没有命令与事件相关联。 null 命令阻止调用视图模型中的处理程序。

在对此感到困惑并浏览 EventToCommandBehavoir 源代码和数十个 Whosebug posting 之后,我终于找到了问题所在。

我发现有两点需要注意:

首先是 EventToCommandBehavoir 只需要一个 Xamarin.Forms.Command,而不是任何 ICommand。

第二个,非常感谢 post,ContentView 必须在其构造函数中包含这个 才能使命令工作 (虽然,不是其他类型,如文本)。

Content.BindingContext = this;

因为有多个 ICommand 实现(特别是当您需要 AsyncCommand 时很有用)并且 Labels 等无需设置即可正常工作 Content.BindingContext,因此很容易被 EventToCommandBehavior 混淆。