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

Однодокументный интерфейс MFC

Обработка событий комбинированного списка, расположенного на панели инструментов

При нажатии на кнопки панели инструментов нам не нужно передавать из класса CMainFrame, которому принадлежит панель инструментов, значение выбранного цвета пера в закрытую переменную m_iColor класса CDrawSDIDoc. Это делается через обработчики событий меню, которые перехватываются классом CDrawSDIDoc, поскольку кнопки панели и пункты меню Color имеют одинаковые идентификаторы.

В случае с комбинированным списком нам нужно сделать так, чтобы при выборе опции комбинированного списка на панели инструментов выбранное значение толщины пера передавалось бы как значение в переменную m_iWidth класса CDrawSDIDoc. Поскольку в этом классе мы объявили эту переменную закрытой от клиента класса, то необходимо дополнить класс функцией доступа, которую назовем SetWidth().

  • Выделите класс CDrawSDIDoc во вкладке Class View и через контекстное меню вызовите мастер Add Function, который заполните так


    Не забудьте нажать кнопку Add перед нажатием кнопки Finish

  • Тело функции заполните так
    Функция доступа SetWidth() класса CDrawSDIDoc
    // Установить толщину пера
    void CDrawSDIDoc::SetWidth(int nWidth)
    {
      m_iWidth = nWidth;
    }

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

Но как получить значение от комбинированного списка. Можно перхватывать события, которые генерирует комбинированный список при выборе пользователем нового элемента списка. Это событие имеет имя ON_CBN_SELCHANGE. Поскольку это сообщение генерируется в классе CMainFrame, перехватим его здесь. Для этого:

  • Введите вручную регистрацию события ON_CBN_SELCHANGE в карту сообщений класса, чтобы она выглядела так
    Карта сообщений класса CMainFrame в файле MainFrm.cpp
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
      ON_WM_CREATE()
      ON_COMMAND(ID_VIEW_COLORBAR, OnViewColorbar)
      ON_UPDATE_COMMAND_UI(ID_VIEW_COLORBAR, OnUpdateViewColorbar)
      ON_CBN_SELCHANGE(IDC_CBWIDTH, OnSelChangeWidth)
    END_MESSAGE_MAP()
  • Через мастер добавьте в класс CMainFrame функцию-обработчик OnSelChangeWidth, заполнив мастер так

  • Заполните, обработчик кодом, как показано ниже
    Обработчик OnSelChangeWidth() в файле MainFrm.cpp
    // Функция-обработчик выбора элемента комбинированного списка
    void CMainFrame::OnSelChangeWidth(void)
    {
      int iIndex = m_ctrlWidth.GetCurSel();
      if(iIndex == CB_ERR)// Если получили индекс
        return;
      
      int iWidth = iIndex + 1;
      switch(iIndex + 1){ // Скорректировать толщину
        case  9: iWidth = 16; break;
        case 10: iWidth = 24; break;
        case 11: iWidth = 32;
      }
      
      // Получить активный документ
      CDrawSDIDoc* pDoc = (CDrawSDIDoc*)GetActiveDocument();
      if(pDoc) // Если получили указатель
        pDoc->SetWidth(iWidth); // Передать толщину в документ
    }
  • Поскольку класс документа CDrawSDIDoc в файле MainFrm.cpp не известен, включите в начало этого файла инструкцию #include. Следует иметь ввиду, что она должна располагаться в конце других инструкций #include, иначе могут появиться ошибки компиляции, поскольку предыдущие инструкции тоже могут содержать подобный код
    Инструкция включения файла DrawSDIDoc.h в файл MainFrm.cpp
    .........................................
    .........................................
    #include "DrawSDIDoc.h"
    // CMainFrame
    
    IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
    
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
      ON_WM_CREATE()
      ON_COMMAND(ID_VIEW_COLORBAR, OnViewColorbar)
      ON_UPDATE_COMMAND_UI(ID_VIEW_COLORBAR, OnUpdateViewColorbar)
      ON_CBN_SELCHANGE(IDC_CBWIDTH, OnSelChangeWidth)
    END_MESSAGE_MAP()
    .........................................
  • Постройте приложение и убедитесь, что список управляет толщиной линии, но при изменении толщины через меню значение списка синхронно не изменяется

Исправим это...

Обновление комбинированного списка при вводе толщины пера через меню

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

Функция-обработчик в файле DrawSDIDoc.cpp
// Срабатывает при нажатии на любой пункт меню Width
void CDrawSDIDoc::OnWidthCommand(UINT nID)
{
  switch(nID){
    case ID_WIDTH_16: m_iWidth = 16; break;
    case ID_WIDTH_24: m_iWidth = 24; break;
    case ID_WIDTH_32: m_iWidth = 32; break;
    default: m_iWidth = nID - ID_WIDTH_1 + 1;
  }
}

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

Здесь необходимы следующие шаги:

  1. Через обработчик меню вызвать функцию обновления комбинированного списка
  2. В функции обновления, как члене документа, вначале получить указатель на активное окно представления View, поскольку сам документ не является оконным объектом и не происходит от базового класса CWnd
  3. Затем через указатель окна представления получить указатель на обрамляющее это представление фреймовое окно-рамку, которому и принадлежит панель инструментов с комбинированным списком
  4. И наконец, через полученный указатель на фреймовое окно обратиться к комбинированному списку и установить его в соответствие с выбранной через меню документа толщиной пера
  5. Подключить к файлу документа заголовочный файл фреймового окна, поскольку будем добывать в документе указатель на фреймовое окно, а его порождающий класс CMainFrame в документе не виден

