Элементы управления подклассами

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

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

Элементы управления подклассами до ComCtl32.dll версии 6

Элемент управления можно поместить в подкласс и сохранить пользовательские данные в элементе управления. Это происходит при использовании версий ComCtl32.dll до версии 6. Существует ряд недостатков при создании подклассов с более ранними версиями ComCtl32.dll.

Чтобы сделать новый элемент управления, лучше всего начать с одного из распространенных элементов управления Windows и расширить его в соответствии с определенной потребностью. Чтобы расширить элемент управления, создайте элемент управления и замените ее существующую процедуру окна новым. Новая процедура перехватывает сообщения элемента управления и либо действует над ними, либо передает их исходной процедуре для обработки по умолчанию. Используйте функцию SetWindowLong или SetWindowLongPtr, чтобы заменить WNDPROC элемента управления. В следующем примере кода показано, как заменить WNDPROC.

OldWndProc = (WNDPROC)SetWindowLongPtr (hButton,
GWLP_WNDPROC, (LONG_PTR)NewWndProc);

Хранение пользовательских данных

Может потребоваться хранить пользовательские данные с отдельным окном. Эти данные можно использовать новой процедурой окна для определения способа рисования элемента управления или места отправки определенных сообщений. Например, можно использовать данные для хранения указателя класса C++ на класс, представляющий элемент управления. В следующем примере кода показано, как использовать SetProp для хранения данных с окном.

SetProp (hwnd, TEXT("MyData"), (HANDLE)pMyData);

Недостатки старого подклассного подхода

В следующем списке перечислены некоторые недостатки использования ранее описанного подхода к подклассу элемента управления.

  • Процедуру окна можно заменить только один раз.
  • После его создания сложно удалить подкласс.
  • Связывание частных данных с окном неэффективно.
  • Чтобы вызвать следующую процедуру в цепочке подклассов, вы не можете привести старую процедуру окна и вызвать ее, необходимо вызвать ее с помощью функции CallWindowProc.

Элементы управления подклассами с помощью ComCtl32.dll версии 6

Примечание.

ComCtl32.dll версии 6 только Юникод. Общие элементы управления, поддерживаемые ComCtl32.dll версии 6, не должны быть подклассами (или суперклассами) с процедурами окна ANSI.

 

ComCtl32.dll версии 6 содержит четыре функции, которые упрощают создание подклассов и устраняют недостатки, которые ранее обсуждались. Новые функции инкапсулируют управление, связанное с несколькими наборами эталонных данных, поэтому разработчик может сосредоточиться на функциях программирования, а не на управлении подклассами. Ниже перечислены функции подкласса:

SetWindowSubclass

Эта функция используется для первоначального подкласса окна. Каждый подкласс однозначно определяется адресом pfnSubclass и его uIdSubclass. Оба из них являются параметрами функции SetWindowSubclass . Несколько подклассов могут совместно использовать одну и ту же процедуру подкласса, и идентификатор может идентифицировать каждый вызов. Чтобы изменить эталонные данные, можно выполнить последующие вызовы SetWindowSubclass. Важное преимущество заключается в том, что каждый экземпляр подкласса имеет собственные эталонные данные.

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

LRESULT CALLBACK MyWndProc (HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass,
DWORD_PTR dwRefData);

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

Примечание.

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

 

В следующем примере показана скелетная реализация процедуры окна для подклассированного элемента управления.

LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
                               LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
        .
        .
        .
        return TRUE;
    // Other cases...
    } 
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

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

case WM_INITDIALOG:
{
    HWND button = GetDlgItem(hDlg, IDC_OWNERDRAWBUTTON);
    SetWindowSubclass(button, OwnerDrawButtonProc, 0, 0);
    return TRUE;
}

GetWindowSubclass

Эта функция извлекает сведения о подклассе. Например, можно использовать GetWindowSubclass для доступа к эталонным данным.

RemoveWindowSubclass

Эта функция удаляет подклассы. RemoveWindowSubclass в сочетании с SetWindowSubclass позволяет динамически добавлять и удалять подклассы.

DefSubclassProc

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