Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система Windows 95 для программиста

© Александр Фролов, Григорий Фролов
Том 22, М.: Диалог-МИФИ, 1993, 271 стр.

[Назад] [Содеожание] [Дальше]

6.4. Приложение Property Sheet Demo

Приложение Property Sheet Demo демонстрирует способ создания простейшего блокнота, который появляется при выборе строки Options из меню File.

С помощью блокнота заполняется произвольно выбранная нами структура параметров, приведенная ниже:

typedef struct 
{
  int  nBold;
  int  nItalic;
  int  nUnderline;
  int  nUseTabs;
  char szKeyWord[80];
} OPTIONS;

Первая страница блокнота, которая называется Set Effects, показана на рис. 6.2.

Рис. 6.2. Первая страница блокнота

На ней расположены переключатели с независимой фиксацией Bold, Italic и Underline. Если изменить состояние одного из этих переключателей, разблокируется кнопка Apply (которая изначально находится в заблокированном состоянии).

На второй странице блокнота с названием Using Tabs (рис. 6.3) находятся два переключателя с зависимой фиксацией Use Tabs и Don't use Tabs.

Рис. 6.3. Вторая страница блокнота

Кнопка Apply разблокируется автоматически при выборе страницы Using Tabs, что достигается соответствующей обработкой извещения PSN_SETACTIVE.

С помощью страницы Keyword (рис. 6.4) пользователь может ввести некоторое ключевое слово, длина которого не должна превышать 8 символов.

Рис. 6.4. Третья страница блокнота

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

Если длина нового ключевого слова превышает 8 символов, на экране появляется сообщение, показанное на рис. 6.5.

Рис. 6.5.Сообщение о превышении длины ключевого слова

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

Исходные тексты приложения Property Sheet Demo

Функции приложения Property Sheet Demo определены в файле psheet.c (листинг 6.1).

Листинг 6.1. Файл psheet\psheet.c



#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "resource.h"
#include "afxres.h"
#include "psheet.h"

// Структура, в которой хранятся параметры
typedef struct 
{
  int  nBold;
  int  nItalic;
  int  nUnderline;
  int  nUseTabs;
  char szKeyWord[80];
} OPTIONS;

OPTIONS opt;

// Массив описаний страниц блокнота
PROPSHEETPAGE   psheetPage[3];

// Заголовок блокнота
PROPSHEETHEADER psheetHeader;

// Идентификаторы страниц блокнота
HPROPSHEETPAGE hPage[3];

HINSTANCE hInst;
char szAppName[]  = "PropSheetApp";
char szAppTitle[] = "Property Sheet Demo";

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  hInst = hInstance;

  // Преверяем, не было ли это приложение запущено ранее
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    if(IsIconic(hWnd))
      ShowWindow(hWnd, SW_RESTORE);
    SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Регистрируем класс окна
  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
      return FALSE;
    
  // Создаем главное окно приложения
  hWnd = CreateWindow(szAppName, szAppTitle,
   WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
   NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);

  // Отображаем окно и запускаем цикл обработки сообщений
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage (&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hWnd, WM_CREATE,     WndProc_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY,    WndProc_OnDestroy);
    HANDLE_MSG(hWnd, WM_COMMAND,    WndProc_OnCommand);

    default:
      return(DefWindowProc(hWnd, msg, wParam, lParam));
  }
}

// -----------------------------------------------------
// Функция WndProc_OnCreate
// -----------------------------------------------------
BOOL WndProc_OnCreate(HWND hWnd, 
  LPCREATESTRUCT lpCreateStruct)
{
  // Инициализируем библиотеку стандартных органов управления
  InitCommonControls();

  // Инициализируем параметры
  opt.nBold =
  opt.nItalic =
  opt.nUnderline =
  opt.nUseTabs = 0;
  strcpy(opt.szKeyWord, "Undef");

  return TRUE;
}

// -----------------------------------------------------
// Функция WndProc_OnDestroy
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnDestroy(HWND hWnd)
{
  PostQuitMessage(0);
  return 0L;
}

