diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj
index b0e9f84eb42..1ae55ca4c84 100644
--- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj
+++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj
@@ -358,6 +358,7 @@
+
@@ -546,6 +547,11 @@
IsNullOrEmptyStateTriggerPage.xaml
+
+
+ VoiceCommandsPage.xaml
+
+ WrapLayoutPage.xaml
@@ -1350,6 +1356,10 @@
DesignerMSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+ DesignerMSBuild:Compile
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Package.appxmanifest b/Microsoft.Toolkit.Uwp.SampleApp/Package.appxmanifest
index c9118ddbe4f..5cea81f4dcb 100644
--- a/Microsoft.Toolkit.Uwp.SampleApp/Package.appxmanifest
+++ b/Microsoft.Toolkit.Uwp.SampleApp/Package.appxmanifest
@@ -48,5 +48,6 @@
+
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs
new file mode 100644
index 00000000000..15f40edab1a
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Xaml.Interactivity;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Automation.Peers;
+using Windows.UI.Xaml.Automation.Provider;
+using Windows.UI.Xaml.Controls;
+
+namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
+{
+ public class ClickAction : DependencyObject, IAction
+ {
+ public object Execute(object sender, object parameter)
+ {
+ if (sender is Button btn && btn.IsEnabled)
+ {
+ var peer = new ButtonAutomationPeer(btn);
+ var invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
+ invokeProv?.Invoke();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs
new file mode 100644
index 00000000000..7e016bab64d
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Xaml.Interactivity;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
+{
+ public class SelectListBoxItemAction : DependencyObject, IAction
+ {
+ public Direction MoveDirection
+ {
+ get => (Direction)GetValue(MoveDirectionProperty);
+ set => SetValue(MoveDirectionProperty, value);
+ }
+
+ public static readonly DependencyProperty MoveDirectionProperty = DependencyProperty.Register(nameof(MoveDirection), typeof(Direction), typeof(SelectListBoxItemAction), new PropertyMetadata(default(Direction)));
+
+ public object Execute(object sender, object parameter)
+ {
+ if (sender is ListBox lb)
+ {
+ var selectedIndex = lb.SelectedIndex;
+ switch (MoveDirection)
+ {
+ case Direction.Up when selectedIndex > 0:
+ lb.SelectedIndex--;
+ break;
+ case Direction.Down when selectedIndex < lb.Items.Count - 1:
+ lb.SelectedIndex++;
+ break;
+ case Direction.First when lb.Items.Count > 0:
+ lb.SelectedIndex = 0;
+ break;
+ case Direction.Last when lb.Items.Count > 0:
+ lb.SelectedIndex = lb.Items.Count - 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ public enum Direction
+ {
+ Up,
+ Down,
+ First,
+ Last
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommands.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommands.png
new file mode 100644
index 00000000000..06aa81ad292
Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommands.png differ
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml
new file mode 100644
index 00000000000..fad852ff5ac
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs
new file mode 100644
index 00000000000..a1b03843ea1
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs
@@ -0,0 +1,95 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Microsoft.Toolkit.Uwp.UI.Behaviors;
+using Windows.UI.Popups;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class VoiceCommandsPage : Page
+ {
+ public VoiceCommandsPage()
+ {
+ this.InitializeComponent();
+ this.Loaded += this.VoiceCommandsPage_Loaded;
+ }
+
+ private async void VoiceCommandsPage_Loaded(object sender, RoutedEventArgs e)
+ {
+ if (VoiceCommandTrigger.SpeechRecognizer is null)
+ {
+ VoiceCommandTrigger.SpeechRecognizer = await WindowsMediaSpeechRecognizer.CreateAsync(Window.Current);
+ }
+ }
+
+ private void ButtonAdd_Click(object sender, RoutedEventArgs e)
+ {
+ MoveItem(listBoxAvailable, listBoxSelected);
+ }
+
+ private void MoveItem(ListBox from, ListBox to)
+ {
+ var selectedIndex = from.SelectedIndex;
+ var selectedItem = from.SelectedItem;
+ if (selectedIndex > -1)
+ {
+ from.Items.RemoveAt(selectedIndex);
+ from.SelectedIndex = Math.Min(selectedIndex, from.Items.Count - 1);
+ to.Items.Add(selectedItem);
+ to.SelectedIndex = to.Items.Count - 1;
+ }
+ }
+
+ private void ButtonDelete_Click(object sender, RoutedEventArgs e)
+ {
+ MoveItem(listBoxSelected, listBoxAvailable);
+ }
+
+ private void ButtonAppend_Click(object sender, RoutedEventArgs e)
+ {
+ if (!string.IsNullOrWhiteSpace(textBoxExtraItem.Text))
+ {
+ listBoxSelected.Items.Add(new ListBoxItem
+ {
+ Content = textBoxExtraItem.Text
+ });
+ listBoxSelected.SelectedIndex = listBoxSelected.Items.Count - 1;
+ textBoxExtraItem.Text = string.Empty;
+ }
+ }
+
+ public void WhatCanISay()
+ {
+ string content = "You can speak the following voice commands: \r\n" +
+ "- What can I say\r\n" +
+ "- Help \r\n" +
+ "- Add\r\n" +
+ "- Delete\r\n" +
+ "- Move avaiable up\r\n" +
+ "- Move avaiable down\r\n" +
+ "- Move avaiable to First\r\n" +
+ "- Move avaiable to Last\r\n" +
+ "- Move selected up\r\n" +
+ "- Move selected down\r\n" +
+ "- Move selected to First\r\n" +
+ "- Move selected to Last";
+ _ = new MessageDialog(content, "What can I Say").ShowAsync();
+ }
+
+ private void ToggleListning_Toggled(object sender, RoutedEventArgs e)
+ {
+ if (triggerListning is object)
+ {
+ triggerListning.Text = toggleListning.IsOn ? "Voice Off" : "Voice On";
+ actionListning.Value = !toggleListning.IsOn;
+ }
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json
index 5b356756f2d..464eb217664 100644
--- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json
@@ -864,7 +864,7 @@
{
"Name": "ViewportBehavior",
"Type": "ViewportBehaviorPage",
- "Subcategory": "Systems",
+ "Subcategory": "Behaviors",
"About": "Behavior for listening element enter or exit viewport",
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Behaviors",
"CodeFile": "ViewportBehaviorCode.bind",
@@ -875,7 +875,7 @@
{
"Name": "AutoFocusBehavior",
"Type": "AutoFocusBehaviorPage",
- "Subcategory": "Systems",
+ "Subcategory": "Behaviors",
"About": "Behavior to automatically set the focus on a control when it loads",
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Behaviors/",
"XamlCodeFile": "AutoFocusBehaviorXaml.bind",
@@ -885,13 +885,22 @@
{
"Name": "FocusBehavior",
"Type": "FocusBehaviorPage",
- "Subcategory": "Systems",
+ "Subcategory": "Behaviors",
"About": "Behavior to automatically set the focus on the first control which accepts it",
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Behaviors/",
"CodeFile": "FocusBehaviorXaml.bind",
"Icon": "/Assets/Helpers.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/FocusBehavior.md"
},
+ {
+ "Name": "VoiceCommands",
+ "Type": "VoiceCommandsPage",
+ "Subcategory": "Behaviors",
+ "About": "Behavior to trigger Actions using a voice command",
+ "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Behaviors/",
+ "Icon": "/SamplePages/VoiceCommands/VoiceCommands.png",
+ "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/FocusBehavior.md"
+ },
{
"Name": "Markdown Parser",
"Type": "MarkdownParserPage",
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs
new file mode 100644
index 00000000000..ddfe3d18c06
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Toolkit.Uwp.UI.Behaviors
+{
+ ///
+ /// Service used to report a recognized speech result
+ ///
+ public interface ISpeechRecognitionResult
+ {
+ ///
+ /// Gets the Text of the recognized speech
+ ///
+ string Text { get; }
+
+ ///
+ /// Gets the RawConfidence of the recognized speech
+ ///
+ double RawConfidence { get; }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs
new file mode 100644
index 00000000000..ecaf111225f
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Toolkit.Uwp.UI.Behaviors
+{
+ ///
+ /// Service used to recongize speech
+ ///
+ public interface ISpeechRecognizer
+ {
+ ///
+ /// Occurs when a speech is recognized
+ ///
+ event RecognizedEventHandler Recognized;
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs
new file mode 100644
index 00000000000..c8a995ff810
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.Toolkit.Uwp.UI.Behaviors
+{
+ ///
+ /// used to report the recognized speech result.
+ ///
+ public class RecognizedEventArgs : EventArgs
+ {
+ ///
+ /// Gets the result of a recognized speech
+ ///
+ public ISpeechRecognitionResult Result { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The result which is speech recognized
+ public RecognizedEventArgs(ISpeechRecognitionResult result)
+ {
+ this.Result = result;
+ }
+ }
+
+ ///
+ /// The Delegate for a Recognized Event.
+ ///
+ /// Sender ThemeListener
+ /// The event arguments.
+ public delegate void RecognizedEventHandler(ISpeechRecognizer sender, RecognizedEventArgs e);
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs
new file mode 100644
index 00000000000..358a1553679
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Toolkit.Uwp.UI.Behaviors
+{
+ ///
+ /// This class reports a recognized speech
+ ///
+ public class SpeechRecognitionResult : ISpeechRecognitionResult
+ {
+ ///
+ /// Gets the Text of the recognized speech
+ ///
+ public string Text { get; }
+
+ ///
+ /// Gets the RawConfidence of the recognized speech
+ ///
+ public double RawConfidence { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// the speech recognized text
+ /// the rawConfidence of recognized speech
+ public SpeechRecognitionResult(string text, double rawConfidence)
+ {
+ Text = text;
+ RawConfidence = rawConfidence;
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs
new file mode 100644
index 00000000000..44f116eee94
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs
@@ -0,0 +1,156 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.Xaml.Interactivity;
+using Windows.Gaming.Input.ForceFeedback;
+using Windows.UI.Core;
+using Windows.UI.Xaml;
+
+namespace Microsoft.Toolkit.Uwp.UI.Behaviors
+{
+ ///
+ /// A behavior that listens spoken voice commands and executes its actions when that event is fired.
+ ///
+ public class VoiceCommandTrigger : Trigger
+ {
+ private static readonly Dictionary> _triggers = new Dictionary>(StringComparer.InvariantCultureIgnoreCase);
+ private static ISpeechRecognizer _speechRecognizer;
+
+ ///
+ /// Gets or sets the SpeechRecognizer object used to recongize the voice commands
+ ///
+ public static ISpeechRecognizer SpeechRecognizer
+ {
+ get => _speechRecognizer;
+ set
+ {
+ if (value != _speechRecognizer)
+ {
+ if (_speechRecognizer is object)
+ {
+ _speechRecognizer.Recognized -= SpeechRecognizer_Recognized;
+ }
+
+ _speechRecognizer = value;
+ if (_speechRecognizer is object)
+ {
+ _speechRecognizer.Recognized += SpeechRecognizer_Recognized;
+ }
+ }
+ }
+ }
+
+ private static void SpeechRecognizer_Recognized(ISpeechRecognizer sender, RecognizedEventArgs e)
+ {
+ Debug.WriteLine(e.Result.Text);
+ if (_triggers.TryGetValue(e.Result.Text, out var list))
+ {
+ foreach (var trigger in list)
+ {
+ _ = trigger.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ if (trigger.IsEnabled)
+ {
+ Interaction.ExecuteActions(trigger.AssociatedObject, trigger.Actions, e);
+ }
+ });
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the spoken Text to trigger the Actions
+ ///
+ ///
+ /// Use the | to seperate alternative texts
+ ///
+ public string Text
+ {
+ get => (string)GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(VoiceCommandTrigger), new PropertyMetadata(default(string), OnTextPropertyChanged));
+
+ ///
+ /// Gets or sets a value indicating whether the VoiceCommand is enabled. Default is true
+ ///
+ public bool IsEnabled
+ {
+ get => (bool)GetValue(IsEnabledProperty);
+ set => SetValue(IsEnabledProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register(nameof(IsEnabled), typeof(bool), typeof(VoiceCommandTrigger), new PropertyMetadata(true));
+
+ private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is VoiceCommandTrigger source)
+ {
+ var newValue = (string)e.NewValue;
+ var oldValue = (string)e.OldValue;
+ if (!string.IsNullOrEmpty(oldValue))
+ {
+ source.Remove(oldValue);
+ }
+
+ if (!string.IsNullOrEmpty(newValue))
+ {
+ source.Add(newValue);
+ }
+ }
+ }
+
+ ///
+ protected override void OnDetaching()
+ {
+ Remove(Text);
+ base.OnDetaching();
+ }
+
+ private void Add(string text)
+ {
+ foreach (var item in text.Split('|'))
+ {
+ if (_triggers.TryGetValue(item, out var list))
+ {
+ list.Add(this);
+ }
+ else
+ {
+ list = new List()
+ {
+ this
+ };
+ _triggers[item] = list;
+ }
+ }
+ }
+
+ private void Remove(string text)
+ {
+ foreach (var item in text.Split('|'))
+ {
+ if (_triggers.TryGetValue(item, out var list))
+ {
+ list.Remove(this);
+ if (list.Count == 0)
+ {
+ _triggers.Remove(item);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs
new file mode 100644
index 00000000000..c982cb18d4f
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs
@@ -0,0 +1,99 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using Windows.Globalization;
+using Windows.Media.SpeechRecognition;
+using Windows.UI.Core;
+using Windows.UI.Xaml;
+
+namespace Microsoft.Toolkit.Uwp.UI.Behaviors
+{
+ ///
+ /// This class used the Windows to recognize speech for the
+ ///
+ public class WindowsMediaSpeechRecognizer : ISpeechRecognizer
+ {
+ private readonly SpeechRecognizer _speechRecognizer;
+
+ private WindowsMediaSpeechRecognizer(SpeechRecognizer speechRecognizer)
+ {
+ _speechRecognizer = speechRecognizer;
+ }
+
+ ///
+ /// Creates a which can be used for a
+ ///
+ /// The is used for triggering the Activated event. This will restart the speech recongition.
+ /// The created
+ public static async Task CreateAsync(Window window)
+ {
+ SpeechRecognizer sr = null;
+ foreach (var item in Windows.System.UserProfile.GlobalizationPreferences.Languages)
+ {
+ var language = new Language(item);
+ try
+ {
+ sr = new SpeechRecognizer(language);
+ break;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ }
+ }
+
+ if (sr is null)
+ {
+ sr = new SpeechRecognizer();
+ }
+
+ var d = new WindowsMediaSpeechRecognizer(sr);
+ Debug.WriteLine($"SpeechRecognizer Language: {sr.CurrentLanguage.DisplayName}");
+
+ sr.ContinuousRecognitionSession.AutoStopSilenceTimeout = TimeSpan.MaxValue;
+ await sr.CompileConstraintsAsync();
+ sr.ContinuousRecognitionSession.ResultGenerated += d.ContinuousRecognitionSession_ResultGenerated;
+ try
+ {
+ await sr.ContinuousRecognitionSession.StartAsync();
+ window.Activated += d.Window_Activated;
+ return d;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private void Window_Activated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
+ {
+ if (e.WindowActivationState == CoreWindowActivationState.CodeActivated && _speechRecognizer.State == SpeechRecognizerState.Idle)
+ {
+ _ = _speechRecognizer.ContinuousRecognitionSession.StartAsync();
+ }
+ }
+
+ private void ContinuousRecognitionSession_ResultGenerated(SpeechContinuousRecognitionSession sender, SpeechContinuousRecognitionResultGeneratedEventArgs args)
+ {
+ OnRecognized(new RecognizedEventArgs(new SpeechRecognitionResult(args.Result.Text, args.Result.RawConfidence)));
+ }
+
+ ///
+ /// Occurs when a speech is recognized
+ ///
+ public event RecognizedEventHandler Recognized;
+
+ ///
+ /// Called when speech is recognized
+ ///
+ /// used to report the
+ protected virtual void OnRecognized(RecognizedEventArgs args)
+ {
+ Recognized?.Invoke(this, args);
+ }
+ }
+}