首页 > 解决方案 > 用于具有奇怪行为的 WPF 显示的计时器

问题描述

我在 WPF 中制作了一个带有几个按钮的面板。当鼠标进入某个按钮时,会出现新按钮,并在鼠标离开 1000 毫秒后消失。

但我有一个奇怪的行为:在某些情况下,1000 毫秒在每次使用后变得越来越短。

定时器事件

private void TimerEventProcessorForButtonA(Object myObject, EventArgs myEventArgs)
{
    _myTimerForButtonA.Stop(); 
    miniButton1.Visibility = System.Windows.Visibility.Hidden;
}

private void TimerEventProcessorForButtonB(Object myObject, EventArgs myEventArgs)
{
    _myTimerForButtonB.Stop(); 
    miniButton2.Visibility = System.Windows.Visibility.Hidden;
}

WaitTime 函数调用定时器:

public void WaitThisTimeAndHideMiniButton1(int givenTime)
{
    _myTimerForButtonA = new System.Windows.Forms.Timer();
    _myTimerButtonA.Tick += new EventHandler(TimerEventProcessorForForButtonA);
    _myTimerForForButtonA.Interval = givenTime;
    _myTimerForForButtonA.Start();
}

public void WaitThisTimeAndHideMiniButton2(int givenTime)
{
    _myTimerForButtonB = new System.Windows.Forms.Timer();
    _myTimerForButtonB.Tick += new EventHandler(TimerEventProcessorForForButtonB);
    _myTimerForForButtonB.Interval = givenTime;
    _myTimerForForButtonB.Start();
}

离开按钮时的事件:

private void buttonA_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
    Border button = sender as Border;
    button.Background = (SolidColorBrush)new BrushConverter().ConvertFromString(_colorOut);
    WaitThisTimeAndHideMiniButton1(1000); // hide minibuttons
}

private void buttonB_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
    Border button = sender as Border;
    button.Background = (SolidColorBrush) new BrushConverter().ConvertFromString(_colorOut);
    WaitThisTimeAndHideMiniButton2(1000);
}

如代码中所述,我有 buttonA 和 buttonB。当我输入 buttonA 时,出现 miniButton1 - 它会在鼠标离开事件后 1000 毫秒消失。显示 miniButton2 的 buttonB 也是如此。

如果我只进入和离开按钮B,一切都是正确的。与按钮 A 相同。 问题:如果我进入/离开按钮 A 和 B,那么这 1000 毫秒会越来越短。miniButton 的消失也出现在我离开按钮 A 和 B 之前。也发生在我离开按钮之前。

一切的行为就像计时器相互混淆一样。你知道怎么解决吗?

标签: c#.netwpf

解决方案


Timer每次触发事件时都会重新创建一个新MouseLeave事件,如果您碰巧在 1000 毫秒内触发了该事件两次,那么您将在前一次执行的事件Timer之前重新创建一个新事件,这将使前一次无限期运行。TickTimerTimer

以下是您MouseLeave在 1000 毫秒内触发两次事件时发生的情况:

1) MouseLeave: _myTimerForButtonA is assigned a new Timer instance (ie: Timer1), Tick event registered.
2) MouseLeave: _myTimerForButtonA is assigned a new Timer instance (ie: Timer2), Tick event registered.
3) Timer1.Tick event fires TimerEventProcessorForButtonA, whichs stops _myTimerForButtonA which points to Timer2.
4) Timer2.Tick event fires TimerEventProcessorForButtonA, whichs stops _myTimerForButtonA which points to Timer2 (and is already stopped).
5) Timer1.Tick event fires TimerEventProcessorForButtonA indefinitely, because Timer1 is not referenced anymore and no one will ever call `Stop` on it.

MouseEnter我能够通过停止事件计时器来修复您的代码

    private void TimerEventProcessorForButtonA(Object myObject, EventArgs myEventArgs)
    {
        Debug.WriteLine("TimerEventProcessorForButtonA");
        _myTimerForButtonA.Stop();
        _myTimerForButtonA.Dispose();
        miniButton1.Visibility = System.Windows.Visibility.Hidden;
    }

    private void TimerEventProcessorForButtonB(Object myObject, EventArgs myEventArgs)
    {
        Debug.WriteLine("** TimerEventProcessorForButtonB");
        _myTimerForButtonB.Stop();
        _myTimerForButtonB.Dispose();
        miniButton2.Visibility = System.Windows.Visibility.Hidden;
    }

    public void WaitThisTimeAndHideMiniButton1(int givenTime)
    {
        _myTimerForButtonA = new System.Windows.Forms.Timer();
        _myTimerForButtonA.Tick += new EventHandler(TimerEventProcessorForButtonA);
        _myTimerForButtonA.Interval = givenTime;
        _myTimerForButtonA.Start();
    }

    public void WaitThisTimeAndHideMiniButton2(int givenTime)
    {
        _myTimerForButtonB = new System.Windows.Forms.Timer();
        _myTimerForButtonB.Tick += new EventHandler(TimerEventProcessorForButtonB);
        _myTimerForButtonB.Interval = givenTime;
        _myTimerForButtonB.Start();
    }

    private void buttonA_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
    {
        Border button = sender as Border;
        button.Background = (SolidColorBrush)new BrushConverter().ConvertFromString(_colorOut);
        WaitThisTimeAndHideMiniButton1(1000); // hide minibuttons
    }

    private void buttonB_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
    {
        Border button = sender as Border;
        button.Background = (SolidColorBrush)new BrushConverter().ConvertFromString(_colorOut);
        WaitThisTimeAndHideMiniButton2(1000);
    }

    private void buttonA_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
    {
        if (_myTimerForButtonA?.Enabled == true)
            _myTimerForButtonA.Stop();
        miniButton1.Visibility = System.Windows.Visibility.Visible;
    }

    private void buttonB_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
    {
        if (_myTimerForButtonB?.Enabled == true)
            _myTimerForButtonB.Stop();
        miniButton2.Visibility = System.Windows.Visibility.Visible;
    }

正如您帖子的评论中所述,您不应该Timer每次都创建一个新的,并且应该在完成后将其丢弃。此外,在 WPF 项目中引用表单是一个坏主意,除非真的有必要,但这种情况很少发生。您可能想阅读有关UI 动画的Storyboard的内容。


推荐阅读