Шаг 21.
Библиотека OWL.
Настраиваемый диалоговый класс

    На этом шаге мы рассмотрим создание и использование настраиваемого диалогового класса.

    После создания класса для вашего диалогового окна, вы можете добавить новые функции или переопределить унаследованные функции для улучшения работы диалогового окна. Например, вы можете использовать кнопки ОК и Cancel диалогового окна, переопределив свои собственные функции CmOk() и CmCancel(). Если вам хочется, вы можете предложить свою собственную функцию CanClose(), позволяющую вашей программе определять, можно ли закрыть диалоговое окно. Вы можете даже управлять "вручную" передачей данных из и в буфер обмена диалогового окна.

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

#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\dialog.h>
#include <owl\dc.h>
#include <owl\edit.h>
#include "pr21_1.rc"

// Структура буфера обмена.
struct TTransBuf
{
  char nameEdit[81];
  char addrEdit[81];
  char cityEdit[81];
  char stateEdit[3];
  char zipEdit[11];
  char phoneEdit[20];
  char birthEdit[9];
};

// Буфер обмена.
struct TTransBuf transBuf;


//Класс приложения
class TApp: public TApplication
{
  public:
	 TApp():TApplication(){}
	 void InitMainWindow();
};

//Класс основного окна
class TWndw:public TFrameWindow
{
  public:
	 TWndw(TWindow *parent, const char far *title);
  protected:
	 void CmTestDialog();
	 void Paint(TDC &paintDC,BOOL,TRect&);

	 DECLARE_RESPONSE_TABLE (TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TFrameWindow)
  EV_COMMAND(CM_TESTDIALOG,CmTestDialog),
END_RESPONSE_TABLE;

//Класс диалогового окна
class TDlg:public TDialog
{
  protected:
	  TTransBuf oldTransBuf;
  public:
	  TDlg(TWindow *parent, TResId resld);
  protected:
	  void SetupWindow();
	  void CmCancel();
	  void IdDefault();
	  void IdClear();

