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

Top.Mail.Ru

Использование "Контроллера" в диалоге


Программа с диалогом в качестве главного окна

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

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


Основное окно программы не должно быть универсальным окном, изменяющим свой размер. Много небольших приложений работают лучше в формате диалогового окна. Очевидное преимущество такого подхода заключается в том, что, для размещения элементов управления на поверхности диалога, можно использовать редактор ресурсов. Таким образом реализован пользовательский интерфейс (ПИ) частотного анализатора. Ниже этот полезный подход описан подробнее. Вы можете разгрузитьть исходный текст простого приложения, которое демонстрирует, описанные методы (любезность Laszlo Radanyi).

Прежде всего мы должны разработать диалоговое окно, используя редактор ресурсов. Мы назначаем идентификаторы всем элементам управления и непосредственно диалогу. В данном примере ресурс диалога имеет идентификатор DLG_MAIN. В процедуре WinMain мы не можем регистрировать окна любого класса, потому что Windows имеет предопределенный класс для диалоговых окон. Вместо того, чтобы создавать окно, мы вызываем функцию CreateDialog, передавая ей указатель на нашу собственную процедуру диалога (объясню позже).

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

Для удобства, мы храним значение HINSTANCE в глобальной переменной. Этот вариант фактически является предшественником более общего объекта - Приложения. Однако, в данном случае, пример слишком тривиальный, чтобы заслуживать собственного класса.



HINSTANCE TheInstance = 0;

int WINAPI WinMain
   (HINSTANCE hInst, HINSTANCE hPrevInst, char * cmdParam, int cmdShow)
{
    TheInstance = hInst;
    _set_new_handler (& NewHandler);

    HWND hDialog = 0;

    hDialog = 
        CreateDialog (hInst, MAKEINTRESOURCE (DLG_MAIN), 0, DialogProc);

    if (!hDialog)
    {
        char buf [100];
        wsprintf (buf, "Error x%x", GetLastError ());
        MessageBox (0, buf, "CreateDialog", MB_ICONEXCLAMATION | MB_OK);
        return 1;
    }

    MSG  msg;
    int status;
    while ((status = GetMessage (& msg, 0, 0, 0)) != 0)
    {
        if (status == -1)
            return -1;
        if (!IsDialogMessage (hDialog, & msg))
        {
            TranslateMessage ( & msg );
            DispatchMessage ( & msg );
        }
    }

    return msg.wParam;
}

Процедура диалога - точно такая же как и процедура Windows, за исключением того, что она возвращает TRUE, когда она обрабатывает сообщение и FALSE, когда его не обрабатывает. Нет никакой потребности вызывать процедуру, заданную по умолчанию, потому что Windows делает это за нас всякий раз, когда процедура диалога возвращает FALSE (делает за Вас это дело, так почему же не сделано точно также при использовании оконной процедуры...). Первое сообщение, которое диалог получает - WM_INITDIALOG, а последнее - WM_CLOSE. В ходе обработки этих сообщений мы создаем и уничтожаем "Контроллер" (Controller). Других случаях, отличных от этих, диалог ожидает сообщения от его элементов управления управления, передаваемого как WM_COMMAND. Одно из элементов управления, требует специальной обработки. Это (горизонтальная) линейка прокрутки (scrollbar). Она посылает сообщение WM_HSCROLL. Средства управления линейкой прокрутки (scrollbar) имеются в частотном анализаторе, и там показано, как иметь с ними дело.



