4

我正在使用以下代码尝试 Timer 类:-

protected void Page_Load(object sender, EventArgs e)
{
    System.Timers.Timer tm = new System.Timers.Timer();
    tm.Elapsed += new System.Timers.ElapsedEventHandler(tm_Elapsed);
    tm.Interval = 1000;
    tm.Start();
}

void tm_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    int lbl = Convert.ToInt32(Label1.Text);
    Label1.Text = (lbl+1).ToString();
}

最初,Label1.Text 为“1”。

但是当我运行应用程序时,标签的文本显示为 1 并且不会增加。

4

5 回答 5

3

正如其他答案中已经提到的那样, System.Timers.Timer 在非 GUI 线程上触发。这将不允许您访问 GUI 元素并会引发跨线程异常。您可以使用 MethodInvoker 访问 tm_Elapsed事件中的 GUI 元素。由于您在 Forms 中有 Timer 并希望访问 GUI 元素,因此其他 Timer 类最适合您,即System.Windows.Forms.Timer

实现一个以用户定义的时间间隔引发事件的计时器。此计时器针对在 Windows 窗体应用程序中的使用进行了优化,并且必须在窗口中使用。

protected void Page_Load(object sender, EventArgs e)
{         
     System.Windows.Forms.Timer tm = new System.Windows.Forms.Timer();
     tm.Tick += tm_Tick;
     tm.Interval = 1000;
     tm.Start();
}

void tm_Tick(object sender, EventArgs e)
{
     int lbl = Convert.ToInt32(label1.Text);
     label1.Text = (lbl + 1).ToString();
}

根据 OP 的评论进行编辑,他是在网页中执行此操作,而不是像加载事件名称所暗示的那样赢得表单。

如果您不需要来自服务器的任何内容,则可以使用 javascript。如果您想更新 html 控件并且需要从服务器执行,那么您可以使用asp:Timer

html (.aspx)

  <form id="form1" runat="server">             
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <asp:Timer runat="server" id="UpdateTimer" interval="5000" ontick="UpdateTimer_Tick" />
        <asp:UpdatePanel runat="server" id="TimedPanel" updatemode="Conditional">
            <Triggers>
                <asp:AsyncPostBackTrigger controlid="UpdateTimer" eventname="Tick" />
            </Triggers>
            <ContentTemplate>
                 <asp:Label id="Label1" runat="server" Text="1" />
            </ContentTemplate>
        </asp:UpdatePanel>
    </form>

背后的代码

protected void UpdateTimer_Tick(object sender, EventArgs e)
{
    Label1.Text = int.Parse(Label1.Text) + 1;
}
于 2015-05-07T07:18:18.780 回答
1

尝试这个:

    void tm_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        Label1.Invoke((Action)(() =>
        {
            int lbl = Convert.ToInt32(Label1.Text);
            Label1.Text = (lbl+1).ToString();
        }));
    }

问题是System.Timers.Timer它在非 UI 线程中触发其事件,并且由于您无法从非 UI 线程安全地访问或更新控件,因此它似乎不起作用。

调用.Invoke(...)UI 元素允许您将代码推送到 UI 线程上,使其安全。

于 2015-05-07T07:03:44.090 回答
1

System.Timers.Timer 旨在用于多线程环境。您不能直接访问 Elapsed 事件中的 ui 元素。

如果您的应用程序是 Windows 窗体,请使用 System.Windows.Forms.Timer。如果您的应用程序是 WPF,请使用 System.Windows.Threading.DispatcherTimer。

如果出于任何原因要使用 System.Timers.Timer,请使用 ui 元素的 Invoke 方法。例如在 Windows 窗体中:

Label1.Invoke(new Action(() =>
{
    int lbl = Convert.ToInt32(Label1.Text);
    Label1.Text = (lbl+1).ToString();
}));
于 2015-05-07T07:09:27.923 回答
0

您可以尝试以下,

if(this.Label1.InvokeRequired)
     {
         this.Label1.BeginInVoke((MethodInvoker) delegate() { int lbl = Convert.ToInt32(Label1.Text);
Label1.Text = (lbl+1).ToString();});    
     }
     else
     {
         int lbl = Convert.ToInt32(Label1.Text);
         Label1.Text = (lbl+1).ToString();
     }

问题是,WPF/Windows 窗体/Windows 8 应用程序/WP 8(.1) 应用程序不允许从不同线程更改 UI。您需要 UI 线程来更新它。

于 2015-05-07T07:13:38.137 回答
0

尝试使用System.Threading.Timer(线程安全)结合@Enigmativity 解决方案来避免跨线程异常:

    private System.Threading.Timer m_timer = null;
    private const int DUE_TIME = 1000;
    private const int INTERVAL = 1000;

    private void Page_Load(object sender, EventArgs e)
    {
        System.Threading.AutoResetEvent autoEvent = new System.Threading.AutoResetEvent(false);
        System.Threading.TimerCallback tcb = new System.Threading.TimerCallback(timer_Elapsed);
        m_timer = new System.Threading.Timer(tcb, null, DUE_TIME, INTERVAL);
    }

    void timer_Elapsed(object sender)
    {
        label1.Invoke((Action)(() =>
        {
            int lbl = Convert.ToInt32(label1.Text);
            label1.Text = (lbl + 1).ToString();
        }));
    }
于 2015-05-07T07:28:41.087 回答