Как отобразить прошедшее время в метке - PullRequest
3 голосов
/ 22 декабря 2011

То, что должно быть прямым, не здесь, и я пока не могу найти выход, несмотря на то, что много читаю.

У меня есть кнопка, которая выполняет функцию времени. Поэтому при нажатии на кнопку должно отображаться время в миллисекундах в метке с интервалом 500 мс. И когда желаемый результат будет достигнут, я хочу, чтобы таймер остановился. Мне не нужно просто указывать конечное время (общее время, потраченное) в метке, но метка должна динамически отображать истекшее время. Мой код будет:

    private void btnHistory_Click(object sender, EventArgs e)
    {
        Class1 c = new Class1();
        c.StartClock(ref label12);

        Utility.PopulateHistory(dgvRecords_history, _util); //time consuming function

        c.StopClock();
    }

А в Class1 пишу так:

    internal void StartClock(ref Label l)
    {
        Timer t = new Timer();
        t.Interval = 500;
        t.Enabled = true;
        t.Tag = l;
        t.Tick += new EventHandler(t_Tick);
        t.Start();
    }

    int i;
    bool stop;
    void t_Tick(object sender, EventArgs e)
    {
        if (stop)
        {
            ((Timer)sender).Stop();
            return;
        }

        ((Label)((Timer)sender).Tag).Text = (++i).ToString();
    }

    internal void StopClock()
    {
        i = 0;
        stop = true;
    }

Что происходит, событие t_Tick запускается только после того, как сработает полный код под событием кнопки. То есть событие галочки запускается после того, как оно проходит через функцию StopClock! Я понятия не имею, с какой стати это должно быть так!

2 вопроса в основном:

  1. Как правильно выполнить мое требование, чтобы справиться с этим? Я знаю, что я должен использовать другие встроенные классы для оценки производительности, но это только для целей отображения. Для этого какой идеальный подход?

  2. Почему мой код не работает?

РЕДАКТИРОВАТЬ: Я использовал здесь System.Windows.Forms Timer здесь, но результат ничем не отличается с System.Timers Timer

Ответы [ 4 ]

6 голосов
/ 22 декабря 2011

Проблема в том, что ваша долгосрочная задача также выполняется в потоке пользовательского интерфейса. Поэтому таймер не может запустить и обновить пользовательский интерфейс, поскольку поток занят обработкой долго выполняющейся задачи.

Вместо этого вы должны использовать BackgroundWorker для обработки долгосрочной задачи.

В коде:

private void btnHistory_Click(object sender, EventArgs e) 
{ 
    Class1 c = new Class1(ref label12); 
    c.StartClock(); 

    var backgroundWorker = new BackgroundWorker();
    backgroundWorker.DoWork += (s, e) =>
        {
            // time consuming function
            Utility.PopulateHistory(dgvRecords_history, _util);
        };

    backgroundWorker.RunWorkerCompleted += (s, e) =>
        {
            c.StopClock();
        };

    backgroundWorker.RunWorkerAsync();
} 

Как отметил ChrisWue, поскольку теперь у вас есть долгосрочная задача в отдельном потоке, он должен вызывать любой доступ к элементам управления пользовательского интерфейса в потоке пользовательского интерфейса.

Если для выполнения вашей длительной задачи просто необходимы некоторые данные из пользовательского интерфейса, вы можете передать эти данные в качестве параметра RunWorkerAsync(). Если вам нужно вывести некоторые данные результатов в пользовательский интерфейс, вы можете сделать это в обработчике события RunWorkerCompleted. Если вам иногда нужно обновить пользовательский интерфейс по мере продвижения, вы можете сделать это в обработчике события ProgressChanged, вызвав ReportProgress() в вашем обработчике DoWork.

Если ничего из вышеперечисленного не требуется, вы можете использовать ThreadPool, как в ответе StaWho.

2 голосов
/ 22 декабря 2011

Ваша трудоемкая функция блокирует основной поток. Вы можете использовать BackgroundWorker или ниже трюк:

    public Form1()
    {
        InitializeComponent();
        t.Tick +=new EventHandler(t_Tick);
        t.Interval = 500;
    }

    int timeElapsed = 0;
    System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
    private void button1_Click(object sender, EventArgs e)
    {
        t.Start();
        ThreadPool.QueueUserWorkItem((x) =>
        {
            TimeConsumingFunction();
        });

    }

    void TimeConsumingFunction()
    {
        Thread.Sleep(10000);
        t.Stop();
    }

    void t_Tick(object sender, EventArgs e)
    {
        timeElapsed += t.Interval;
        label1.Text = timeElapsed.ToString();
    }
0 голосов
/ 24 декабря 2011

@ Ответ Дэйнел Роуз сработал для меня отлично, но только если была обработана недопустимая операция с несколькими потоками. Я мог бы сделать так:

private void btnHistory_Click(object sender, EventArgs e) 
{ 
    Class1 c = new Class1(ref label12); 
    c.StartClock(); 

    var backgroundWorker = new BackgroundWorker();
    backgroundWorker.DoWork += ((s, e) =>
        {
            // time consuming function
            Utility.PopulateHistory(dgvRecords_history, _util);
        });

    backgroundWorker.RunWorkerCompleted += ((s, e) =>
        {
            c.StopClock();
        });

    backgroundWorker.RunWorkerAsync();
} 

А в классе Utility, где выполняется трудоемкая функция,

    internal static void PopulateHistory(DataGridView dgv, Utility util)
    {
        SetDataGridView_History(dgv, util);
    }

    delegate void UpdateDataGridView_History(DataGridView dgv, Utility util);
    static void SetDataGridView_History(DataGridView dgv, Utility util)
    {
        if (dgv.InvokeRequired)
        {
            UpdateDataGridView_History updaterDelegate = new UpdateDataGridView_History(SetDataGridView_History);
            ((Form)util._w).Invoke(updaterDelegate, new object[] { dgv, util });
        }
        else
            //code that utilizes UI thread (long running process in my case)
    }

Спасибо всем, кто помог. Я отмечаю ответ Даниэля ..

0 голосов
/ 22 декабря 2011

Добавьте таймер в коллекцию компонентов формы. Или храните таймер в поле класса.

Таймер является сборщиком мусора, потому что он больше недоступен, когда ваш метод возвращает.

Я не знаю о вашем долго работающем коде, но наш новый должен запускаться в отдельном потоке или делать вызовы Application.DoEvents

(И удалите ссылку в вашем коде, она не используется).

...