BOOL CALLBACK DialogProc
    (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static Controller* control = 0;
    switch (message)
    {
    case WM_INITDIALOG:
        try
        {
            control = new Controller (hwnd);
        }
        catch (WinException e)
        {
            MessageBox (0, e.GetMessage (), "Exception", 
                         MB_ICONEXCLAMATION | MB_OK);
            PostQuitMessage(1);
        }
        catch (...)
        {
            MessageBox (0, "Unknown", "Exception", 
                         MB_ICONEXCLAMATION | MB_OK);
            PostQuitMessage(2);
        }
        return TRUE;
    case WM_COMMAND:
        control->Command(hwnd, LOWORD (wParam), HIWORD (wParam));
        return TRUE;
    case WM_HSCROLL:
        control->Scroll (hwnd, LOWORD (wParam), HIWORD (wParam));
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return TRUE;
    case WM_CLOSE:
        delete control;
        DestroyWindow (hwnd);
        return TRUE;
    }
    return FALSE;
}

Давайте, взглянем на "Контроллер". Обратите внимание, что каждый элемент управления на поверхности диалогового окна имеет соответствующий (внедренный) объект управления внутри "Контроллера". Имеются редакторы, селективные списки, радиокнопки и линейки прокрутки. Встречается специальный элемент управления "metafile", который рисует шкалу частот и два объекта отображения, соответствующие двум статическим подокнам, в которые мы рисуем графики. И, наконец, мы имеем объект Painter, который является ответственным за асинхронную перерисовку каждого из двух подокон.



class Controller
{
public:
    Controller(HWND hwnd);
    ~Controller ();
    void Command (HWND hwnd, int id, int code);
    void Scroll (HWND hwnd, int cmd, int pos);
    void Paint (HWND hwnd);
    void ReInit (HWND hwnd);
    void Stop (HWND hwnd);

private:
    void InitScrollPositions ();
    void PaintScale ();

    BOOL                _isStopped;

    int                 _bitsPerSample;
    int                 _samplesPerSecond;
    int                 _fftPoints;
    int                 _samplesPerBuf;

    EditReadOnly        _edit;
    Combo               _comboFreq;
    Combo               _comboPoints;
    RadioButton         _radio8;
    RadioButton         _radio16;
    ScrollBarMap        _scroll;

    StaticEnhMetafileControl    _scaleFreq;

    ViewWave            _viewWave;
    ViewFreq            _viewFreq;

    Painter             _display;
};

Конструктор "Контроллера" заботится об инициализации всех элементов управления, передавая им дескриптор диалогового окна и соответствующие идентификаторы. Как косметическую добавку, мы присоединяем нашу собственную иконку к диалогу. В противном случае система использовала бы стандартную иконку Windows.


Controller::Controller (HWND hwnd)
   :_isStopped (TRUE),
    _bitsPerSample (16),
    _samplesPerSecond (SAMPLES_SEC),
    _fftPoints (FFT_POINTS * 4),
    _samplesPerBuf (FFT_POINTS * 2),
    _radio8 (hwnd, IDC_8_BITS),
    _radio16 (hwnd, IDC_16_BITS),
    _scroll (hwnd, IDC_SCROLLBAR),
    _edit (hwnd, IDC_EDIT),
    _comboFreq (hwnd, IDC_SAMPLING),
    _comboPoints (hwnd, IDC_POINTS),
    _viewWave (hwnd, IDS_WAVE_PANE, FFT_POINTS * 8),
    _viewFreq (hwnd, IDS_FREQ_PANE),
    _scaleFreq (hwnd, IDC_FREQ_SCALE),
    _display (hwnd,
            _viewWave,
            _viewFreq,
            _samplesPerBuf,
            _samplesPerSecond,
            _fftPoints)
{
    // Attach icon to main dialog
    HICON hIcon = LoadIcon (TheInstance, MAKEINTRESOURCE (ICO_FFT));
    SendMessage (hwnd, WM_SETICON, WPARAM (ICON_SMALL), LPARAM (hIcon));
    // Other initializations...
}

Использование диалогового окна в качестве главного - очень удобная и простая техника, особенно для приложений, которые используют панелеподобный интерфейс. Между прочим, приложение "Морской бой" (см. домашню страницу) использует тот же самый прием.

Далее: использование диалоговых окон в Windows приложениях.