上一篇我们提到如何让“讲述人”读出自定义的CanReadGrid,但“讲述人”仍然无法识别CanReadGrid上绑定的Command。XAML代码如下:

    <StackPanel>
<TextBlock Text="{x:Bind Title,Mode=OneWay}" Foreground="White"></TextBlock>
<local:CanReadGrid Background="Red" AutomationProperties.Name="Can read gird" Height="100">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Tapped">
<Core:InvokeCommandAction Command="{x:Bind ChangeTitleCommand}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</local:CanReadGrid>
</StackPanel>

  我们可以看到通过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的依赖属性(Dependency Property):

public System.Windows.Input.ICommand Command { get; set; }
Member of Windows.UI.Xaml.Controls.Primitives.ButtonBase

  而我们自定义的CanReadGrid,则是通过附加属性(Attached Property)来获得绑定Command的能力。

  附件属性也是一种特殊的依赖属性,二者殊归同路。既然Button通过依赖属性可以做到的事情,附加属性一样可以完成。

  想要弄明白Button的Command是如何被调用的,最简单的办法就是去查看源码呗:

    public class 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>
        protected override string 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>
        protected override 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>
        public override object GetPattern(PatternInterface patternInterface)
        {
            if (patternInterface == PatternInterface.Invoke)
            {
                return this;
            }
            return base.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())
            {
                throw new ElementNotEnabledException();
            }
            base.Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param)
            {
                ((Button)base.Owner).AutomationButtonBaseClick();
                return null;
            }), null);
        }
    }

  果不其然发现了上一篇我们提到的GetClassNameCore,GetAutomationControlTypeCore,GetPattern三个方法。另外还有一个奇怪的void IInvokeProvider.Invoke()。这货看名字也能猜出来是干啥的啦,这货竟然去调了Button类里的Click方法……

  知道真相的我眼泪流出来……搞啥呢,那我在这里调一下Command.Execute不就成了!

  首先我们给CanReadGrid添加ExecuteCommand方法,该方法通过DependencyObject的GetValue方法一层层拿到Command,然后执行Execute。

    public class CanReadGrid : Grid
{
protected override AutomationPeer OnCreateAutomationPeer()
{
return new GridAutomationPeer((this));
} public void ExecuteCommand()
{
var behaviors = Interaction.GetBehaviors(this);
var actions = behaviors[].GetValue(EventTriggerBehavior.ActionsProperty) as ActionCollection;
var command = actions[].GetValue(InvokeCommandAction.CommandProperty) as ICommand;
command.Execute(null);
}
}

  第二步就是完善GridAutomatioPeer,这里需要注意的是IInvokeProvider这个接口,通过Button的源码推测具有Action的控件需要实现这个接口的Invoke方法来执行操作。我们也是在Invoke方法里来调用CanReadGrid类里的ExecuteCommand方法。

    public class GridAutomationPeer : FrameworkElementAutomationPeer, IInvokeProvider
{
public GridAutomationPeer(Grid owner)
: base(owner)
{ } public async void Invoke()
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
((CanReadGrid)base.Owner).ExecuteCommand();
});
} protected override object GetPatternCore(PatternInterface patternInterface)
{
if (patternInterface == PatternInterface.Invoke)
{
return this;
} return null;
}
}

  大功告成!开启“讲述人”模式来验证成果吧!

  结尾插播个广告,本篇内容100%原创,当时俺翻烂了Google的搜索页面、中英文各种blog也木有讲如何让“讲述人”调用Command,逼的俺自由发挥啊。特地写了这篇造福全人类,各位不要吝啬点个推荐哦,虽然“讲述人”并没有什么卵用……

UWP开发砸手机系列(二)—— “讲述人”识别自定义控件Command的更多相关文章

  1. UWP开发砸手机系列(一)—— Accessibility

    因为今天讨论的内容不属于入门系列,所以我把标题都改了.这个啥Accessibility说实话属于及其蛋疼的内容,即如何让视力有障碍的人也能通过声音来使用触屏手机……也许你这辈子也不会接触,但如果有一天 ...

  2. 使用 PySide2 开发 Maya 插件系列二:继承 uic 转换出来的 py 文件中的类 Ui_Form

    使用 PySide2 开发 Maya 插件系列二:继承 uic 转换出来的 py 文件中的类 Ui_Form 开发环境: Wing IDE 6.1 步骤1: 打开 Wing IDE,创建一个新的 pr ...

  3. MUI框架开发HTML5手机APP(二)--页面跳转传值&底部选项卡切换

      概 述 JRedu 在上一篇博客中,我们学习了如何使用Hbuilder创建一个APP,同时如何使用MUI搭建属于自己的第一款APP,没有学习的同学可以戳链接学习: http://www.cnblo ...

  4. [UWP开发]处理手机后退事件

    众所周知,uwp程序是一套代码,可以run在不同的平台上.但是不同的设备肯定有其独特之处,所以针对这些独特之处,必须用“独特的代码”来处理. 所以微软提供了一系列的拓展类库来实现这种特殊处理. 如上图 ...

  5. Win10 UWP开发:摄像头扫描二维码/一维码功能

    这个示例演示整合了Aran和微软的示例,无需修改即可运行. 支持识别,二维码/一维码,需要在包清单管理器勾选摄像头权限. 首先右键项目引用,打开Nuget包管理器搜索安装:ZXing.Net.Mobi ...

  6. UWP开发入门(十二)——神器Live Visual Tree

    很久以前,我们就有Snoop这样的工具实时修改.查看正在运行的WPF程序,那时候调个样式,修改个模板,相当滋润.随着历史的车轮陷进WP的泥潭中,无论WP7的Silverlight还是WP8.1的run ...

  7. 步步为营 SharePoint 开发学习笔记系列总结

    转:http://www.cnblogs.com/springyangwc/archive/2011/08/03/2126763.html 概要 为时20多天的sharepoint开发学习笔记系列终于 ...

  8. 使用 PySide2 开发 Maya 插件系列 总览

    使用 PySide2 开发 Maya 插件系列 总览 使用 PySide2 开发 Maya 插件系列一:QT Designer 设计GUI, pyside-uic 把 .ui 文件转为 .py 文件 ...

  9. 【Qt编程】基于Qt的词典开发系列<十二>调用讲述人

    我们知道,win7系统自带有讲述人,即可以机器读出当前内容,具体可以将电脑锁定,然后点击左下角的按钮即可.之前在用Matlab写扫雷游戏的时候,也曾经调用过讲述人来进行游戏的语音提示.具体的Matla ...

随机推荐

  1. oracle忘记sys及system密码

    一.忘记除SYS.SYSTEM用户之外的用户的登录密码. 用SYS (或SYSTEM)用户登录. CONN SYS/PASS_WORD AS SYSDBA; 使用如下语句修改用户的密码. ALTER ...

  2. MPI 集合通信函数 MPI_Scatterv(),MPI_Gatherv(),MPI_Allgatherv(),MPI_Alltoall(),MPI_Alltoallv(),MPI_Alltoallw()

    ▶ 函数 MPI_Scatterv() 和 MPI_Gatherv() .注意到函数 MPI_Scatter() 和 MPI_Gather() 只能向每个进程发送或接受相同个数的元素,如果希望各进程获 ...

  3. UCI一位搞theory的教授列的一些数学方面的比较趣味性的网页

    http://www.ics.uci.edu/~eppstein/recmath.html 据说是recreational的,没具体看,先收藏下来.

  4. 单片机(TTL)与电脑RS232接口

    2010年11月28日 21:38 1.先介绍电脑上与单片机进行通讯的接口的名称 (1)一般是用电脑串口来进行通讯的,平常大家说的电脑的串口是指台式电脑主机后面的九针接口,如下图 ‍这个接口有个专业的 ...

  5. 27.OGNL与ValueStack(VS)-获取Stack Context中的信息

    转自:https://wenku.baidu.com/view/84fa86ae360cba1aa911da02.html 我们知道,除了可以从值栈中获取信息,还可以从Stack Context中获取 ...

  6. apache启动不了, 查找错误

    今天apache启动不了, 本来以为是端口冲突, 用 cmd-> netstat -aon|findstr "80"  或 tasklist|findstr "80 ...

  7. DB2 数据类型转换

    db2数据类型转换: 1.CAST() CAST (<expression> AS <data_ type>[ length ]) 2.CONVERT() CONVERT (& ...

  8. Git--时光穿梭机之删除文件06

    在Git中,删除也是一个修改操作,我们实际操作一下吧,先添加一个文件test.txt到Git并且提交 $ git add test.txt $ git commit -m "add test ...

  9. 安装nvidia driver

    ubuntu16.04 下载地址 http://www.nvidia.com/Download/index.aspx dpkg -i nvidia-diag-driver-local-repo-ubu ...

  10. 49. Group Anagrams (string, HashTable)

    Given an array of strings, group anagrams together. For example, given: ["eat", "tea& ...