// -----------------------------------------------------
// Функция WndProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  switch (id)
  {
    case ID_FILE_EXIT:  
      PostQuitMessage(0); // завершаем работу приложения
      return 0L;
      break;
      
      case ID_HELP_ABOUT:
      MessageBox(hWnd, 
        "Property Sheet Demo Application, v.1.0\n"
        "(C) Alexandr Frolov, 1995\n"
        "Email: frolov@glas.apc.org",
        szAppTitle, MB_OK | MB_ICONINFORMATION);
      return 0L;
      break;

    case ID_FILE_OPTIONS:  
    {
      // Инициализируем страницы блокнота  
      psheetPage[0].dwSize = sizeof(PROPSHEETPAGE);
      psheetPage[0].hInstance = hInst;
      psheetPage[0].pszIcon = MAKEINTRESOURCE(IDI_EFFECTS);
      psheetPage[0].dwFlags = PSP_USETITLE | PSP_USEICONID;
      psheetPage[0].pszTemplate = 
        MAKEINTRESOURCE(IDD_DIALOG1);
      psheetPage[0].pfnDlgProc = DlgProc1;
      psheetPage[0].pszTitle = "Set Effects";
      psheetPage[0].lParam = 0;

      // Добавляем страницу в блокнот, сохраняя ее
      // идентификатор в массиве hPage
      hPage[0] = CreatePropertySheetPage(&psheetPage[0]);

      psheetPage[1].dwSize = sizeof(PROPSHEETPAGE);
      psheetPage[1].hInstance = hInst;
      psheetPage[1].pszIcon = MAKEINTRESOURCE(IDI_TAB);
      psheetPage[1].dwFlags = PSP_USETITLE | PSP_USEICONID;
      psheetPage[1].pszTemplate = 
        MAKEINTRESOURCE(IDD_DIALOG2);
      psheetPage[1].pfnDlgProc = DlgProc2;
      psheetPage[1].pszTitle = "Using Tabs";
      psheetPage[1].lParam = 0;
      hPage[1] = CreatePropertySheetPage(&psheetPage[1]);

      psheetPage[2].dwSize = sizeof(PROPSHEETPAGE);
      psheetPage[2].hInstance = hInst;
      psheetPage[2].pszIcon = MAKEINTRESOURCE(IDI_KEYWORD);
      psheetPage[2].dwFlags = PSP_USETITLE | PSP_USEICONID;
      psheetPage[2].pszTemplate = 
        MAKEINTRESOURCE(IDD_DIALOG3);
      psheetPage[2].pfnDlgProc = DlgProc3;
      psheetPage[2].pszTitle = "Keyword";
      psheetPage[2].lParam = 0;
      hPage[2] = CreatePropertySheetPage(&psheetPage[2]);

      // Инициализируем заголовок блокнота
      psheetHeader.dwSize = sizeof(PROPSHEETHEADER);
      psheetHeader.hInstance = hInst;
      psheetHeader.pszIcon = MAKEINTRESOURCE(IDI_APPICONSM);
      psheetHeader.dwFlags = PSH_USEICONID;
      psheetHeader.hwndParent = hWnd;
      psheetHeader.pszCaption = "Property Sheet Sample";
      psheetHeader.nPages = 
        sizeof(psheetPage) / sizeof(PROPSHEETPAGE);
      psheetHeader.phpage = (HPROPSHEETPAGE FAR  *)&hPage[0];
      
      // Создаем и отображаем блокнот
      PropertySheet(&psheetHeader);

      return 0L;
      break;
    }
    default:
    break;
  }
  return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, 
    codeNotify, DefWindowProc);
}

// -----------------------------------------------------
// Функция DlgProc1
// для первой страницы блокнота
// -----------------------------------------------------
BOOL APIENTRY
DlgProc1(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc1_OnInitDialog);
    HANDLE_MSG(hdlg, WM_COMMAND,    DlgProc1_OnCommand);
    HANDLE_MSG(hdlg, WM_NOTIFY,     DlgProc1_OnNotify);
    default:
      break;
  }
  return FALSE;
}

