Опубликован: 17.08.2010 | Доступ: свободный | Студентов: 999 / 59 | Оценка: 4.11 / 3.89 | Длительность: 29:38:00
Самостоятельная работа 5:

Работа с таймерами MFC

Управление блокировкой кнопок "Пуск" и "Стоп"

Для управления доступом кнопок воспользуемся переменными, которые были созданы нами ранее как экземпляры класса CButton

// Для управления кнопкой "Пуск" 
  CButton m_cStartTimer;
  // Для управления кнопкой "Стоп" 
  CButton m_cStopTimer;

Эти переменные связаны с идентификаторами соответствующих кнопок в функции DoDataExchange() и с помощью методов класса CButton мы можем управлять этими кнопками.

Связь переменных класса CButton с соответствующими ресурсами
void CTimersDlg::DoDataExchange(CDataExchange* pDX)
{
  CDialog::DoDataExchange(pDX);
  DDX_Text(pDX, IDC_STATICTIME, m_curTime);
  DDX_Text(pDX, IDC_STATICDATE, m_strDate);
  DDX_Text(pDX, IDC_INTERVAL, m_iInterval);
  DDV_MinMaxInt(pDX, m_iInterval, 1, 100000);
  DDX_Control(pDX, IDC_STARTTIMER, m_cStartTimer);
  DDX_Control(pDX, IDC_STOPTIMER, m_cStopTimer);
  DDX_Text(pDX, IDC_STATICCOUNT, m_sCount);
}
  • Дополните обработчики этих кнопок следующим кодом
    Дополнения в обработчике кнопки "Пуск"
    void CTimersDlg::OnBnClickedStarttimer()
    {
      // TODO: Add your control notification handler code here
      
      // Инициализировать вспомогательную переменную
      // для счетчика срабатывания второго таймера
      m_iCount = 0;
      // Форматировать счетчик для отображения
      m_sCount.Format("%d", m_iCount);
      UpdateData(FALSE); // Обновить диалоговое окно
      SetTimer(ID_COUNT_TIMER, m_iInterval, NULL);// Запустить второй таймер
      
      // Блокировать кнопку пуска
      m_cStartTimer.EnableWindow(FALSE);
      // Разблокировать кнопку останова
      m_cStopTimer.EnableWindow(TRUE);
    }
    Дополнения в обработчике кнопки "Стоп"
    void CTimersDlg::OnBnClickedStoptimer()
    {
      // TODO: Add your control notification handler code here
      
      KillTimer(ID_COUNT_TIMER); // Остановить второй таймер
      
      // Разблокировать кнопку пуска
      m_cStartTimer.EnableWindow(TRUE);
      // Блокировать кнопку останова
      m_cStopTimer.EnableWindow(FALSE);
    }
  • Постройте проект и обратите внимание, что часы опять стали появляться с задержкой, пока не сработает в первый раз их таймер

Ранее при инициализации приложения именно для этой цели в функции OnInitDialog() мы посылали сообщение WM_TIMER, а в обработчике OnTimer() не было проверки, кто послал это сообщение. Теперь же обработчик принимает целевые сообщения от каждого таймера. Чтобы обработать нецелевое сообщение:

  • Добавьте в код обработчика следующее дополнение
    Дополнение в обработчик OnTimer()
    void CTimersDlg::OnTimer(UINT nIDEvent)
    {
      // TODO: Add your message handler code here and/or call default
      
      // Получить текущее время
      // CTime curTime = CTime::GetCurrentTime();
      curTime = CTime::GetCurrentTime();
      
      // Который таймер запустил это событие
      switch(nIDEvent){
        case ID_CLOCK_TIMER: // Первый таймер-часы
        default:
          // Извлечь текущее время в нужном формате
          m_curTime = curTime.Format("%H:%M:%S");
          break;
        case ID_COUNT_TIMER: // Второй таймер-счетчик
          m_iCount++; // Увеличить счетчик
          // Форматировать счетчик для отображения
          m_sCount.Format("%d", m_iCount);
      }
      
      // Переслать значения из переменных приложения на экран
      UpdateData(FALSE);
      
      CDialog::OnTimer(nIDEvent);
    }
  • Постройте проект и убедитесь, что все наладилось

    И еще одно, в функции OnInitDialog() вызов функции UpdateData(FALSE) можно убрать, поскольку в приведенном обработчике OnTimer() есть вызов этой функции, а мы все равно посылаем сообщение WM_TIMER строкой

    PostMessage(WM_TIMER);// Исполнить функцию-обработчик OnTimer()
  • Уберите вызов UpdateData(FALSE) из функции OnInitDialog()

Решение проблемы реентерабельности

Мы уже говорили, что если запустить приложение и ввести в поле редактирования значение вне установленного нами диапазона [1, 100000] для переменной m_iInterval, то возникнет конфликт реентерабельности, который приводит к аварии. Чтобы это исправить, введем в обработчик изменения поля OnEnChangeInterval() флаг, который будем обнулять перед вызовом функции UpdateData(), а после вызова - поднимать. Далее во всех других местах программы функцию UpdateData() будем выполнять только при условии, что флаг поднят, т.е. функция UpdateData() завершится.

  • Добавьте в класс CTimersDlg логическую переменную updateFlag
  • Инициализируйте ее значением true в конструкторе класса CTimersDlg
  • Добавьте в обработчик OnEnChangeInterval() следующий код
    Дополнение в обработчик OnEnChangeInterval()
    void CTimersDlg::OnEnChangeInterval()
    {
    ................................................
      // Переслать значения из поля ввода элементов управления,
      // отображаемые на экране, в соответствующие переменные
      updateFlag = false;
      UpdateData(TRUE);
      updateFlag = true;
    }
  • Найдите в обработчике OnTimer() вызов функции UpdateData() и посадите его на условие
    if(updateFlag)
      UpdateData(FALSE);
  • В самое начало обработчика кнопки "Пуск" введите код
    if(!updateFlag)
      return;
  • Постройте приложение, удостовертесь что все работает, но не высвечивается начальное значение счетчика 0, как это было до введения второго таймера
  • Сделайте, чтобы высвечивался нуль как и раньше

Сколько таймеров можно включить одновременно

В операционной системе Windows всем приложениям, запущенным одновременно, доступно ограниченное количество таймеров. Когда каждое приложение требует много таймеров и суммарное их количество в системе превышает допустимое значение, некоторым приложениям их число будет ограничено, а некоторым будет отказано вовсе. Поэтому рекомендуется следовать общему правилу: если предполагается использовать больше двух или трех таймеров, нужно перепроектировать приложение так, чтобы уменьшить их количество.


Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .