SoftCraft
разноликое программирование

Отправная точка
Программирование
Windows API
Автоматы
Нейроинформатика
Парадигмы
Параллелизм
Проектирование
Теория
Техника кодирования
Трансляторы
Учебный процесс
Прочие вопросы

Разное

Беллетристика
Брюзжалки
Цели и задачи
Об авторе


Обертка для контекста устройств


Холст или контекст устройств Windows

Перевод А. И. Легалова

Англоязычный оригинал находится на сервере компании Reliable Software


Чтобы раукрашивать, рисовать или печатать в окне, Вам необходим контекст устройств (device context или, кратко, DC). DC - это ресурс, который заимствуется у Windows и, как предполагается, возвращается сразу же после того, как вы сделаете свою работу. Отсюда и берет корни объект Canvas (Холст). Конструктор Холста получает DC, а деструктор освобождает его. Важно то, что Вы создаете объекты Canvas как автоматические (стековые) переменные. Это гарантирует, что, когда программа выйдет из локальной области (контекста), всегда вызовется их деструктор, в которой определены ресурсы (прдлагаемый класс является примером более общей методологии Управления ресурсами). Типичное использование объекта Canvas демонстрируется следующем кодом (вы его уже видели в программе Generic):


void Controller::Paint ()
{
    // prepare the canvas and let View do the rest
    PaintCanvas canvas (_hwnd);
    _view.Paint (canvas, _model);
    // Notice: The destructor of PaintCanvas called automatically!
}

Различные типы Холста совместно используют общего предка - класс Canvas. Обратите внимание, что конструктор Холста защищен. Фактически, Вы не можете сформировать объект этого класса. Однако, наследующие классы открыты, чтобы обеспечить доступ к их собственным конструкторам. Между прочим, Вы можете осуществлять добавление новых методов к Холсту, по мере возникновения такой потребности.


class Canvas
{
public:
    // operator cast to HDC
    // (used when passing Canvas to Windows API)
    operator HDC () { return _hdc; }

    void Point (int x, int y, COLORREF color)
    {
        ::SetPixel (_hdc, x, y, color);
    }

    void MoveTo (int x, int y)
    {
        ::MoveToEx (_hdc, x, y, 0);
    }

    void Line ( int x1, int y1, int x2, int y2 )
    {
        MoveToEx (_hdc, x1, y1, 0);
        LineTo (_hdc, x2, y2);
    }

    void Rectangle (int left, int top, int right, int bottom)
    {
        // draw rectangle using current pen
        // and fill it using current brush
        ::Rectangle (_hdc, left, top, right, bottom);
    }

    void GetTextSize (int & cxChar, int & cyChar)
    {
        TEXTMETRIC tm;
        GetTextMetrics (_hdc, & tm);
        cxChar = tm.tmAveCharWidth;
        cyChar = tm.tmHeight + tm.tmExternalLeading;
    }

    void Text (int x, int y, char const * buf, int cBuf)
    {
        ::TextOut ( _hdc, x, y, buf, cBuf );
    }

    void Char (int x, int y, char c)
    {
        TextOut (_hdc, x, y, & c, 1);
    }

    void SelectObject (void* pObj)
    {
        ::SelectObject (_hdc, pObj);
    }

protected:

    Canvas (HDC hdc): _hdc (hdc) {}

    HDC  _hdc;
};

В ответ на сообщение WM_PAINT нужно создать объект PaintCanvas. Обратите внимание на способ получения и освобождения DC объектом PaintCanvas.


class PaintCanvas: public Canvas
{
public:
    // Constructor obtains the DC
    PaintCanvas (HWND hwnd)
    : Canvas (BeginPaint (hwnd, & _paint)),
      _hwnd (hwnd)
    {}

    // Destructor releases the DC
    ~PaintCanvas ()
    {
        EndPaint (_hwnd, & _paint);
    }

protected:

    PAINTSTRUCT _paint;
    HWND        _hwnd;
};

Другой важный пример - класс UpdateCanvas, который используется для графических операций вне контекста обработки сообщения WM_PAINT. Конечно, ваша программа может всегда инициировать перерисовку, вызывая InvalidateRect, но во многих случаях это было бы массовым убийством. Если ваша программа осуществляет перерисовку новых объектов, когда они обрабатываются или в ответ на действия пользователя, Вы можете модифицировать окно, используя UpdateCanvas.


class UpdateCanvas: public Canvas
{
public:
    UpdateCanvas (HWND hwnd)
    :   Canvas (GetDC (hwnd)),
        _hwnd(hwnd)
    {}

    ~UpdateCanvas ()
    {
        ReleaseDC (_hwnd, _hdc);
    }

protected:

    HWND _hwnd;
};

Можно создать и другие типы Холста: DrawItemCanvas используется для рисования элементов управления их владельцем, MemCanvas - для рисования во фрагментах памяти, и т.д.

Далее: Использование перьев и кистей для рисования на холсте.