C#如何等待Panel的KeyDown事件触发后再继续执行后续代码
实现方案
你可以通过TaskCompletionSource<T>将事件回调转为可等待的Task,这是.NET中将事件驱动逻辑转为异步等待模式的标准实现方式。
核心实现步骤
- 首先在窗体类中定义
TaskCompletionSource字段,用于存储当前等待的按键任务信号源:
// 类字段,用于传递按键事件的结果到等待的异步方法 private TaskCompletionSource<Keys> _currentKeyWaitSource;
- 封装异步等待按键的方法,返回可等待的Task:
/// <summary> /// 等待用户在警告面板触发指定按键 /// </summary> /// <returns>用户按下的按键(仅返回Add或Escape)</returns> private async Task<Keys> WaitUserConfirmAsync() { // 每次调用都新建实例,避免旧事件的结果干扰 _currentKeyWaitSource = new TaskCompletionSource<Keys>(); panWarning.Show(); panWarning.Focus(); // 等待按键事件触发后返回结果 var pressedKey = await _currentKeyWaitSource.Task; panWarning.Hide(); return pressedKey; }
- 修改KeyDown事件的逻辑,按键触发后给信号源设置结果:
private void panWarning_KeyDown(object sender, KeyEventArgs e) { // 没有正在等待的任务时直接跳过 if (_currentKeyWaitSource == null || _currentKeyWaitSource.Task.IsCompleted) return; switch (e.KeyCode) { case Keys.Add: case Keys.Escape: // 设置任务结果,唤醒等待的异步方法继续执行 _currentKeyWaitSource.SetResult(e.KeyCode); e.Handled = true; break; } }
- 改造你的条目触发方法,按步骤插入等待逻辑即可:
private async Task TriggerItemAsync() { // 执行步骤1 ExecuteStep1(); // 等待用户按键 var inputKey = await WaitUserConfirmAsync(); if (inputKey == Keys.Escape) { // 处理用户中止的逻辑 RollbackOperation(); return; } // 执行步骤2 ExecuteStep2(); // 再次等待用户按键 inputKey = await WaitUserConfirmAsync(); if (inputKey == Keys.Escape) { RollbackOperation(); return; } // 执行步骤3 ExecuteStep3(); }
注意事项
- 要处理窗口关闭的异常场景,避免未完成的任务一直挂在内存中,可在窗体的
FormClosing事件中添加以下逻辑:
private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { _currentKeyWaitSource?.TrySetCanceled(); }
- 如果存在面板容易丢失焦点的问题,可以开启窗体的
KeyPreview = true属性,直接在窗体层面监听按键事件,避免焦点问题导致按键捕获失败。 - 异步方法不要用
void作为返回值(事件处理函数除外),统一用Task类型方便异常捕获和状态管理。
内容的提问来源于stack exchange,提问作者Cainnech
相关产品推荐
相关产品推荐