С учетом вышеизложенного выполните следующее:

  • Введите в документ функцию обновления комбинированного списка, назовите ее CDrawSDIDoc::UpdateComboBox(). Для этого для класса CDrawSDIDoc заполните мастер Add Function так


    Не забудьте нажать кнопку Add перед нажатием кнопки Finish

  • Мастер сгенерирует заготовку, которую заполните следующим кодом
    Функция UpdateComboBox() в файле DrawSDIDoc.cpp
    // Функция обновления комбинированного списка
    void CDrawSDIDoc::UpdateComboBox(int iIndex)
    {
      // Получить позицию активного вида в Z-последовательности видов
      POSITION pos = GetFirstViewPosition();
      if(!pos) // Позиция не получена, она нулевая
        return;
    
      // Получить указатель на верхнее представление (активный вид)
      CView* pView = GetNextView(pos);
      if(!pView) // Указатель не получен, он нулевой
        return;
    
      // Получить указатель на фреймовое окно с помощью вида
      CMainFrame* pFrame = (CMainFrame*)pView->GetTopLevelFrame();
      if(!pFrame) // Указатель не получен, он нулевой
        return;
    
      // Обновить комбинированный список на панели инструментов
      pFrame->m_ctrlWidth.SetCurSel(iIndex);
    }
  • Добавьте в начала файла DrawSDIDoc.cpp инструкцию препроцессору для подключения класса фреймового окна, объявление которого находится в файле приложения MainFrm.h. Имейте ввиду, что она должна располагаться в конце других инструкций #include, иначе могут появиться ошибки компиляции, поскольку предыдущие инструкции тоже могут содержать подобный код
    Инструкция препроцессору в файле DrawSDIDoc.cpp
    ...............................................................
    #include "MainFrm.h"
    // CDrawSDIDoc
      
    IMPLEMENT_DYNCREATE(CDrawSDIDoc, CDocument)
      
    BEGIN_MESSAGE_MAP(CDrawSDIDoc, CDocument)
      ON_COMMAND_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnColorCommand)
      ON_UPDATE_COMMAND_UI_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnUpdateColorUI)
      ON_COMMAND_RANGE(ID_WIDTH_1, ID_WIDTH_32, OnWidthCommand)
      ON_UPDATE_COMMAND_UI_RANGE(ID_WIDTH_1, ID_WIDTH_32, OnUpdateWidthUI)
    END_MESSAGE_MAP()
    ...............................................................
  • Поместите вызов функции UpdateComboBox() в обработчике выбора пункта меню Width
    Вызов функции в обработчике меню файла DrawSDIDoc.cpp
    // Срабатывает при нажатии на любой пункт меню Width
    void CDrawSDIDoc::OnWidthCommand(UINT nID)
    {
      switch(nID){
        case ID_WIDTH_16: m_iWidth = 16; break;
        case ID_WIDTH_24: m_iWidth = 24; break;
        case ID_WIDTH_32: m_iWidth = 32; break;
        default: m_iWidth = nID - ID_WIDTH_1 + 1;
      }
      UpdateComboBox(nID - ID_WIDTH_1);
    }
  • Постройте приложение и убедитесь, что все работает правильно!!!..., кроме одного - при создании нового документа комбинированный список не устанавливается в начало
  • Чтобы это исправить, разместите вызов UpdateComboBox(0) еще и в функции OnNewDocument() класса CDrawSDIDoc (сделайте это самостоятельно!!!)

Есть еще одна неувязочка, которая может не понравиться ПОЛЬЗОВАТЕЛЮ. После прикрепления к левому или правому боку фреймового окна панель скрывает комбинированный список. На данном этапе исправим это, установив свойство панели прикрепляться только к верхнему краю, что не очень ограничит права пользователя. Для этого:

  • Измените в функции CMainFrame::OnCreate() соответствующий фрагмент кода
    Изменение свойства прикрепления панели в файле CMainFrm.cpp
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    ................................................................
      m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
      EnableDocking(CBRS_ALIGN_ANY);
      m_wndColorBar.EnableDocking(CBRS_ALIGN_TOP);// Наша!!!
      EnableDocking(CBRS_ALIGN_TOP);
      
      DockControlBar(&m_wndToolBar);
      DockControlBar(&m_wndColorBar);// Наша!!!
      
      return 0;
    }

И последнее. Если потянуть панель в плавающем состоянии за нижний край, то список опять исчезает. Разозлимся и отключим у пользователя свет, газ и воду, а у панели - возможность изменять размер.

  • Просто закомментируйте код, как показано в листинге
    Отключение свойства панели изменять размер
    // Вспомогательная упаковка кода создания панели инструментов
    bool CMainFrame::CreateColorBar(void)
    {
      // Создать инструментальную панель 
      // для выбора цвета и настроить ее
      if (!m_wndColorBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
        | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY /* | CBRS_SIZE_DYNAMIC */) ||
        !m_wndColorBar.LoadToolBar(IDR_TBCOLOR))
      {
        TRACE0("Failed to create toolbar\n");
        return FALSE;      // fail to create
      }
    ..................................................
      // Устанавливаем первый элемент видимым
      m_ctrlWidth.SetCurSel(0);
      
      return TRUE;
    }
Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .