使用 XF 的 ReactiveUI:如何为自定义控件创建命令绑定器?

ReactiveUI with XF: How to create a Command Binder for a custom control?

我正在使用 Xamarin FormsReactiveUI 并尝试将来自自定义 XF 控件的自定义命令绑定到我的视图模型。

this.BindCommand(ViewModel, vm => vm.HasChangesCommand, view => view.socket1);

我的控件 socket1 有一个依赖项 属性 ICommand 类型的命令。但是,我收到错误消息:

"System.Exception: Couldn't find a Command Binder for [ControlName]"

我想我必须为我的控件创建一个 Command Binder,但我找不到关于如何执行此操作的任何提示。
是否有关于如何在 Xamarin Forms 上为自定义控件创建 Command Binder 的文档?

编辑: 我已经看到添加第三个参数 "eventName" 它正在工作。但是我想知道是否有任何方法可以构建该 Command Binder,这样您就不需要在该调用中指定事件。

解决此问题的一种方法是改为对命令执行 OneWayBind。然后您的控件应处理该命令。根据需要设置启用并在需要时执行命令。

this.OneWayBind(ViewModel, vm => vm.HasChangesCommand, view => view.socket1.CommandName);

如果您希望能够将 BindCommand 与自定义视图一起使用,最简单的方法是在名为 Command。将 OneWayBind 用作 也很容易,尽管当您习惯使用 BindCommand 进行命令绑定时也很容易忘记这样做。

如果您想使用任何其他东西(事件、手势识别器等),您可以创建 ICreatesCommandBinding 的实现,它定义命令如何连接到目标对象。因此,您可以执行以下操作:

public class SocketControl : ContentView
{
    public static readonly BindableProperty MyCustomCommandProperty = BindableProperty.Create(
        nameof(MyCustomCommand),
        typeof(ICommand),
        typeof(SocketControl));

    public ICommand MyCustomCommand
    {
        get => (ICommand)GetValue(MyCustomCommandProperty);
        set => SetValue(MyCustomCommandProperty, value);
    }

    //...
}

public sealed class SocketControlCommandBinder : ICreatesCommandBinding
{
    public IDisposable BindCommandToObject(ICommand command, object target, IObservable<object> commandParameter)
    {
        var socket = (SocketControl)target;

        // get the original value so we can restore it when the binding is disposed...
        var originalValue = socket.GetValue(SocketControl.MyCustomCommandProperty);
        var disposable = Disposable.Create(() => socket.SetValue(SocketControl.MyCustomCommandProperty, originalValue));

        // set the control's command to the view-model's command
        socket.SetValue(SocketControl.MyCustomCommandProperty, command);

        return disposable;
    }

    public IDisposable BindCommandToObject<TEventArgs>(ICommand command, object target, IObservable<object> commandParameter, string eventName)
    {
        /// not shown here ...
        return Disposable.Empty;
    }

    /// <summary>
    /// Returns a positive integer when this class supports BindCommandToObject for this
    /// particular Type. If the method isn't supported at all, return a non-positive integer.
    /// When multiple implementations return a positive value, the host will use the one which
    /// returns the highest value. When in doubt, return '2' or '0'
    /// </summary>
    /// <param name="type">The type to query for.</param>
    /// <param name="hasEventTarget">If true, the host intends to use a custom event target.</param>
    /// <returns>A positive integer if BCTO is supported, zero or a negative value otherwise</returns>
    public int GetAffinityForObject(Type type, bool hasEventTarget)
    {
        return type.GetTypeInfo().IsAssignableFrom(typeof(SocketControl).GetTypeInfo()) ? 2 : 0;
    }
}

创建命令绑定器后,您需要注册它以便 ReactiveUI 知道如何使用它。在您的 app.xaml.cs(或您创建应用程序的任何地方):

Splat.Locator.CurrentMutable.Register(
    () => new SocketControlCommandBinder(),
    typeof(ReactiveUI.ICreatesCommandBinding));