	  DECLARE_RESPONSE_TABLE(TDlg);
};

DEFINE_RESPONSE_TABLE1(TDlg, TDialog)
  EV_COMMAND(ID_DEFAULT, IdDefault),
  EV_COMMAND(ID_CLEAR, IdClear),
  EV_COMMAND(IDCANCEL, CmCancel),
END_RESPONSE_TABLE;


TWndw::TWndw(TWindow *parent, const char far *title):
		  TFrameWindow(parent,title)
{
	// Добавить меню к главному окну.
	AssignMenu("MENU_1");
	// Определить расположение и размеры окна.
	Attr.X=50;
	Attr.Y=50;
	Attr.W=GetSystemMetrics(SM_CXSCREEN)/3;
	Attr.H=GetSystemMetrics(SM_CXSCREEN)/4;
	// Инициализировать буфер обмена.
	memset (&transBuf, 0, sizeof(transBuf));
}

// TWndw::CmTestDialog()
// Эта функция реагирует на команду Dialog/Test
// меню Dialog.
void TWndw::CmTestDialog()
{
  // Создать окно диалога.
  TDialog *dialog = new TDlg(this, ADDRDLG);
  // Выполнить окно диалога.
  int result = dialog->Execute();
  // Если пользователь нажал кнопку ОК... 
  if (result == IDOK)
     // Заставить главное окно перерисоваться
     // с новыми данными диалогового окна. 
     Invalidate();
}

//TWndw::Paint()
// Эта функция переопределяет функцию
// TWindow Paint(), реагирует на сообщение WM_PAINT,
// которое Windows посылает окну в случае, когда
// окно должно перерисоваться.
void TWndw::Paint(TDC &paintDC,BOOL,TRect&)
{
  TEXTMETRIC tm;
  //Получить текущую текстовую информацию.
  paintDC.GetTextMetrics(tm);
  //Установить выравнивание вправо выводимого текста.
  paintDC.SetTextAlign(TA_RIGHT);
  //Определить размер самой длинной метки
  TSize size=paintDC.GetTextExtent("BIRTHDAY:",9);
  //Вывести все метки в рабочую область окна
  paintDC.TextOut(size.cx+12,tm.tmHeight,"NAME:");
  paintDC.TextOut(size.cx+12,tm.tmHeight*2,"ADDRESS:");
  paintDC.TextOut(size.cx+12,tm.tmHeight*3,"CITY:");
  paintDC.TextOut(size.cx+12,tm.tmHeight*4,"STATE:");
  paintDC.TextOut(size.cx+12,tm.tmHeight*5,"ZIP:");
  paintDC.TextOut(size.cx+12,tm.tmHeight*6,"PHONE:");
  paintDC.TextOut(size.cx+12,tm.tmHeight*7,"BIRTHDAY:");
  //Установить выравнивание влево выводимого на	печать текста.
  paintDC.SetTextAlign(TA_LEFT);
  //Напечатать даннные диалогового окна в
  //соответствующих позициях на экране,
  //основываясь на длине самой длинной метки и высоте текущего шрифта.
  paintDC.TextOut(size.cx+20,tm.tmHeight,transBuf.nameEdit);
  paintDC.TextOut(size.cx+20,tm.tmHeight*2,transBuf.addrEdit);
  paintDC.TextOut(size.cx+20,tm.tmHeight*3,transBuf.cityEdit);
  paintDC.TextOut(size.cx+20,tm.tmHeight*4,transBuf.stateEdit);
  paintDC.TextOut(size.cx+20,tm.tmHeight*5,transBuf.zipEdit);
  paintDC.TextOut(size.cx+20,tm.tmHeight*6,transBuf.phoneEdit);
  paintDC.TextOut(size.cx+20,tm.tmHeight*7,transBuf.birthEdit);
}

// ************************
// Реализация класса TDlg.
// ************************
// TDlg::TDlg()
// Конструктор диалогового окна.
TDlg::TDlg(TWindow *parent,TResId resId):
			TDialog(parent,resId)
{
  //Создаем управляющий объект  OWL для каждого
  //управляющего элемента диалогового окна.
  new TEdit(this,ID_NAME,sizeof(transBuf.nameEdit));
  new TEdit(this,ID_ADDR,sizeof(transBuf.addrEdit));
  new TEdit(this,ID_CITY,sizeof(transBuf.cityEdit));
  new TEdit(this,ID_STATE,sizeof(transBuf.stateEdit));
  new TEdit(this,ID_ZIP,sizeof(transBuf.zipEdit));
  new TEdit(this,ID_PHONE,sizeof(transBuf.phoneEdit));
  new TEdit(this,ID_BIRTH,sizeof(transBuf.birthEdit));
  //Назначить буфер обмена диалогового окна.
  TransferBuffer=&transBuf;
}

// TDlg::SetupWindow()
// Эта функция переопределяет SetupWindow() класса
// TDialog и вызывается перед выводом диалогового
// окна на экран. Она может использоваться для
// заключительной инициализации.
void TDlg::SetupWindow()
{
  // ВАЖНО! Необходимо выполнить обычную установку.
  TDialog::SetupWindow();
  // Сохранить текущие данные диалогового окна на
  // случай, если пользователь передумал и нажал кнопку Cancel.
  memcpy(&oldTransBuf, &transBuf, sizeof(transBuf));
}

// TDlg::IdDefault()
// Эта функция реагирует на нажатие кнопки Default
// диалогового окна, заполняя буфер обмена и диалоговое окно информацией,
// определенной по умолчанию.
void TDlg::IdDefault()
{
  //Копировать данные в буфер обмена.
  strcpy(transBuf.nameEdit,"Иван Иванович Иванов");
  strcpy(transBuf.addrEdit,"ул.Пушкина 23-51");
  strcpy(transBuf.cityEdit,"Курган");
  strcpy(transBuf.stateEdit,"RU");
  strcpy(transBuf.zipEdit,"640000");
  strcpy(transBuf.phoneEdit,"46-28-37");
  strcpy(transBuf.birthEdit,"13.10.72");
  //Поместить новые данные в диалоговое окно.
  TransferData(tdSetData);
}

// TDlg::IdClear()
// Эта функция реагирует на нажатие кнопки Clear
// диалогового окна, удаляя из него все данные.
void TDlg::IdClear()
{
  //Очистить буфер обмена.
  memset(&transBuf, 0, sizeof(transBuf));
  //Очистить диалоговое окно.
  TransferData(tdSetData);
}

// TDlg::CmCancel()
// Эта функция переопределяет функцию класса
// TDialog, реагирует на нажатие кнопки Cancel
// диалогового окна, восстанавливая буфер обмена
// в первоначальное состояние.
void TDlg::CmCancel()
{
  // Восстановить буфер обмена.
  memcpy(&transBuf, &oldTransBuf, sizeof(oldTransBuf));
  // Разрешить выполнение переопределенной функции CmCansel().
  TDialog::CmCancel();
}

void TApp::InitMainWindow()
{
  TFrameWindow *wndw= new TWndw(0,"Получение данных из диалогового окна 2");
  SetMainWindow(wndw);
  // Активизировать библиотеку DLL стандартных
  // настраиваемых элементов управления Borland.
  EnableBWCC();
}

int OwlMain(int,char *[])
{
  return TApp().Run();
}

