Шаг 56.
Библиотека OWL.
Управляющие элементы окна. Анализ программы 55 шага

    На этом шаге мы рассмотрим структуру и принципы работы программы, приведенной на предыдущем шаге.

    После изучения работы программы, приведенной на 55 шаге, обратите свое внимание на ее текст. В начале программы включаются заголовочные файлы, в которых она нуждается:

#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\dialog.h>
#include <owl\radiobut.h>
#include <owl\edit.h>
#include <owl\listbox.h>
#include <owl\combobox.h>
#include <owl\scrollba.h>
#include <owl\dc.h>
#include <string.h>
#include "pr55_1.rc"

    В этот список входят заголовочные файлы для различных объектов управляющих элементов. Например, файл LISTBOX.H является заголовочным файлом класса TListBox, тогда как файл SCROLLBA.H - заголовочный файл класса TScrollBar. По имени заголовочного файла вы можете выяснить, какой файл относится к какому классу. Вы должны включать заголовочные файлы для тех классов, которые используются в вашей программе, в противном случае ваша программа не будет компилироваться.

    Используемые в программе константы находятся в файле ресурсов, приведенном на 55 шаге:

#define MENU_1      100 
#define DIALOG_1    200 
#define CM_DIALOG   201

#define ID_EDIT     101
#define ID_RADIO1   102
#define ID_RADIO2   103
#define ID_RADIO3   104
#define ID_SCROLLER 105
#define ID_CHECK1   106
#define ID_CHECK2   107
#define ID_CHECK3   108
#define ID_LISTBOX  109
#define ID_COMBOBOX 110

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

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

struct
{
  char edit[81];
  BOOL radio1;
  BOOL radio2;
  BOOL radio3;
  TScrollBarData scrollerData;
  BOOL check1;
  BOOL check2;
  BOOL check3;
  TListBoxData  listBoxData;
  TComboBoxData comboBoxData;
} transBuf;	

    Так как каждый из управляющих элементов содержит данные специального типа, буфер обмена диалогового окна должен обеспечить для каждого управляющего элемента подходящие поля. В рассмотренном ранее буфере обмена edit[] представляет собой массив символов для строки области ввода; radio1 - radio3 - булевы значения, указывающие на статус выбора кнопок; ScrollerData - структура TScrollBarData, которая содержит информацию о линейке прокрутки; check1 - check3 - булевы значения, указывающие на статус трех кнопок с независимой фиксацией; listBoxData - объект TListBoxData, который содержит данные окна списка, и ComboBoxData - объект TComboBoxData, который обеспечивает хранение данных для комбинированного окна.

    Если вы еще не догадались, TScrollBarData, TListBoxData и TComboBoxData являются OWL-структурами и классами, специально сконструированными для облегчения передачи и управления данными из линеек прокрутки, окон списка и комбинированных окон.

    TScrollBarData является структурой, определенной в файле SCROLLBA.H; она имеет три поля. Поле данных HighValue является целым числом, указывающим верхнюю границу диапазона линейки прокрутки. LowValue - целое число, представляющее нижнюю границу диапазона линейки прокрутки. Position - целое число, представляющее текущую установку окна прокрутки. Класс TScrollBar содержит функции для получении данных из структуры TScrollBarData.

    В классе TListBoxData содержатся различные общие члены-данные, содержащие информацию об окне списка, включая количество выбранных строк (SelCount), индексы выбранных строк (SelIndexes), строки, которые должны быть выбраны, когда отображается окно списка (SelStrings), и строки, которые будут передаваться в окно списка (Strings). Этот класс также содержит функции-члены для работы с данными окна списка. Например, функция-член AddString() добавляет строку к объекту TListBoxData, в то время как GetSelString() отыскивает выбранную строку. К другим функциям-членам относятся: GetSelStringLength(), которая возвращает длину выбранной строки, ResetSelections(), которая очищает объект TListBoxData от всех выбранных строк. Select(), которая выбирает строку по данному индексу, и SelectString(), которая добавляет строку к выбранным строкам.

    Класс TComboBoxData содержит члены-данные, представляющие данные комбинированного окна, так же, как и функции-члены для работы с этими данными. Например, указатель на символ Selection указывает на выбранную строку в комбинированном окне. Strings - массив строк, которые будут передаваться в это окно. SelIndex - индекс выбранной строки в массиве Strings. Функции-члены включают AddString(), которая добавляет строку к массиву Strings, и AddStringItem(), которая добавляет строку к массиву Strings и данные к массиву ItemDatas объекта.

    Возвратимся к примеру - класс главного окна помимо своего конструктора содержит только две функции-члена:

