Timer is not working in separate class in C#

2019-07-20 22:57发布

I would like to get the time my user instance is alive.
Therefore I added the following timer function.
But it does not work nevermind what I try:

using System;
using System.Collections.Generic;
using System.Timers;
using System.Threading;
using System.Windows;

public MainWindow() {
    User myUser = new User();

    System.Timers.Timer timer = new System.Timers.Timer(1000);
    timer.Elapsed += new ElapsedEventHandler(OnTimerElapsed);
    timer.AutoReset = true;
    timer.Enabled = true;

    myUser.setTimer(timer);
}

private void OnTimerElapsed(object source, ElapsedEventArgs e) {
    User currentUser = (User)source;
    MessageBox.Show("OnTimerElapsed: " + currentUser.Id);
}

This is my UserClass which includes the timer instance:

public class User : NotifyPropertyChanged {

    private System.Timers.Timer _timer;

    public void setTimer(System.Timers.Timer timer)
    {
        _timer = timer;
    }
}

标签: c# wpf timer
2条回答
你好瞎i
2楼-- · 2019-07-20 23:26

You are trying to cast the object source in the OnTimerElapsed to a type User

It's a Timer type.

You are causing an System.InvalidCastException every time the Timer ticks. It's not crashing, because it's on a worker thread.

By the way, simply setting a breakpoint on the line

User currentUser = (User)source;

would have revealed this error.

The handler method's first parameter is always the object that fired the event, in this case, it's the Timer instance, not the User type defined by you.

查看更多
祖国的老花朵
3楼-- · 2019-07-20 23:34

A Timer is good for causing actions to happen after some interval, or on a periodic basis. But a Timer does not in and of itself actually keep track of time. You need a Stopwatch for that.

Something that might work in your example:

class User
{
    private Stopwatch _alive = Stopwatch.StartNew();

    public TimeSpan Alive { get { return _alive.Elapsed; } }
}

This adds a Stopwatch to your User object so that it knows how long it's been alive.

Then in your window code:

public MainWindow() {
    User myUser = new User();

    myUser.Timer = new System.Timers.Timer(1000);
    myUser.Timer.Elapsed += (sender, e) => OnTimerElapsed(myUser);
    myUser.Timer.AutoReset = true;
    myUser.Timer.Enabled = true;
}

private void OnTimerElapsed(User currentUser) {
    MessageBox.Show(string.Format("OnTimerElapsed: {0}, alive {1:0} seconds",
        currentUser.Id, currentUser.Alive.TotalSeconds));
}

This does a couple of things:

  • It changes the timer handler so that it captures and passes the myUser object reference to the actual handler method. In your example, you were trying to cast the source parameter, but that's the Timer object, not a User object.
  • It retrieves the TimeSpan value from the Alive property, and displays the value as seconds.

Note that the User object does not itself need to know anything about the Timer object, at least not to accomplish the above.

Something like that should work; obviously you will make modifications according to your specific need.

查看更多
登录 后发表回答