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

Работа MFC с GDI (GDI+)

Рисование встроенных примитивов GDI

Добавим в приложение код для рисования кругов и квадратов с помощью GDI -функций Ellipse() и Rectangle(). Обеим функциям передается объект типа CRect, задающий прямоугольник, в который будет вписана рисуемая фигура. Функция Rectangle() заполняет все место в прямоугольнике, а функция Ellipse() рисует эллипс или круг, причем середина каждой стороны прямоугольника касается края эллипса.

Будем использовать в этих функциях текущие настройки пера и кисти, которые выберет пользователь. Для этого нужно их создать. В случае с пером можно использовать нулевое перо (стиль пера PS_NULL ), но когда речь идет о кисти, то нужно будет создать сплошную кисть, имеющую цвет фона окна (светло-серый).

Прямоугольники и эллипсы расположим на холсте в две строки по четыре фигуры в каждой строке. Для рисования фигур создадим отдельный метод, как и в случае с рисованием линий, и назовем его DrawRegion().

  • В панели Class View выберите класс CPaintDlg
  • Вызовите мастер Add Function и настройте его так


    Мастер добавит код

    Объявление функции DrawRegion() в файле PaintDlg.h
    class CPaintDlg : public CDialog
    {
    
    .............................................
    public:
      // Таблица цветов
      static const COLORREF m_crColors[8];
    private:
      // Рисование линий на холсте
      void DrawLine(CPaintDC * pDC, int iColor);
    public:
      afx_msg void OnPaint();
    private:
      // Функция рисования примитивных фигур GDI - прямоугольников и эллипсов
      void DrawRegion(CPaintDC * pDC, int iTool, int iShape, int iColor);
    };
    Заготовка определения функции DrawRegion() в файле PaintDlg.cpp
    // Функция рисования примитивных фигур GDI - прямоугольников и эллипсов
    void CPaintDlg::DrawRegion(CPaintDC * pDC, int iTool, int iShape, int iColor)
    {
    }
  • Заполните код функции DrawRegion() так

    Функция DrawRegion() в файле PaintDlg.cpp
    // Функция рисования примитивных фигур GDI - прямоугольников и эллипсов
    void CPaintDlg::DrawRegion(CPaintDC * pDC, int iTool, int iShape, int iColor)
    {
      // Объявить и создать перья
      CPen pnSolidPen(PS_SOLID, 1, m_crColors[iColor]);
      CPen pnDashPen(PS_DASH, 1, m_crColors[iColor]);
      CPen pnDotPen(PS_DOT, 1, m_crColors[iColor]);
      CPen pnDashDotPen(PS_DASHDOT, 1, m_crColors[iColor]);
      CPen pnDashDotDotPen(PS_DASHDOTDOT, 1, m_crColors[iColor]);
      CPen pnNullPen(PS_NULL, 1, m_crColors[iColor]);
      CPen pnInsidePen(PS_INSIDEFRAME, 1, m_crColors[iColor]);
      CPen pnCombinationPen(PS_DOT|PS_GEOMETRIC|PS_ENDCAP_ROUND
              , 1, m_crColors[iColor]);
      
      // Объявить и создать кисти
      CBrush brSolidBrush(m_crColors[iColor]);
      CBrush brBDiagBrush(HS_BDIAGONAL, m_crColors[iColor]);
      CBrush brCrossBrush(HS_CROSS, m_crColors[iColor]);
      CBrush brDiagCrossBrush(HS_DIAGCROSS, m_crColors[iColor]);
      CBrush brFDiagBrush(HS_FDIAGONAL, m_crColors[iColor]);
      CBrush brHorizBrush(HS_HORIZONTAL, m_crColors[iColor]);
      CBrush brVertBrush(HS_VERTICAL, m_crColors[iColor]);
      CBrush brNullBrush(RGB(208, 208, 208)); // Светло-серый системный цвет
      
      // Получить область рисования
      // Все размеры GDI в пикселах
      CRect rClientRect;
      GetClientRect(rClientRect);
      rClientRect.NormalizeRect();
      
      // Параметры рисования
      const int iDisp = 8;// Высота и ширина междурядья и краев клиентской области
      int iHeight = (rClientRect.Height() - 3 * iDisp) / 2; // Высота фигуры для двух рядов
      int iWidth = (rClientRect.Width() - 5 * iDisp) / 4; // Ширина фигуры для четырех в ряд
      
      CRect rCurrentRect; // Текущие координаты рисования
      CPen* pOldPen = NULL; // Создали указатель пера и сразу обезопасили
      CBrush* pOldBrush = NULL; // Создали указатель кисти и сразу обезопасили
      
      // Перебираем в цикле все перья и кисти
      // и сразу рисуем
      for(int count = 0; count < 8; count ++){
        // Вычисляем текущие координаты привязки фигуры
        if(count == 0){ // Начать первую строку
          rCurrentRect.left = rClientRect.left + iDisp;// Координата X
          rCurrentRect.right = rCurrentRect.left + iWidth;// Координата X
          rCurrentRect.top = rClientRect.top + iDisp;// Координата Y
          rCurrentRect.bottom = rCurrentRect.top + iHeight;// Координата Y
        }
        else if(count == 4){ // Начать вторую строку
          rCurrentRect.left = rClientRect.left + iDisp;// Координата X
          rCurrentRect.right = rCurrentRect.left + iWidth;// Координата X
          rCurrentRect.top = rClientRect.top + 2 * iDisp + iHeight;// Координата Y
          rCurrentRect.bottom = rCurrentRect.top + iHeight;// Координата Y
        }
        else{// Продолжить текущую строку
          rCurrentRect.left = rCurrentRect.right + iDisp;// Координата X
          rCurrentRect.right = rCurrentRect.left + iWidth;// Координата X
        }
      
        // Выбираем очередное перо и кисть
        switch(count){
          case 0: // Сплошная линия и сплошная заливка
            // Сразу запомним старое перо и кисть
            pOldPen = pDC->SelectObject(&pnSolidPen);
            pOldBrush = pDC->SelectObject(&brSolidBrush);
            break;
          case 1: // Заливка штриховкой обратными косыми
            pDC->SelectObject(&pnDashPen);
            pDC->SelectObject(&brBDiagBrush);
            break;
          case 2: // Заливка вертикальной и горизонтальной штриховкой 
            pDC->SelectObject(&pnDotPen);
            pDC->SelectObject(&brCrossBrush);
            break;
          case 3: // Заливка штриховкой по обеим диагоналям 
            pDC->SelectObject(&pnDashDotPen);
            pDC->SelectObject(&brDiagCrossBrush);
            break;
          case 4: // Заливка диагональю из черточек и двух точек
            pDC->SelectObject(&pnDashDotDotPen);
            pDC->SelectObject(&brFDiagBrush);
            break;
          case 5: // Нулевая обводка, горизонтальная штриховка, 
            pDC->SelectObject(&pnNullPen);
            pDC->SelectObject(&brHorizBrush);
            break;
          case 6: // Заливка вертикальной штриховкой
            pDC->SelectObject(&pnInsidePen);
            pDC->SelectObject(&brVertBrush);
            break;
          case 7: // Нулевая заливка
            pDC->SelectObject(&pnCombinationPen);
            pDC->SelectObject(&brNullBrush);
        } // end of switch
      
        // Анализируем переключатели группы Tool
        // Чем рисовать: пером или кистью (что включено)?
        // Мы заготовили и то и другое!!!
        switch(iTool){
          case 0: // Включен режим Tool = Pen - рисовать только пером
            // Скрываем кисть
            pDC->SelectObject(&brNullBrush);
            break;
          case 1: // Включен режим Tool = Brush - отображать только заливку
            // Скрываем перо
            pDC->SelectObject(&pnNullPen);
            break;
          default:
            // Скрываем и то и другое на всякий случай
            pDC->SelectObject(&pnNullPen);
            pDC->SelectObject(&brNullBrush);
        }
      
        // Анализируем переключатели группы Shape
        // Что рисовать: эллипс или прямоугольник (что включено)?
        // Координаты расположения для каждой итерации у нас есть!!!
        switch(iShape){
          case 2: // Включен режим Shape = Square - рисовать прямоугольник
            pDC->Rectangle(rCurrentRect);
            break;
          case 1: // Включен режим Shape = Circle - рисовать эллипс
            pDC->Ellipse(rCurrentRect);
        }
      } // end of for
      
      // Восстановить в контексте устройства первоначальные перо и кисть
      pDC->SelectObject(pOldPen);
      pDC->SelectObject(pOldBrush);
    }

    Вызов функции DrawRegion() нужно размещать в том же самом месте, где линии будут рисоваться при каждом разрушении окна и куда мы ранее поместили вызов функции DrawLine(). Это место - в функции OnPaint() второго окна, обслуживаемого классом CPaintDlg.

  • Дополните функцию OnPaint() соответствующим новым кодом

    Изменения в функции OnPaint() файла PaintDlg.cpp
    void CPaintDlg::OnPaint()
    {
      CPaintDC dc(this); // Локальный контекст устройства 
    
      // Получить указатель на родительское окно
      CGraphicsDlg* pWnd = (CGraphicsDlg*) GetParent();
    
      if(!pWnd) return; // Если родитель не существует - выйти
    
      if(pWnd->m_iTool == 0 // Если выбран режим Pen
         && pWnd->m_iShape == 0) // и если выбран режим Line
        DrawLine(&dc, pWnd->m_iColor); // Рисуем линии
      
      if(pWnd->m_iTool != 2 // Если не выбран растр Bitmap
         && pWnd->m_iShape != 0) // Если не выбран режим Line
        // Рисуем прямоугольник, эллипс или их контуры текущим цветом
        DrawRegion(&dc, pWnd->m_iTool, pWnd->m_iShape, pWnd->m_iColor); 
    }
  • Постройте приложение и убедитесь, что все работает как нужно

    Должно получиться что-то подобное


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