class TWndw : public  TFrameWindow
{
  public:
	 TWndw (TWindow *parent, const char far *title);
  protected:
         void Paint(TDC&, BOOL, TRect&);
         void CmDialog();

         DECLARE_RESPONSE_TABLE(TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TFrameWindow)
	  EV_COMMAND (CM_DIALOG, CmDialog),
END_RESPONSE_TABLE;

    Конечно, функция Paint() создает изображение окна, тогда как CmDialog() является функцией отклика на сообщение-команду CM_DIALOG, которая генерируется при выборе пользователем команды Test Dialog в меню Dialog. Класс окна диалога содержит только конструктор, в котором элементы управления диалогового окна связаны с объектами управления OWL:

class TDlg : public TDialog
{
  public:
    TDlg(TWindow *parent, TResId resId);
};

    Теперь, когда вы проанализировали классы рассматриваемой программы, обратим внимание на конструктор главного окна. Как обычно, этот конструктор добавляет меню окна, а также позиционирует и устанавливает его размеры. Этот конструктор также устанавливает буфер обмена диалогового окна. Чтобы проделать это, программа сначала копирует строку "Default" в поле буфера обмена, соответствующее области ввода, вследствие чего эта строка появляется в области ввода диалогового окна:

  strcpy(transBuf.edit, "Default");

    Затем программа устанавливает булевы значения, которые характеризуют состояние радио-кнопок и кнопок с независимой фиксацией:

  transBuf.radio1 = TRUE;
  transBuf.radio2 = FALSE;
  transBuf.radio3 = FALSE;
  transBuf.check1 = TRUE;
  transBuf.check2 = FALSE;
  transBuf.check3 = FALSE;

    Конструктор главного окна инициализирует значения диапазона линейки прокрутки и ее положения, устанавливая поля scrollerData.HighValue, scrollerData.LowValue и scrollerData.Position в их начальные значения:

  transBuf.scrollerData.HighValue = 100;
  transBuf.scrollerData.LowValue = 0;
  transBuf.scrollerData.Position = 50;

    После инициализации линейки прокрутки программа добавляет строку к объекту listBoxData:

  transBuf.listBoxData.AddString("ListString1", TRUE) ;
  transBuf.listBoxData.AddString("Liststring2");
  transBuf.listBoxData.AddString("ListString3");
  transBuf.listBoxData.AddString("ListString4");
  transBuf.listBoxData.AddString("ListString5");
  transBuf.listBoxData.AddString("ListString6");

    Строки, добавленные к объекту listBoxData, изображаются в окне списка при его появлении. В функции AddString() в качестве аргументов используется строка, добавляемая к объекту, некоторое булево значение, указывающее, должна ли быть эта строка изначально выбрана в окне списка. Так как этот второй аргумент по умолчанию имеет значение FALSE, вам лишь необходимо предусмотреть его для тех строк, которые вы хотите установить изначально выбранными.

    Объект comboBoxData получает свои строки точно таким же образом:

  transBuf.comboBoxData.AddString("ComboString1", TRUE);
  transBuf.comboBoxData.AddString("ComboString2");
  transBuf.comboBoxData.AddString("ComboString3");
  transBuf.comboBoxData.AddString("ComboString4");
  transBuf.comboBoxData.AddString("ComboString5");
  transBuf.comboBoxData.AddString("ComboString6");