    Файл ресурсов:

#ifndef WORKSHOP_INVOKED
#include "windows.h"
#endif

#define ADDRDLG         100
#define ID_NAME         101
#define ID_ADDR         102
#define ID_CITY         103
#define ID_STATE        104
#define ID_ZIP          105
#define ID_PHONE        106
#define ID_BIRTH        107
#define ID_DEFAULT      109
#define ID_CLEAR        110
#define CM_EXIT       24310
#define CM_TESTDIALOG   108


#ifdef RC_INVOKED

ADDRDLG DIALOG 39, 34, 239, 124
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Address Form"
FONT 8, "MS Sans Serif"
{
 EDITTEXT ID_NAME, 42, 9, 188, 12, WS_BORDER | WS_TABSTOP
 EDITTEXT ID_ADDR, 42, 26, 188, 12, WS_BORDER | WS_TABSTOP
 EDITTEXT ID_CITY, 42, 44, 70, 12, WS_BORDER | WS_TABSTOP
 EDITTEXT ID_STATE, 140, 45, 16, 12, WS_BORDER | WS_TABSTOP
 EDITTEXT ID_ZIP, 180, 45, 50, 12, WS_BORDER | WS_TABSTOP
 EDITTEXT ID_PHONE, 43, 67, 65, 12, WS_BORDER | WS_TABSTOP
 EDITTEXT ID_BIRTH, 147, 67, 45, 12, WS_BORDER | WS_TABSTOP
 LTEXT "Name:", -1, 17, 11, 20, 8
 LTEXT "Address:", -1, 9, 28, 29, 8
 LTEXT "City:", -1, 23, 47, 17, 8
 LTEXT "State:", -1, 116, 47, 20, 8
 LTEXT "Zip:", -1, 163, 46, 13, 8
 LTEXT "Phone:", -1, 16, 69, 24, 8
 LTEXT "Birthday:", -1, 113, 69, 30, 8
 DEFPUSHBUTTON "OK", IDOK, 12, 93, 37, 25
 PUSHBUTTON "Cancel", IDCANCEL, 62, 93, 39, 21
 PUSHBUTTON "Default", ID_DEFAULT 175, 98, 50, 14
 PUSHBUTTON "Clear",   ID_CLEAR,  116, 98, 50, 14
}


MENU_1 MENU
{
 POPUP "&File"
 {
  MENUITEM "E&xit", CM_EXIT
 }
 POPUP "&Dialog"
 {
  MENUITEM "&Test Dialog...", CM_TESTDIALOG
 }
}

#endif
Текст этого приложения можно взять здесь.

    Когда вы запустите эту программу и выберите команду Test Dialog пункта меню Dialog, вы увидите экран, изображенный на рисунке 1.


Рис.1. Результат работы приложения

    Теперь диалоговое окно имеет две новые кнопки. Кнопка Clear удаляет все данные из диалогового окна, а кнопка с названием Default наполняет строки редактирования диалогового окна данными по умолчанию.

    Все дополнительные функции диалогового окна в этой программе обеспечиваются классом диалогового окна. Этот класс определяет свою собственную структуру TTransBuf для содержания копии первоначального буфера обмена, а также функции для инициализации диалогового окна и реакции на кнопки Clear, Default и Cancel. Кнопка OK сохраняет свое определенное по умолчанию поведение.

    Когда пользователь активизирует диалоговое окно, конструктор создает управляющие объекты для управляющих элементов, которые будут участвовать в процессе передачи, и устанавливает значения поля TransferBuffer диалогового окна так, что он указывает на буфер обмена. Перед непосредственным выводом диалогового окна на экран OWL вызывает свою функцию SetupWindow(), которая переопределена в классе диалогового окна:

void TDlg::SetupWindow()
{
  // ВАЖНО! Необходимо выполнить обычную установку.
  TDialog::SetupWindow();
  // Сохранить текущие данные диалогового окна на
  // случай, если пользователь передумал и нажал кнопку Cancel.
  memcpy(&oldTransBuf, &transBuf, sizeof(transBuf));
}

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

    Затем диалоговое окно появляется на экране. Если пользователь выберет кнопку Default, выполняется функция IdDefault():

void TDlg::IdDefault()
{
  //Копировать данные в буфер обмена.
  strcpy(transBuf.nameEdit,"Иван Иванович Иванов");
  strcpy(transBuf.addrEdit,"ул.Пушкина 23-51");
  strcpy(transBuf.cityEdit,"Курган");
  strcpy(transBuf.stateEdit,"RU");
  strcpy(transBuf.zipEdit,"640000");
  strcpy(transBuf.phoneEdit,"46-28-37");
  strcpy(transBuf.birthEdit,"13.10.72");
  //Поместить новые данные в диалоговое окно.
  TransferData(tdSetData);
}

    Эта функция сначала использует strcpy() для копирования информации об адресе в буфер обмена диалогового окна. Затем она вызывает функцию TransferData() класса диалогового окна (унаследованную из TWindow) для копирования содержимого буфера обмена в управляющие элементы диалогового окна. Единственным аргументом TransferData() является значение, определяющее направление передачи. OWL определяет константы для этого значения. TF_SETDATA копирует данные из буфера обмена в диалоговое окно. TF_GETDATA копирует данные из диалогового окна в буфер обмена, а TF_SIZEDATA возвращает размер данных в байтах без выполнения передачи.

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

void TDlg::IdClear()
{
  //Очистить буфер обмена.
  memset(&transBuf, 0, sizeof(transBuf));
  //Очистить диалоговое окно.
  TransferData(tdSetData);
}

    Эта функция просто вызывает memset() для копирования всех нулей в передающий буфер обмена, а затем вызывает TransferData() для копирования нулевых строк в диалоговое окно. Даже после выполнения этой операции структура oldTransBuf все еще сохраняет первоначальное содержание буфера обмена. Программе требуются эти данные в случае, когда пользователь выбирает кнопку Cancel в существующем диалоговом окне, что приводит к вызову функции CmCancel():

void TDlg::CmCancel()
{
  // Восстановить буфер обмена.
  memcpy(&transBuf, &oldTransBuf, sizeof(oldTransBuf));
  // Разрешить выполнение переопределенной функции CmCansel().
  TDialog::CmCancel();
}

    Эта функция переопределяет функцию CmCancel() класса TDialog и вызывается, когда пользователь нажал кнопку с идентификатором ресурса IDCANCEL. Поскольку пользователь решил отменить произведенные в диалоговом окне изменения данных, CmCancel() должна восстановить в первоначальное состояние буфер обмена. Функция выполняет это посредством вызова memcpy() для копировании содержимого oldTransBuf в transBuf.

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




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