使用 XF 的 ReactiveUI:如何为自定义控件创建命令绑定器?
ReactiveUI with XF: How to create a Command Binder for a custom control?
我正在使用 Xamarin Forms 和 ReactiveUI 并尝试将来自自定义 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));
我正在使用 Xamarin Forms 和 ReactiveUI 并尝试将来自自定义 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));