// -----------------------------------------------------
// Функция DlgProc1_OnInitDialog
// Вызывается при инициализации первой страницы блокнота
// -----------------------------------------------------
BOOL DlgProc1_OnInitDialog(HWND hdlg, 
  HWND hwndFocus, LPARAM lParam)
{
  // Устанавливаем переключатели в соответствии
  // со значениями параметров, записанных в 
  // структуре opt
  SendMessage(GetDlgItem(hdlg, IDC_BOLD), 
    BM_SETCHECK, opt.nBold, 0L);

  SendMessage(GetDlgItem(hdlg, IDC_ITALIC), 
    BM_SETCHECK, opt.nItalic, 0L);

  SendMessage(GetDlgItem(hdlg, IDC_UNDERLINE), 
    BM_SETCHECK, opt.nUnderline, 0L);
  return TRUE;
}

// -----------------------------------------------------
// Функция DlgProc1_OnNotify
// Обрабатывает извещение от первой страницы блокнота
// -----------------------------------------------------
LRESULT 
DlgProc1_OnNotify(HWND hdlg, int idFrom, NMHDR* pnmhdr)
{
  switch(pnmhdr->code)
  {
    // Когда пользователь нажимает кнопку OK, получаем
    // состояние переключателей, расположенных в первой
    // странице блокнота, и сохраняем это состояние в
    // соответствующих полях структуры opt
    case PSN_APPLY:
    {
      opt.nBold = (int)SendMessage(
        GetDlgItem(hdlg, IDC_BOLD), BM_GETCHECK, 0L, 0L);

      opt.nItalic = (int)SendMessage(
        GetDlgItem(hdlg, IDC_ITALIC), BM_GETCHECK, 0L, 0L);

      opt.nUnderline = (int)SendMessage(
        GetDlgItem(hdlg, IDC_UNDERLINE), BM_GETCHECK, 0L, 0L);
      break;
    }
    default:
      break;
  }
  return FALSE;
}

// -----------------------------------------------------
// Функция DlgProc1_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void DlgProc1_OnCommand(HWND hdlg, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  // Разблокируем кнопку "Apply"
  PropSheet_Changed(GetParent(hdlg), hdlg);
  return NULL;
}

// -----------------------------------------------------
// Функция DlgProc2
// для второй страницы блокнота
// -----------------------------------------------------
BOOL APIENTRY
DlgProc2(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc2_OnInitDialog);
    HANDLE_MSG(hdlg, WM_NOTIFY,     DlgProc2_OnNotify);
    default:
      break;
  }
  return FALSE;
}

// -----------------------------------------------------
// Функция DlgProc2_OnInitDialog
// Вызывается при инициализации второй страницы блокнота
// -----------------------------------------------------
BOOL DlgProc2_OnInitDialog(HWND hdlg, 
  HWND hwndFocus, LPARAM lParam)
{
  SendMessage(GetDlgItem(hdlg, IDC_USETABS), 
    BM_SETCHECK, opt.nUseTabs, 0L);

  SendMessage(GetDlgItem(hdlg, IDC_DONTUSETABS), 
    BM_SETCHECK, (opt.nUseTabs) ? 0 : 1, 0L);

  return TRUE;
}

// -----------------------------------------------------
// Функция DlgProc2_OnNotify
// Обрабатывает извещение от второй страницы блокнота
// -----------------------------------------------------
LRESULT 
DlgProc2_OnNotify(HWND hdlg, int idFrom, NMHDR* pnmhdr)
{
  switch(pnmhdr->code)
  {
    case PSN_SETACTIVE:
    {
      // Разблокируем кнопку Apply, когда страница
      // блокнота становится активной
      PropSheet_Changed(GetParent(hdlg), hdlg);
      break;
    }

    case PSN_KILLACTIVE:
    {
      // Блокируем кнопку Apply, когда страница
      // блокнота становится неактивной
      PropSheet_UnChanged(GetParent(hdlg), hdlg);
      break;
    }

    case PSN_APPLY:
    {
      opt.nUseTabs = (int)SendMessage(
        GetDlgItem(hdlg, IDC_USETABS), 
        BM_GETCHECK, 0L, 0L);
      break;
    }
    default:
      break;
  }
  return FALSE;
}