    Когда пользователь выбирает команду Test Dialog в меню Dialog, OWL вызывает функцию главного окна CmDialog():

void TWndw::CmDialog()
{
  // Создать диалоговое окно.
  TDialog *dialog = new TDlg(this, DIALOG_1);
  // Отобразить диалоговое окно. 
  int result = dialog->Execute();
  // Проверить, вышел ли пользователь из 
  // диалогового окна по кнопке OK и
  // отобразить новые данные диалогового окна, если это так.
  if   (result == IDOK) Invalidate();
} 

    Эта функция сначала создает диалоговое окно Control Dialog, а затем вызывает dialog->Execute() для его отображения. Если пользователь выходит из окна по нажатию кнопки ОК, функция CmDialog() вызывает Invalidate(), которая генерирует сообщение WM_PAINT для окна, заставляя окно перерисовать свое отображение. Таким образом обеспечивается то, что любые изменения, внесенные пользователем в диалоговое окно, выводятся в текущее изображение.

    Функция Paint() сначала получает текущие параметры текста:

  paintDC.GetTextMetrics(textMetrics);

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

  paintDC.SetTextAlign(TA_RIGHT);

    Для того, чтобы расставить метки данных окна в правильных позициях, программе необходимо знать длину самой длинной метки:

  TSize size = paintDC.GetTextExtent("EDIT CONTROL:", 13);

    Затем, используя высоту текста, которая была возвращена в textMetrics.tmHeight функцией GetTextMetrics(), и ширину самой длинной метки, которая возвращена в size.cx функцией GetTextExtent(), программа печатает метки данных в окне:

  paintDC.TextOut(size.cx + 12, textMetrics.tmHeight, "EDIT CONTROL:"); 
  paintDC.TextOut(size.cx + 12, textMetrics.tmHeight*2, "RADIO BUTTON:"); 
  paintDC.TextOut(size.cx + 12, textMetrics.tmHeight*3, "SCROLL BAR:"); 
  paintDC.TextOut(size.cx + 12, textMetrics.tmHeight*4, "CHECK BOX 1:"); 
  paintDC.TextOut(size.cx + 12, textMetrics.tmHeight*5, "CHECK BOX 2:"); 
  paintDC.TextOut(size.cx + 12, textMetrics.tmHeight*6, "CHECK BOX 3:");
  paintDC.TextOut(size.cx + 12, textMetrics.tmHeight*7, "LIST BOX:");
  paintDC.TextOut(size.cx + 12, textMetrics.tmHeight*8, "COMBO BOX;");

    Затем функция Paint() должна печатать данные, следующие за соответствующими метками. Для этого она сначала устанавливает выравнивание текста влево:

  paintDC.SetTextAlign(TA_LEFT);

    Затем программа отделяет данные от их метрк, изменяя цвет текста на голубой:

  paintDC.SetTextColor(TColor::LtBlue);

    Для изображения содержимого области ввода программе надо всего лишь отобразить строку, хранящуюся в transBuf.edit:

  paintDC.TextOut(size.cx + 20, textMetrics.tmHeight, transBuf.edit);

    Однако для показа состояния радио-кнопок требуется, чтобы функция определила, какая кнопка выбрана, и затем выводила соответствующую строку сообщения:

  if (transBuf.radio1) num = 1; 
  else if (transBuf.radio2) num = 2; 
       else num = 3;
  wsprintf(s, "Radio button #%d chosen", num); 
  paintDC.TextOut(size.cx + 20, textMetrics.tmHeight*2, s);

    Чтобы изобразить текущее положение линейки прокрутки, программе надо только преобразовать целое число scrollerData.Position в строку символов и затем с помощью функции TextOut() отобразить эту строку:

  wsprintf(s, "%d", transBuf.scrollerData.Position); 
  paintDC.TextOut(size.cx + 20, textMetrics.tmHeight*3, s);

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

  if (transBuf.check1) strcpy (s, "Chosen"); 
  else strcpy(s, "Not chosen"); 
  paintDC.TextOut(size.cx + 20, textMetrics.tmHeight*4, s);

    Остальные две кнопки программа обрабатывает точно таким же образом. Чтобы изобразить текущую выбранную пользователем строку в окне списка, программа вызывает функцию-член GetSelString() объекта listBoxData и отображает возвращенную строку:

