在WPF客户端开发中,借助Windows系统自带的语音接口,我们可以让程序听懂用户说话,也能把文字念出来。核心类库位于System.Speech程序集,其中语音识别由SpeechRecognitionEngine负责,语音合成由SpeechSynthesizer实现。下面先看整体结构示意图。

一、环境准备与程序集引用
WPF默认不会引用语音相关组件,需要手动添加System.Speech程序集。在Visual Studio中右键项目选择添加引用,于程序集列表里勾选System.Speech即可。该类库从.NET Framework 3.0开始提供,在.NET 6及以上若使用Windows专用运行时也可通过NuGet安装对应的兼容包。
需要注意的是,语音识别引擎依赖系统已安装的语音识别语言包。中文用户应在控制面板的语音识别设置中确认中文识别模块已启用,否则代码运行时会抛出找不到识别引擎的异常。语音合成同样需要对应的中文语音库,通常Windows自带Microsoft Huihui等中文音色。
二、语音合成:让程序开口说话
SpeechSynthesizer的使用非常直接,创建实例后调用Speak或SpeakAsync方法就能播放文本。为了不阻塞UI线程,在WPF中应当使用异步方法,并在事件回调里更新界面状态。
下面的例子展示了点击按钮后播报一段欢迎语,并在播报完成时修改界面提示。我们通过StateChanged事件感知合成器状态,避免用户重复点击造成声音叠加。
using System.Speech.Synthesis;
using System.Windows;
namespace WpfSpeechDemo
{
public partial class MainWindow : Window
{
private SpeechSynthesizer synth;
public MainWindow()
{
InitializeComponent();
// 初始化合成器
synth = new SpeechSynthesizer();
synth.StateChanged += Synth_StateChanged;
}
private void Synth_StateChanged(object sender, StateChangedEventArgs e)
{
// 回到UI线程更新文本
Dispatcher.Invoke(() =>
{
StatusText.Text = e.State.ToString();
});
}
private void SpeakBtn_Click(object sender, RoutedEventArgs e)
{
// 异步播报,不卡界面
synth.SpeakAsync("欢迎使用语音助手,请问有什么可以帮您");
}
}
}
上述代码中的StatusText是界面上的TextBlock名称。SpeakAsync会在后台线程处理音频输出,StateChanged事件参数里的State可能是Speaking、Paused或Ready,方便我们做按钮禁用等交互控制。
如果希望调整语速、音量和音色,可以设置synth.Rate、synth.Volume以及通过synth.SelectVoice方法指定已安装的音色名称。这些属性都支持在播报前动态切换,适合做多角色语音提示。
三、语音识别:接收并解析用户指令
语音识别比合成稍复杂,因为需要预先定义语法。最简单的做法是使用DictationGrammar,它允许识别任意连续语句;但在实际业务中,用Choices配合GrammarBuilder限定指令集能显著提升准确率。
SpeechRecognitionEngine需要先调用SetInputToDefaultAudioDevice绑定麦克风,再加载语法并调用RecognizeAsync启动监听。识别结果在SpeechRecognized事件中返回,事件参数包含置信度与文本。
using System.Speech.Recognition;
using System.Windows;
namespace WpfSpeechDemo
{
public partial class MainWindow : Window
{
private SpeechRecognitionEngine recognizer;
public MainWindow()
{
InitializeComponent();
recognizer = new SpeechRecognitionEngine();
// 使用中文识别引擎
recognizer.SetInputToDefaultAudioDevice();
// 构建限定指令语法
Choices commands = new Choices();
commands.Add(new string[] { "打开文件", "保存数据", "关闭窗口" });
GrammarBuilder gb = new GrammarBuilder();
gb.Culture = new System.Globalization.CultureInfo("zh-CN");
gb.Append(commands);
Grammar grammar = new Grammar(gb);
recognizer.LoadGrammar(grammar);
recognizer.SpeechRecognized += Recognizer_SpeechRecognized;
recognizer.RecognizeAsync(RecognizeMode.Multiple);
}
private void Recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
Dispatcher.Invoke(() =>
{
ResultText.Text = "识别结果:" + e.Result.Text + " 置信度:" + e.Result.Confidence;
});
}
}
}
代码中RecognizeMode.Multiple表示持续监听,适合语音助手场景。若只想要单次识别,可改为Single并在完成后重新调用RecognizeAsync。置信度低于0.6的结果通常不可靠,可在事件里加判断忽略。
很多新手会把<input>这类标签名称和函数调用混淆,这里要说明SpeechRecognitionEngine是类,而LoadGrammar()是函数调用,不是标签。在WPF里界面元素才用标签描述,后台逻辑都是普通C#对象与方法。
四、UI绑定与异常处理
识别到的文本往往要驱动界面变化,例如根据指令切换页面。推荐把识别结果通过事件聚合或MVVM命令传递到ViewModel,保持后台音频逻辑与XAML解耦。
异常方面,麦克风被其他程序占用时会触发NoInputTimeout;用户未授权麦克风权限则引擎启动失败。建议在启动识别前用try-catch包裹,并提示用户检查系统隐私设置里的麦克风开关。
| 功能 | 主要类 | 常用方法 |
|---|---|---|
| 语音合成 | SpeechSynthesizer | SpeakAsync, SelectVoice |
| 语音识别 | SpeechRecognitionEngine | LoadGrammar, RecognizeAsync |
通过上面几个步骤,WPF程序就能完整具备语音交互能力。后续还可结合语义理解把识别文本转为业务命令,或把合成声音换成云端更自然的神经网络音色,但本地System.Speech方案足以覆盖大多数离线场景。
WPFSpeechRecognitionSpeechSynthesis修改时间:2026-08-04 15:36:35