// -----------------------------------------------------
// Функция DlgProc3
// для третьей страницы блокнота
// -----------------------------------------------------
BOOL APIENTRY
DlgProc3(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc3_OnInitDialog);
    HANDLE_MSG(hdlg, WM_NOTIFY,     DlgProc3_OnNotify);
    HANDLE_MSG(hdlg, WM_COMMAND,    DlgProc3_OnCommand);
    default:
      break;
  }
  return FALSE;
}

// -----------------------------------------------------
// Функция DlgProc3_OnInitDialog
// Вызывается при инициализации третьей страницы блокнота
// -----------------------------------------------------
BOOL DlgProc3_OnInitDialog(HWND hdlg, 
  HWND hwndFocus, LPARAM lParam)
{
  SetWindowText(GetDlgItem(hdlg, IDC_EDITKEYWORD), 
    opt.szKeyWord);

  return TRUE;
}

// -----------------------------------------------------
// Функция DlgProc3_OnNotify
// Обрабатывает извещение от третьей страницы блокнота
// -----------------------------------------------------
LRESULT 
DlgProc3_OnNotify(HWND hdlg, int idFrom, NMHDR* pnmhdr)
{
  switch(pnmhdr->code)
  {
    // Выполняем проверку длины ключевого слова,
    // которое не должно быть длиннее 8 символов
    case PSN_KILLACTIVE:
    {
      char szTempBuf[80];

      // Получаем новое ключевое слово во временный буфер
      GetWindowText(GetDlgItem(hdlg, IDC_EDITKEYWORD), 
        szTempBuf, 80);

      // Проверяем его длину
      if(lstrlen(szTempBuf) > 8)
      {
        // Если длина больше 8 символов, выводим
        // предупреждающее сообщение
        MessageBox(NULL, "Too long keyword, "
          "must be shorter than 8 characters", 
          szAppName,  MB_OK | MB_ICONEXCLAMATION);

        // Отменяем закрытие страницы блокнота
        SetWindowLong(hdlg, DWL_MSGRESULT, TRUE);

        // Отменяем обновление параметров
        return TRUE;
      }
      else
      {
        // Если длина сообщения правильная, разрешаем
        // закрытие страницы блокнота и
        // обновление параметров
        SetWindowLong(hdlg, DWL_MSGRESULT, FALSE);
        return FALSE;
      }
      break;
    }

    case PSN_APPLY:
    {
      GetWindowText(GetDlgItem(hdlg, IDC_EDITKEYWORD), 
        opt.szKeyWord, 80);
      break;
    }
    default:
      break;
  }
  return FALSE;
}

// -----------------------------------------------------
// Функция DlgProc3_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void DlgProc3_OnCommand(HWND hdlg, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  // Сообщение от редактора текста
  if(id == IDC_EDITKEYWORD)
  {
    // Если пользователь изменил ключевое слово в окне
    // редактирования, разблокируем кнопку "Apply"
    if(codeNotify == EN_CHANGE)
    {
      PropSheet_Changed(GetParent(hdlg), hdlg);
    }
  }
  return FALSE;
}

В файле psheet.h (листинг 6.2) находятся описания функций, определенных в приложении Property Sheet Demo.

Листинг 6.2. Файл psheet\psheet.h



// -----------------------------------------------------
// Описание функций
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL WndProc_OnCreate(HWND hWnd, 
  LPCREATESTRUCT lpCreateStruct);
void WndProc_OnDestroy(HWND hWnd);
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify);
LRESULT WndProc_OnNotify(HWND hWnd, int idFrom, 
  NMHDR FAR * pnmhdr);
