我们可以看到通过Behaviors绑定了Command,在Tapped事件发生时触发ChangeTitleCommand。
我们再来对比一下系统控件Button的写法:
<Button Command="{x:Bind ChangeTitleCommand}">I am Button</Button>
在“讲述人”模式下,点击上面这个Button按钮,“讲述人”除了会念出“I am Button Button.”这句话以外,还会补充一句“Double tap to activate.”这时双击Button将会触发ChangeTitleCommand。
其中不一样的地方无非就是Button自带有名为“Command”的,类型为ICommand的依赖属性(DependencyProperty):
public System.Windows.Input.ICommand Command { get; set; }
Member of Windows.UI.Xaml.Controls.Primitives.ButtonBase
而我们自定义的CanReadGrid,则是通过附加属性(Attached Property)来获得绑定Command的能力。
附件属性也是一种特殊的依赖属性,二者殊归同路。既然Button通过依赖属性可以做到的事情,附加属性一样可以完成。
想要弄明白Button的Command是如何被调用的,最简单的办法就是去查看源码呗:
publicclass ButtonAutomationPeer : ButtonBaseAutomationPeer, IInvokeProvider
{
///<summary>Initializes a new instance of the <see cref="T:System.Windows.Automation.Peers.ButtonAutomationPeer" /> class.</summary> ///<param name="owner">The element associated with this automation peer.</param> public ButtonAutomationPeer(Button owner) : base(owner)
{
}
///<summary>Gets the name of the control that is associated with this UI Automation peer.</summary> ///<returns>A string that contains "Button".</returns> protectedoverridestring GetClassNameCore()
{
return"Button";
}
///<summary>Gets the control type of the element that is associated with the UI Automation peer.</summary> ///<returns> /// <see cref="F:System.Windows.Automation.Peers.AutomationControlType.Button" />.</returns> protectedoverride AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Button;
}
///<summary>Gets the object that supports the specified control pattern of the element that is associated with this automation peer.</summary> ///<returns>If <paramref name="patternInterface" /> is <see cref="F:System.Windows.Automation.Peers.PatternInterface.Invoke" />, this method returns a this pointer, otherwise this method returns null.</returns> ///<param name="patternInterface">A value in the enumeration.</param> publicoverrideobject GetPattern(PatternInterface patternInterface)
{
if (patternInterface == PatternInterface.Invoke)
{
returnthis;
}
returnbase.GetPattern(patternInterface);
}
///<summary>This type or member supports the Windows Presentation Foundation (WPF) infrastructure and is not intended to be used directly from your code.</summary> void IInvokeProvider.Invoke()
{
if (!base.IsEnabled())
{
thrownew ElementNotEnabledException();
}
base.Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param)
{
((Button)base.Owner).AutomationButtonBaseClick();
returnnull;
}), null);
}
}
果不其然发现了上一篇我们提到的GetClassNameCore,GetAutomationControlTypeCore,GetPattern三个方法。另外还有一个奇怪的void IInvokeProvider.Invoke()。这货看名字也能猜出来是干啥的啦,这货竟然去调了Button类里的Click方法……
知道真相的我眼泪流出来……搞啥呢,那我在这里调一下Command.Execute不就成了!
首先我们给CanReadGrid添加ExecuteCommand方法,该方法通过DependencyObject的GetValue方法一层层拿到Command,然后执行Execute。
首先我们给CanReadGrid添加ExecuteCommand方法,该方法通过DependencyObject的GetValue方法一层层拿到Command,然后执行Execute。
publicclass CanReadGrid : Grid
{
protectedoverride AutomationPeer OnCreateAutomationPeer()
{
returnnew GridAutomationPeer((this));
}
publicvoid ExecuteCommand()
{
var behaviors = Interaction.GetBehaviors(this);
var actions = behaviors[0].GetValue(EventTriggerBehavior.ActionsProperty) as ActionCollection;
var command = actions[0].GetValue(InvokeCommandAction.CommandProperty) as ICommand;
command.Execute(null);
}
}
第二步就是完善GridAutomatioPeer,这里需要注意的是IInvokeProvider这个接口,通过Button的源码推测具有Action的控件需要实现这个接口的Invoke方法来执行操作。我们也是在Invoke方法里来调用CanReadGrid类里的ExecuteCommand方法。
publicclass GridAutomationPeer : FrameworkElementAutomationPeer, IInvokeProvider
{
public GridAutomationPeer(Grid owner)
: base(owner)
{
}
publicasyncvoid Invoke()
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() => {
((CanReadGrid)base.Owner).ExecuteCommand();
});
}
protectedoverrideobject GetPatternCore(PatternInterface patternInterface)
{
if (patternInterface == PatternInterface.Invoke)
{
returnthis;
}
returnnull;
}
}
大功告成!开启“讲述人”模式来验证成果吧!
开发者交流群:53078485,期待您的加入!