Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > window subclassing


Автор: Belous 10.3.2016, 18:17
Как сделать переход по элементам управления (э.у.) при нажатии <Tab>

В книжке Ч.Петзолд "Программирование для Windows 95" написано, что для этого нужно "введение новой оконной процедуры" (window subclassing). В тестовом примере я сделал почти так, как написано в книжке, за исключением:
1) у меня вместо 3 полос прокрутки - 3 кнопки;
2) у меня э.у. не в главном окне а в модальном. И код вынесен в модуль "Options.cpp", поэтому пришлось сделать 
static WNDPROC foptions[3]; иначе без волшебного слова static программа не компилируется.

В итоге программа компилируется, а потом при выполнении вываливается с ошибкой (см рисунок)

Вопрос как же сделать этот субклассинг?

Добавлено через 42 секунды
Код

// Options.cpp

#include "Options.h"

void Options::CreateDlg(HINSTANCE hinstance)
{
    InitCommonControls();
    WNDCLASSEX w;
    w.cbSize = sizeof(w);
    w.cbClsExtra = 0;
    w.cbWndExtra = 0;
    w.hbrBackground    = (HBRUSH) (COLOR_BTNFACE + 1);
    w.hCursor = LoadCursor(NULL, IDC_ARROW);
    w.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON_D));
    w.hIconSm = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON_D));
    w.hInstance = hinstance;
    w.lpfnWndProc = WndProcOptions;
    w.lpszClassName = STR_OPTIONS;
    w.lpszMenuName = NULL;
    w.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClassEx(&w);
}

void Options::ShowDlg(HWND hwnd, HINSTANCE hinstance)
{
    HWND hdlg;
    POINT point;
    RECT rect;
    DWORD dwStyle;
    EnableWindow(hwnd, FALSE);
    dwStyle = WS_POPUPWINDOW | WS_CAPTION;
    hdlg = CreateWindow(STR_OPTIONS, " Options", dwStyle, 0, 0, 0, 0, hwnd, NULL, hinstance, NULL);
    GetWindowRect(hwnd, &rect);
    point.x = (long) (rect.left + (rect.right - rect.left - INT_OPTIONS_WIDTH) / 2);
    point.y = (long) (rect.top + (rect.bottom - rect.top - INT_OPTIONS_HEIGHT) / 2);
    MoveWindow(hdlg, point.x, point.y, INT_OPTIONS_WIDTH, INT_OPTIONS_HEIGHT, FALSE);
    ShowWindow(hdlg, SW_SHOW);
    //
    dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON;
    hoptionsbutton[0] = CreateWindowEx(0, WC_BUTTON, NULL, dwStyle, 232, 365, 75, 23, hdlg, (HMENU) 1001, hinstance, NULL); // Apply
    ShowWindow(hoptionsbutton[0], SW_SHOW);
    hoptionsbutton[1] = CreateWindowEx(0, WC_BUTTON, NULL, dwStyle, 315, 365, 75, 23, hdlg, (HMENU) 1002, hinstance, NULL); // OK
    ShowWindow(hoptionsbutton[1], SW_SHOW);
    hoptionsbutton[2] = CreateWindowEx(0, WC_BUTTON, NULL, dwStyle, 398, 365, 75, 23, hdlg, (HMENU) 1003, hinstance, NULL); // Cancel
    ShowWindow(hoptionsbutton[2], SW_SHOW);
    foptions[0] = (WNDPROC) SetWindowLong(hoptionsbutton[0], GWL_WNDPROC, (LONG) WndProcOptionsButton);
    foptions[1] = (WNDPROC) SetWindowLong(hoptionsbutton[1], GWL_WNDPROC, (LONG) WndProcOptionsButton);
    foptions[2] = (WNDPROC) SetWindowLong(hoptionsbutton[2], GWL_WNDPROC, (LONG) WndProcOptionsButton);
}

Options::Options()
{
}

Options::~Options()
{
}

