C# 用于WPF显示的计时器,具有奇怪的行为

C# 用于WPF显示的计时器,具有奇怪的行为,c#,.net,wpf,C#,.net,Wpf,我用WPF制作了一个有几个按钮的面板。 当鼠标进入某个按钮时,会出现新按钮,并在鼠标离开1000毫秒后消失 但我有一个奇怪的行为:在某些情况下,1000毫秒在每次使用后变得越来越短 计时器事件 private void TimerEventProcessorForButtonA(Object myObject, EventArgs myEventArgs) { _myTimerForButtonA.Stop(); miniButton1.Visibility = System.

我用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毫秒后消失。 buttonB和miniButton2一样

如果我只按进出按钮,一切都是正确的。和ButtonA一样。 问题:如果我输入/离开按钮A和B,那么这1000毫秒会越来越短。在我离开按钮A和B之前,迷你按钮也会消失。在我离开按钮之前,迷你按钮也会消失

每件事都表现得好像计时器在互相混淆。
你知道怎么解决吗?

所以基本上这是在鼠标离开时发生的:

creating the timer
starting the timer
当计时器过期时会发生这种情况:

stopping the timer
所以,若鼠标反复进入和离开一个按钮,它会不断创建和启动新的计时器。这会导致多个计时器同时运行

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

尽管如评论中所述,此处存在更多问题,但这应该可以解决该问题。

每次触发
MouseLeave
事件时,如果您在1000毫秒内两次触发该事件,您将重新创建一个新的
计时器,在执行上一个
计时器的
勾选事件之前,您将重新创建一个新的
计时器
,这将使上一个
计时器
无限期运行

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

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;
    }

正如你在文章的评论中所说,你不应该每次都创建一个新的
计时器,你应该在完成后处理它。此外,在WPF项目中引用表单是一个坏主意,除非真的有必要,否则很少有必要。您可能需要阅读有关UI动画的内容。

System.Windows.Forms.Timer
实现
IDisposable
。在创建新实例之前,是否要先处理以前的每个实例?不这样做可能会导致您的问题。你能试试看这是否能解决问题吗?需要水晶球应用程序。由于重复创建计时器对象,您遇到了麻烦。坏主意不止一种,你抱怨的是,前一个计时器对象仍然在愉快地滴答作响,所以按钮消失得太快了。更糟糕的是,它从未停止过,因为您无法再调用它的Stop()方法。仅在窗口的构造函数中创建一次计时器对象。使用Closed事件来处置它们。请查看Dispatchermer。不要在WPF应用程序中使用
System.Windows.Forms.Timer
。改用
System.Windows.Threading.dispatchermer
。不需要处理调度程序。如前所述,只创建一次计时器实例。然后反复启动和停止一个现有的计时器。正如前面提到的,我创建了计时器1次,只需使用停止和启动,它就可以工作了@克莱门斯:为什么WPF中不能使用System.Windows.Forms.Timer(实际上它不是winform,但会引发哪个问题)?请阅读此处的备注: