UWP development smash mobile phone series (2) -- "narrator" recognizes custom controls Command and uwpcommand
In the previous article, we mentioned how to allow the "narrator" to read the custom CanReadGrid, but the "narrator" still cannot identify the Command bound to the CanReadGrid. The XAML code is as follows:
<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>
We can see that a Command is bound through Behaviors to trigger ChangeTitleCommand when a Tapped event occurs.
Let's compare the system control Button writing:
<Button Command="{x:Bind ChangeTitleCommand}">I am Button</Button>
In "talker" mode, click the Button above. The "talker" will not only read the "I am Button. in addition, the Double tap to activate will be added. "double-click Button will trigger ChangeTitleCommand.
The difference is that a Button has its own Dependency Property named "Command" and its type is ICommand ):
public System.Windows.Input.ICommand Command { get; set; } Member of Windows.UI.Xaml.Controls.Primitives.ButtonBase
The custom CanReadGrid obtains the ability to bind commands through the Attached Property.
The attachment attribute is also a special dependency attribute. Since the Button can do things through the dependency attribute, the additional attributes can also be done.
To understand how the Button Command is called, the simplest way is to view the source code:
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); } }
Naturally, we found the GetClassNameCore, GetAutomationControlTypeCore, and GetPattern methods we mentioned in the previous article. There is also a strange void IInvokeProvider. Invoke (). This product can also be guessed by name. This product actually calls the Click method in the Button class ......
My tears shed when I knew the truth ...... What are you doing? I can call Command. Execute here!
First, we add the ExecuteCommand method to CanReadGrid. This method obtains the Command layer by using the GetValue method of DependencyObject, and then runs 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); } }
The second step is to improve GridAutomatioPeer. here we need to pay attention to the IInvokeProvider interface. Through the source code of the Button, the control with Action needs to implement the Invoke method of this interface to execute the operation. We also call the ExecuteCommand method in the CanReadGrid class in the Invoke method.
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; } }
Success! Enable the "narrator" mode to verify the results!
An advertisement was inserted at the end. The content of this article was 100% original. At that time, I broke Google's search pages and various Chinese and English blogs. I also talked about how to make the "narrator" Call Command, let's make full use of it. I wrote this article for the benefit of all mankind. Please do not mean to give me some suggestions. Although there is no such thing as "narrator ......