LRESULT CALLBACK WndProcOptions(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
    HWND hparent;
    int i;
    hparent = GetParent(hwnd);
    switch (message)
    {
    case WM_KEYDOWN:
        switch (LOWORD(wparam))
        {
        case VK_ESCAPE:
            SendMessage(hwnd, WM_CLOSE, NULL, NULL);
            break;
        default:
            break;
        }
        return 0;
    case WM_COMMAND:
        i = GetWindowLong((HWND) lparam, GWL_ID);
        switch (LOWORD(wparam))
        {
        case 1001:
            MessageBox(hwnd, "WM_COMMAND 1001", "Message", MB_OK);
            break;
        case 1002:
            MessageBox(hwnd, "WM_COMMAND 1002", "Message", MB_OK);
            break;
        case 1003:
            MessageBox(hwnd, "WM_COMMAND 1003", "Message", MB_OK);
            break;
        default:
            break;
        }
        return 0;
    case WM_CLOSE:
        EnableWindow(hparent, TRUE);
        SetFocus(hparent);
        BringWindowToTop(hparent);
        DestroyWindow(hwnd);
        return 0;
    }
    return DefWindowProc(hwnd, message, wparam, lparam);
}

LRESULT CALLBACK WndProcOptionsButton(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
    int i = GetWindowLong(hwnd, GWL_ID);
    switch (message)
    {
    case WM_KEYDOWN:
        if (wparam == VK_TAB) SetFocus(hoptionsbutton[(i+(GetKeyState(VK_SHIFT) < 0 ? 2 : 1)) % 3]);
        break;
    case WM_SETFOCUS:
        ifocus = i;
        break;
    }
    return CallWindowProc(foptions[i], hwnd, message, wparam, lparam);
}


Добавлено через 1 минуту и 38 секунд
Код

// Options.h

#include <windows.h>
#include <commctrl.h>
#include "resource.h"

#pragma comment (lib, "comctl32.lib")

#define STR_OPTIONS                "ClassOptions"
#define INT_OPTIONS_WIDTH        485
#define INT_OPTIONS_HEIGHT        420

static HICON hiconoptions;
static HWND hoptionsbutton[3];
static WNDPROC foptions[3];
static int ifocus;

class Options
{
public:
    void CreateDlg(HINSTANCE hinstance);
    void ShowDlg(HWND hwnd, HINSTANCE hinstance);
    Options();
    ~Options();
};

LRESULT CALLBACK WndProcOptions(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
LRESULT CALLBACK WndProcOptionsButton(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);

Автор: Belous 10.3.2016, 18:19
исходник

Автор: feodorv 10.3.2016, 21:16
Цитата(Belous @  10.3.2016,  18:17 Найти цитируемый пост)
В итоге программа компилируется, а потом при выполнении вываливается с ошибкой (см рисунок)

В суть вопроса не влезал. По падению программы: у Вас есть hwnd, зачем Вы в CallWindowProc передаёте foptions[i]?
Цитата(Belous @  10.3.2016,  18:17 Найти цитируемый пост)
    return CallWindowProc(foptions[ i ], hwnd, message, wparam, lparam);
 
И чему равно i? Точно меньше 3? А массив foptions инициализирован к моменту вызова CallWindowProc?

Автор: GremlinProg 10.3.2016, 22:18
Цитата(Belous @  10.3.2016,  20:17 Найти цитируемый пост)
GetWindowLong(hwnd, GWL_ID);

это лучше заменить на GetDlgCtrlID, хотя, проблема конечно не в этом
Цитата(Belous @  10.3.2016,  20:17 Найти цитируемый пост)
(HMENU) 1001

а вот это - уже проблема, т.к. вот здесь:
Цитата(Belous @  10.3.2016,  20:17 Найти цитируемый пост)
CallWindowProc(foptions[i], hwnd, message, wparam, lparam);

i равен как раз одному из 1001..1003

Автор: Belous 11.3.2016, 19:16
Исправил, все работает. Всем спасибо.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)