MouseEnter计时事件重复自身

本文关键字:事件 MouseEnter | 更新日期: 2023-09-27 18:06:21

我有几个按钮,我希望当光标放置在它们上面一段指定的时间后,它们能做一些事情。在这种情况下,他们应该把他们的内容写在一个文本框里。

这是定时器:

private static System.Timers.Timer myTimer = 
        new System.Timers.Timer(1500);

这是按钮使用MouseEnter事件执行的方法:

private void keysHover(object sender, EventArgs e)
    {
        myTimer.Elapsed += delegate { keysHoverOK(sender); };
        myTimer.Enabled = true;
    }

当Timer结束时,会执行以下命令:

private void keysHoverOK(object sender)
    {
        this.Dispatcher.Invoke((Action)(() =>
        {
            txtTest.Text += (sender as System.Windows.Controls.Button).Content.ToString();
        }));
        myTimer.Enabled = false;
    }

我不太明白为什么会发生这种情况,但每次一个按钮完成定时器的keysHoverOK方法将写尽可能多的字符,因为有悬停。例如,如果我将鼠标悬停在按钮A上,它将写入A,如果我将鼠标悬停在按钮B上,它将写入AB,从而在文本框上写入AAB,以此类推,这个句子执行的次数与其他按钮执行keysHover方法的次数一样多,即使它们自己没有完成计时器,就像它们的内容被保存在某个地方。当然,现在我想让按钮做的就是写它们的内容,而且只写它们的内容。你知道我哪里做错了吗?

MouseEnter计时事件重复自身

你是指MouseEnter事件吗?我不知道WPF中有任何MouseOver事件。

如果没有一个好的、最小的完整的代码示例,就不可能确切地知道问题是什么。但是,根据您共享的少量代码和您的问题描述,似乎您的主要问题是您正在与多个控件共享单个Timer对象。当一个控件订阅Timer.Elapsed事件时,它永远不会取消订阅,这一事实加剧了这种情况。因此,如果另一个控件启用了计时器(也订阅了事件),当计时器间隔结束时,两个控件都会收到通知。

即使是单个控件也是有问题的,因为每次引发MouseEnter事件时,它都会订阅自己的事件。

解决方法是在鼠标离开控件边界或计时器间隔已过时禁用计时器并取消订阅事件。它可能看起来像这样:

private EventHandler _timerElapsedHandler;
// Subscribed to the MouseEnter event
private void keysHover(object sender, EventArgs e)
{
    _timerElapsedHandler = delegate { keysHoverOK(sender); };
    myTimer.Elapsed += _timerElapsedHandler;
    myTimer.Enabled = true;
}
// Subscribed to the MouseLeave event
private void keysLeave(object sender, EventArgs e)
{
    DisableTimer();
}
private void keysHoverOK(object sender)
{
    this.Dispatcher.Invoke((Action)(() =>
    {
        txtTest.Text += (sender as System.Windows.Controls.Button).Content.ToString();
    }));
    DisableTimer();
}
private void DisableTimer()
{
    myTimer.Elapsed -= _timerElapsedHandler;
    myTimer.Enabled = false;
    _timerElapsedHandler = null;
}

其他评论:

  • 你应该强制转换而不是使用as。只有当引用可以合法地与您要检查的类型不同时,才使用as。当它总是应该是您要检查的类型时,请使用强制类型转换。这样,如果你有一个bug,你会得到一个有意义的异常,而不仅仅是一些NullReferenceException
  • 上面的例子用最少的代码中断修复了这个问题。但说真的,我也会做出其他改变。例如,与其将委托存储在字段中,不如获取Content.ToString()值并存储该值。然后,我将使用命名方法,而不是为委托实例使用匿名方法,该方法仅使用存储的string值附加到Text属性。您可以通过名称订阅和取消订阅命名方法;委托类型做了正确的事情,即使它为订阅和退订使用了不同的委托实例。
  • 您可能考虑的另一个更改是为每个控件使用不同的Timer实例。这样你就不必在鼠标事件发生时订阅或退订;初始化时订阅即可。
  • 最后,特别是因为这是WPF代码,你真的应该考虑将附加的文本存储在一个可观察属性中(例如DependencyProperty,或实现INotifyPropertyChanged),并将其绑定到txtTest.Text属性,而不是直接操作该属性。

我猜当你说:

这是按钮使用MouseOver事件执行的方法:

你可能是指MouseEnter事件?

From what I see:

  • 你有一个中央计时器
  • 它会在你输入
  • 的第一个按钮上开始倒计时
  • 如果您在计时器结束前离开该按钮,则没有停止计时器
  • 您似乎只向事件添加委托而不删除任何

代码段myTimer.Elapsed += delegate { keysHoverOK(sender); };另一个委托添加到已经添加的委托列表中。它不会用一个委托来替换列表。

如果您在计时器结束之前离开按钮,您需要使用减号操作符(myTimer.Elapsed -= ....)从计时器经过的事件中删除委托,然后停止计时器。这里你有一个问题,你已经创建了一个匿名方法,所以你需要:-

  1. 删除匿名方法的研究

  • 研究删除所有事件处理程序
  • 或者最简单的方法

  • 停止并销毁任何正在运行的计时器,并在每次进入按钮时创建一个新的计时器实例。