Freddlow
Freddlow

Reputation: 167

Timer used for WPF display with strange behavior

I made a panel with several buttons in WPF. When mouse enter in some button, new buttons appear and will disappear 1000 ms after mouse leave.

But I have a strange behavior: 1000 ms in some case become shorter and shorter after each use.

Timer event

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 functions calling timer:

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

Event when leave button:

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

As described in code, I have buttonA and buttonB. When I enter buttonA, miniButton1 appear - it will disappear 1000 ms after mouse leave event. Same thing for buttonB that reveal miniButton2.

If I only enter and leave buttonB everything is correct. Same as ButtonA. The problem: If I enter/leave buttonA and B, then, these 1000ms go shorter and shorter. Disappearance of miniButton also appear before I leave buttonA and B. also happen before I leave the button.

Eveything behave like if timers were confusing each other. Do you know how to solve it?

Upvotes: 1

Views: 109

Answers (2)

Roger Leblanc
Roger Leblanc

Reputation: 1583

You're recreating a new Timer every time a MouseLeave event is fired, if you happen to fire that event twice within 1000ms, you'll be recreating a new Timer before the Tick event of the previous Timer execute, which will make the previous Timer run indefinitely.

Here's what's happening when you fire MouseLeave event twice within 1000ms :

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.

I was able to fix your code by stopping the timer on MouseEnter event

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

As stated in comments of your post, you shouldn't create a new Timer each time, and you should dispose it once you're done. Also, referencing Forms in a WPF project is a bad idea unless really necessary, which rarely is. You'll probably want to read about Storyboard for UI animation.

Upvotes: 1

Bizhan
Bizhan

Reputation: 17085

So basically this happens on mouse leave:

creating the timer
starting the timer

And this happens when timer expires:

stopping the timer

So if mouse repeatedly enters and leaves a button, it keeps creating and starting new timers. Which causes more than one timer to be running at the same time.

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

Although there are more issues here as mentioned in comments, this should fix that problem.

Upvotes: 0

Related Questions