当前位置: 首页 > news >正文

UWP开发砸手机系列(二)—— “讲述人”识别自定义控件Command

原文: UWP开发砸手机系列(二)—— “讲述人”识别自定义控件Command

  上一篇我们提到如何让“讲述人”读出自定义的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方法,该方法通过DependencyObjectGetValue方法一层层拿到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[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方法。

    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,逼的俺自由发挥啊。特地写了这篇造福全人类,各位不要吝啬点个推荐哦,虽然“讲述人”并没有什么卵用……

 

 

 

 

 

 

 

 

相关文章:

  • Windows容器网络
  • 阿里云 云监控 安装和启动
  • diff命令
  • 42 Bing Search Engine Hacks
  • 配置新服务器 的一些 依赖库 php mysql nginx
  • 队列的基本概念
  • Dynamics 365/CRM 保存之后触发onchange
  • 设计模式---享元模式(DesignPattern_Flyweight)
  • 计算机宏
  • ES内部分片处理机制——Segment
  • 30 天精通 RxJS(18): Observable Operators - switchMap, mergeMap, concatMap
  • python数据结构之 set
  • Gartner全球IAAS市场报告:阿里云进入全球前三
  • 问题-MethodAddress返回NIL?MethodAddress与published的关系?
  • 【批处理学习笔记】第二十五课:间接传递
  • 《剑指offer》分解让复杂问题更简单
  • 78. Subsets
  • const let
  • IP路由与转发
  • JavaScript新鲜事·第5期
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • ng6--错误信息小结(持续更新)
  • Octave 入门
  • php面试题 汇集2
  • React Transition Group -- Transition 组件
  • React-生命周期杂记
  • SOFAMosn配置模型
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • 爱情 北京女病人
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 给第三方使用接口的 URL 签名实现
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 自定义函数
  • ​VRRP 虚拟路由冗余协议(华为)
  • ​插件化DPI在商用WIFI中的价值
  • (007)XHTML文档之标题——h1~h6
  • (3)选择元素——(17)练习(Exercises)
  • (C#)获取字符编码的类
  • (C语言)fread与fwrite详解
  • (pytorch进阶之路)扩散概率模型
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • **PHP分步表单提交思路(分页表单提交)
  • ./configure,make,make install的作用
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .NET Standard 支持的 .NET Framework 和 .NET Core
  • .NET 反射 Reflect
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • .net6使用Sejil可视化日志
  • .NET实现之(自动更新)