C# 检测按钮单击后检测键关闭

本文关键字:检测 单击 按钮 | 更新日期: 2023-09-27 17:56:57

我想在按钮单击后按下特定键后执行一些操作。像这样:我单击按钮,然后按结束键,然后执行一些操作。

我尝试使用这个简单的代码,但它不起作用。

private void buttonStart_Click(object sender, EventArgs e)
    {
        KeyDown += OnKeyDown;         
    }
    private void OnKeyDown(object sender, KeyEventArgs keyEventArgs)
    {
        if (keyEventArgs.KeyCode == Keys.Return)
            label2.Text = "siala";
    }
}

C# 检测按钮单击后检测键关闭

您没有指定使用的是 Winforms 还是 WPF,
下面是 WPF 的示例,但也应该与 Winforms 非常相似:

bool canClick = true;
public MainWindow()
{
    InitializeComponent();
    TheBox.PreviewMouseDown += TheGrid_PreviewMouseDown;          
}
async void TheGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{            
    if (canClick)
    {
        canClick = false;
        TheBox.PreviewKeyDown += TheGrid_PreviewKeyDown;                
        await Task.Delay(5000);
        TheBox.PreviewKeyDown -= TheGrid_PreviewKeyDown;
        canClick = true;
    }
}
void TheGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
    Console.WriteLine("Button {0}", e.Key);
}

这里的想法是,您在以下情况下
注册一个事件处理程序PreviewKeyDown单击鼠标,然后在 5 秒后删除该处理程序。

这样,用户必须在单击鼠标后的 5 秒内输入密钥。
布尔值用于确保每次不会多次注册事件处理程序。

您还应该研究可能提供更清洁
的反应式扩展解决这个问题的方法。

我会考虑使用Microsoft的反应框架(Rx)(NuGet "Rx-Main","Rx-WinForms","Rx-WPF"),你可以非常清楚地描述你想要在单个LINQ查询中的内容。

Rx 允许您创建可以查询的事件流。

首先制作两个数据流 - 按钮单击和按键按下。

var clicks = Observable.FromEventPattern(
        h => this.button1.Click += h,
        h => this.button1.Click -= h);
var keydowns = Observable.Merge(
        Observable.FromEventPattern<KeyEventHandler, KeyEventArgs>(
            h => this.button1.KeyDown += h,
            h => this.button1.KeyDown -= h),
        Observable.FromEventPattern<KeyEventHandler, KeyEventArgs>(
            h => this.KeyDown += h,
            h => this.KeyDown -= h));

这是迄今为止最难理解的代码。好消息是你真的不需要想太多 - 它们甚至只是使用 Rx 完成的处理程序。

现在,这是您现在可以执行的非常好的查询:

var query =
    from c in clicks
    from kd in keydowns
        .Take(1)
        .TakeUntil(clicks)
    where kd.EventArgs.KeyCode == Keys.Return
    select kd;

这说"当我点击时,除非我得到第二次点击,否则只拿下 1 个键,但如果它碰巧是'返回',则只拿下那个键。

您要做的最后一步是订阅正在生成的值。

var subscription = query.Subscribe(kd => this.label1.Text = "siala");

您甚至可以更进一步,这样做:

var query =
    from c in clicks
    from kd in keydowns
        .Take(1)
        .TakeUntil(clicks)
        .TakeUntil(Observable.Timer(TimeSpan.FromSeconds(3.0)))
    where kd.EventArgs.KeyCode == Keys.Return
    select kd;

现在说"当我点击时,除非我得到第二次点击,否则只拿下 1 个键,或者如果 3 秒过去了,但只有在碰巧是'返回'的情况下才拿下那个键。

如果您希望订阅停止,只需致电subscription.Dispose();

Rx非常强大。

我假设按钮在单击后保持焦点,因此事件不会传播到窗体。您可以使用下面的代码来检查事件的触发位置

您使用的是WPF还是Winforms?

    private void button1_Click(object sender, EventArgs e)
    {
        this.KeyDown += Form1_KeyDown;
        this.button1.KeyDown += button1_KeyDown;
    }
    void button1_KeyDown(object sender, KeyEventArgs e)
    {
        MessageBox.Show("[BUTTON] PRE-KEY-CHECK"); e.Handled = false;
        if (e.KeyCode == Keys.F1)
        {
            MessageBox.Show("[BUTTON] POST-KEY-CHECK");
        }
    }
    void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        MessageBox.Show("[WINDOW] PRE-KEY-CHECK"); e.Handled = false;
        if(e.KeyCode == Keys.F1)
        {
            MessageBox.Show("[WINDOW] POST-KEY-CHECK");
        }
    }

编辑:可以找到该问题的解决方案 winforms中的键盘事件传播

基本上你必须设置this.KeyPreview=true;

编辑 2:仅允许事件注册一次

    Boolean isEventhandlerRegistered = false;
    private void button1_Click(object sender, EventArgs e)
    {
        if (!this.isEventhandlerRegistered)
        {
            this.PreviewKeyDown += Form1_PreviewKeyDown;
            this.isEventhandlerRegistered = true;
        }
    }
    void Form1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
        if (e.KeyCode == Keys.F1)
        {
            MessageBox.Show("[WINDOW] POST-KEY-CHECK");
        }
    }