BOOL APIENTRY
DlgProc1(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL DlgProc1_OnInitDialog(HWND hwnd, 
  HWND hwndFocus, LPARAM lParam);

LRESULT DlgProc1_OnNotify(HWND hWnd, int idFrom, 
  NMHDR* pnmhdr);
void DlgProc1_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify);
BOOL APIENTRY
DlgProc2(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL DlgProc2_OnInitDialog(HWND hwnd, 
  HWND hwndFocus, LPARAM lParam);
LRESULT DlgProc2_OnNotify(HWND hWnd, int idFrom, 
  NMHDR* pnmhdr);
BOOL APIENTRY
DlgProc3(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL DlgProc3_OnInitDialog(HWND hwnd, 
  HWND hwndFocus, LPARAM lParam);
LRESULT DlgProc3_OnNotify(HWND hWnd, int idFrom, 
  NMHDR* pnmhdr);
void DlgProc3_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify);

В файле resource.h (создается автоматически системой Microsoft Visual C++) находятся определения констант для работы с ресурсами приложения Property Sheet Demo. Этот файл представлен в листинге 6.3.

Листинг 6.3. Файл psheet\resource.h


//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by PSHEET.RC
//
#define IDR_APPMENU                     102
#define IDI_APPICON                     103
#define IDI_APPICONSM                   104
#define IDI_EFFECTS                     105
#define IDI_TAB                         106
#define IDI_KEYWORD                     107
#define IDD_DIALOG1                     121
#define IDD_DIALOG2                     122
#define IDD_DIALOG3                     123
#define IDC_BOLD                        1000
#define IDC_ITALIC                      1001
#define IDC_UNDERLINE                   1002
#define IDC_USETABS                     1004
#define IDC_EDITKEYWORD                 1006
#define IDC_DONTUSETABS                 1007
#define ID_FILE_EXIT                    40001
#define ID_HELP_ABOUT                   40003
#define ID_FILE_OPTIONS                 40029

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        125
#define _APS_NEXT_COMMAND_VALUE         40030
#define _APS_NEXT_CONTROL_VALUE         1008
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

Файл psheet.rc (листинг 6.4) содержит определение ресурсов приложения Property Sheet Demo. Он создается автоматически.

Листинг 6.4. Файл psheet\psheet.rc


//Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////
// Menu
//

IDR_APPMENU MENU DISCARDABLE 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Options...", ID_FILE_OPTIONS
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       ID_FILE_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",                   ID_HELP_ABOUT
    END
END

#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

//////////////////////////////////////////////////////////////
#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////
// Icon
//

IDI_APPICON             ICON    DISCARDABLE     "psheet.ico"
IDI_APPICONSM           ICON    DISCARDABLE     "psheetsm.ico"
IDI_EFFECTS             ICON    DISCARDABLE     "EFFECTS.ICO"
IDI_TAB                 ICON    DISCARDABLE     "TAB.ICO"
IDI_KEYWORD             ICON    DISCARDABLE     "KEYWORD.ICO"

//////////////////////////////////////////////////////////////
// Dialog
//

IDD_DIALOG1 DIALOG DISCARDABLE  0, 0, 186, 95
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Sans Serif"
BEGIN
  CONTROL "Bold",IDC_BOLD,"Button",
    BS_AUTOCHECKBOX | WS_TABSTOP,17,23,35,10
  CONTROL  "Italic",IDC_ITALIC,"Button",
    BS_AUTOCHECKBOX | WS_TABSTOP,17,34,35,10
  CONTROL "Underline",IDC_UNDERLINE,"Button",
    BS_AUTOCHECKBOX | WS_TABSTOP,17,45,46,10
  GROUPBOX        "Effects",IDC_STATIC,6,9,112,53
END

IDD_DIALOG2 DIALOG DISCARDABLE  0, 0, 186, 95
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Sans Serif"
BEGIN
  CONTROL  "Use Tabs", IDC_USETABS, "Button", 
  BS_AUTORADIOBUTTON,18,22, 63,10
  GROUPBOX "Tabs",IDC_STATIC,9,7,105,63
  CONTROL  "Don't use Tabs",IDC_DONTUSETABS,"Button",
           BS_AUTORADIOBUTTON,18,35,83,10
END

IDD_DIALOG3 DIALOG DISCARDABLE  0, 0, 211, 102
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Sans Serif"
BEGIN
  EDITTEXT IDC_EDITKEYWORD,22,35,138,13,ES_AUTOHSCROLL
  GROUPBOX "Keyword",IDC_STATIC,9,15,166,52
END

//////////////////////////////////////////////////////////////
// String Table
//

STRINGTABLE DISCARDABLE 
BEGIN
    ID_FILE_EXIT            "Quits the application"
END

#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 3 resource.
//////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

Описание функций

Займемся описанием глобальных переменных и функций приложения Property Sheet Demo.

Глобальные переменные

Структура opt, имеющая тип OPTIONS, содержит параметры, которые настраиваются с помощью блокнота. Тип OPTIONS определен в нашем приложении.

Массив структур psheetPage типа PROPSHEETPAGE хранит описания страниц блокнота. Заголовок блокнота записан в структуре psheetHeader типа PROPSHEETHEADER.

Идентификаторы отдельных страниц, создаваемых функцией CreatePropertySheetPage, записываются в массив структур hPage типа HPROPSHEETPAGE.

В переменной hInst хранится идентификатор приложения, полученный функцией WinMain. Строчные массивы szAppName и szAppTitle хранят, соответственно, имя и заголовок приложения.

WinMain

Функция WinMain не имеет никаких особенностей, поэтому для экономии места в книге мы не будем ее описывать.

WndProc

Функция WndProc обрабатывает следующие сообщения: WM_CREATE, WM_DESTROY и WM_COMMAND. Обработка выполняется с использованием макрокоманды HANDLE_MSG.

WndProc_OnCreate

Обработчик сообщения WM_CREATE инициализирует библиотеку стандартных органов управления, вызывая для этого функцию InitCommonControls.

Затем он выполняет начальную инициализацию полей структуры параметров opt.

WndProc_OnDestroy

Функция WndProc_OnDestroy вызывается, когда пользователь завершает работу приложения. Она останавливает цикл обработки сообщений, вызывая функцию PostQuitMessage.

WndProc_OnCommand

Обработчики сообщений от строк Exit и About действуют также, как и в предыдущем приложении.

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

Блокнот создается и отображается функцией PropertySheet, которой в качестве единственного параметра передается адрес заполненной структуры заголовка блокнота.

DlgProc1

Функция DlgProc1 - это функция диалога для первой страницы блокнота. Она обрабатывает сообщения WM_INITDIALOG, WM_COMMAND и WM_NOTIFY, вызывая для них, соответственно, функции DlgProc1_OnInitDialog, DlgProc1_OnCommand и DlgProc1_OnNotify.

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

DlgProc1_OnInitDialog

Функция DlgProc1_OnInitDialog обрабатывает сообщение WM_INITDIALOG, которое поступает в функцию диалога первой страницы блокнота при инициализации последней.

Единственное, что она делает, это устанавливает переключатели, расположенные на первой странице, в соответствии с содержимым полей структуры параметров opt:

SendMessage(GetDlgItem (hdlg, IDC_BOLD), 
    BM_SETCHECK, opt.nBold, 0L);

Метод установки состояния переключателей в диалоговой панели основан на использовании сообщения BM_SETCHECK. Это сообщение, а также функция GetDlgItem, возвращающая идентификатор окна органа управления, расположенного в диалоговой панели, были подробно описаны в 12 томе "Библиотеки системного программиста".

DlgProc1_OnNotify

Функция DlgProc1_OnNotify обрабатывает извещения, которые поступают в функцию диалога первой страницы в форме сообщения WM_NOTIFY.

В ответ на извещение PSN_APPLY функция DlgProc1_OnNotify получает состояние переключателей и записывает его в соответствующие поля структуры opt:

opt.nBold = (int)SendMessage(GetDlgItem(hdlg, IDC_BOLD), 
  BM_GETCHECK, 0L, 0L);

Для определения состояния переключателей им посылается сообщение BM_GETCHECK.

DlgProc1_OnCommand

Функция DlgProc1_OnCommand обрабатывает сообщение WM_COMMAND, поступающее от переключателей, расположенных на первой странице.

Как только пользователь нажмет какую-либо из имеющихся на первой странице блокнота кнопку, функция DlgProc1_OnCommand разблокирует кнопку Apply, посылая блокноту сообщение PSM_CHANGED:

PropSheet_Changed(GetParent(hdlg), hdlg);

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

DlgProc2

Функция DlgProc2 является функцией диалога для второй страницы блокнота. Она обрабатывает сообщения WM_INITDIALOG и WM_NOTIFY, вызывая для них, соответственно, функции DlgProc2_OnInitDialog и DlgProc2_OnNotify.

DlgProc2_OnInitDialog

Функция DlgProc2_OnInitDialog обрабатывает сообщение WM_INITDIALOG, которое поступает в функцию диалога второй страницы блокнота при ее инициализации.

Ее задача аналогична задаче функции DlgProc1_OnInitDialog - установка переключателей, расположенных на второй странице в соответствии с содержимым полей структуры параметров opt.

Переключатель с идентификатором IDC_DONTUSETABS всегда устанавливается в состояние, противоположное состоянию переключателя IDC_USETABS.

DlgProc2_OnNotify

Функция DlgProc2_OnNotify обрабатывает извещения, которые поступают в функцию диалога второй страницы.

Когда пользователь переключается на вторую страницу, делая ее активной, функция диалога получает извещение PSN_SETACTIVE. Обработчик этого извещения разблокирует кнопку Apply, вызывая для этого макрокоманду PropSheet_Changed.

Извещение PSN_KILLACTIVE поступает в функцию диалога, когда пользователь завершил работу с текущей страницей блокнота и пытается переключиться на другую страницу. При этом кнопка Apply блокируется при помощи макрокоманды PropSheet_UnChanged :

PropSheet_UnChanged(GetParent(hdlg), hdlg);

В ответ на извещение PSN_APPLY функция DlgProc2_OnNotify получает состояние переключателя IDC_USETABS и записывает его в поле nUseTabs структуры opt.

DlgProc3

Функция DlgProc3 является функцией диалога третьей страницы блокнота. Она обрабатывает сообщения WM_INITDIALOG, WM_COMMAND и WM_NOTIFY, вызывая для них, соответственно, функции DlgProc3_OnInitDialog, DlgProc3_OnCommand и DlgProc3_OnNotify.

DlgProc3_OnInitDialog

Функция DlgProc3_OnInitDialog устанавливает содержимое текстового редактора IDC_EDITKEYWORD, записывая в него строку из поля opt.szKeyWord.

DlgProc3_OnNotify

Функция DlgProc3_OnNotify обрабатывает извещения, которые поступают в функцию диалога второй страницы.

Если пользователь изменил текст в окне редактора IDC_EDITKEYWORD, обработчик сообщения WM_COMMAND (будет описан ниже) разблокирует кнопку Apply. Если теперь пользователь попытается нажать эту кнопку или кнопку OK, либо попытается переключиться на другую страницу блокнота, функция диалога третьей страницы получит извещение PSN_KILLACTIVE.

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

Для блокирования обработчик извещения PSN_KILLACTIVE возвращает значение TRUE, установив предварительно код завершения TRUE в структуре окна диалога с помощью функции SetWindowLong :

SetWindowLong(hdlg, DWL_MSGRESULT, TRUE);
return TRUE;

Если же длина полученной текстовой строки меньше 8 символов, обработчик извещения PSN_KILLACTIVE возвращает значение FALSE:

SetWindowLong(hdlg, DWL_MSGRESULT, FALSE);
return FALSE;

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

DlgProc3_OnCommand

Кнопка Apply, расположенная на третьей странице блокнота, разблокируется только в том случае, когда пользователь изменил ключевое слово. Для этого обработчик сообщения WM_COMMAND, реализованный функцией DlgProc3_OnCommand, отслеживает извещение EN_CHANGE. Это извещение поступает от окна текстового редактора в форме сообщения WM_COMMAND в том случае, когда пользователь изменил редактируемый текст (см. том 12 "Библиотеки системного программиста").

Разблокировка кнопки Apply выполняется при помощи макрокоманды PropSheet_Changed.

[Назад] [Содеожание] [Дальше]