  transBuf.listBoxData.GetSelString(s, sizeof(s));
  paintDC.TextOut(size.cx + 20, textMetrics.tmHeight*7, s);

    В качестве аргумента GetSelString() требует указатель на символьный буфер, который содержит возвращенную строку и длину буфера. Программа отображает выбранную строку в комбинированном окне почти точно так же, как описано выше, за тем исключением, что теперь программа должна вызвать функцию-член GetSelString() объекта comboBoxData:

  transBuf.comboBoxData.GetSelString(s, sizeof(s));
  paintDC.TextOut(size.cx + 20, textMetrics.tmHeight*8, s);

    После того, как мы ознакомились с классом главного окна, рассмотрим теперь реализацию класса диалогового окна.

    Конструктор диалогового окна должен конструировать свои элементы управления в том же порядке, в каком они появляются в буфере обмена:

TDlg::TDlg(TWindow *parent, TResId resId): TDialog (parent, resId)
{
  // Построить OWL-объекты для управляющих элементов
  // в диалоговом окне. Механизм передачи работает 
  // правильно, если управляющие элементы OWL строятся 
  // в таком же порядке, в каком они появляются 
  // в структуре буфера обмена transBuf.
  new TEdit (this, ID_EDIT, sizeof(transBuf.edit));
  new TRadioButton (this, ID_RADIO1, 0);
  new TRadioButton (this, ID_RADIO2, 0);
  new TRadioButton (this, ID_RADIO3, 0);
  new TScrollBar (this, ID_SCROLLER);
  new TCheckBox (this, ID_CHECK1);
  new TCheckBox (this, ID_CHECK2);
  new TCheckBox (this, ID_CHECK3);
  new TListBox (this, ID_LISTBOX);
  new TComboBox (this, ID_COMBOBOX, 20);
  // Установить адрес буфера обмена.
  TransferBuffer = &transBuf;
}

    С конструктором TEdit вы уже неоднократно встречались. На 49 шаге вы познакомились с выше рассмотренной формой конструктора TRadioButton. Объект TScrollBar рассматривается впервые. В качестве аргументов в его конструкторе используются:

    Для аргумента TLibId OWL задает по умолчанию некоторое значение, и вам очень редко понадобится его менять, если вообще понадобится.

    Как и многие OWL-объекты, конструктор TScrollBar перегружается. Вторая форма конструктора позволяет создать линейку прокрутки, которая не связана с окном диалога. С этой формой конструктора вы познакомитесь на следующем шаге.

    В конструкторе класса TCheckBox в качестве аргументов используются указатель родительского окна, идентификатор ресурса пометки и объект TLibId. Как и обычно, OWL присваивает аргументу TLibId некоторое значение по умолчанию. Как и для большинства объектов управления окна в OWL, конструктор TCheckBox имеет вторую форму, которая позволяет вам создавать окно проверки, не связанное с диалоговым окном. Вы также познакомитесь с этой формой конструктора на следующем шаге.

    В качестве аргументов в конструкторе TListBox используются указатель родительского окна, идентификатор ресурса окна списка и объект TLibId, для которого OWL задает значение по умолчанию. Ниже вы познакомитесь со вторым конструктором TListBox, который создает окно списка, связанное с иным типом окна, чем диалоговое.

    Наконец, в качестве аргументов конструктора, ТСоmboВох используются указатель родительского окна, идентификатор ресурса комбинированного окна, максимальная длина текста в области редактирования и объект TlibId. OWL задает для аргумента TlibId значение по умолчанию. Как вы, возможно, догадались, конструктор TComboBox имеет вторую форму, которая позволяет вам создать комбинированное окно, отличное от диалогового. Ниже вы ознакомитесь со второй формой конструктора.

    После построения OWL-объектов для каждого из элементов управления в диалоговом окне, процесс заканчивается назначением полю класса TransBuffer диалогового окна адреса буфера обмена.

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




Предыдущий шаг Содержание Следующий шаг