15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Использование стандартных диалогов.

Содержание:

Диалоговое окошко выбора цвета

Для того, чтобы показать диалоговое окошко, позволяющее пользователю выбирать Цвет, сперва необходимо инициализировать структуру CHOOSECOLOR, а затем вызвать функцию ChooseColor. Если пользователь успешно выбрал цвет, то функция вернёт TRUE. В примере, выбранный цвет используется для создания новой кисти.

Струтктура CHOOSECOLOR инициализируется следующим образом:

  • Переменной lpCustColors присваивается значение указателя на статический массив значений. Изначально, цвета в массиве чёрные, однако, массив сделан статическим, чтобы запомнить выбранные пользователем дополнительные цвета.
  • Устанавливаем флаг CC_RGBINIT и присваиваем переменной rgbResult значение цвета, который будет выбран изначально при открытии диалогового окна. Если изначальный цвет не будет указан, то будет выбран чёрный цвет. Переменная rgbCurrent сделана статической, чтобы запомнить последний выбранный пользователем цвет.
  • Устанавливаем флаг CC_FULLOPEN, чтобы дополнительные цвета в диалоговом окошке всегда отображались.

Пример:

CHOOSECOLOR cc;                 // структура
static COLORREF acrCustClr[16]; // массив цветов
HWND hwnd;                      // окно - владелец
HBRUSH hbrush;                  // дескриптор кисти
static DWORD rgbCurrent;        // изначальный цвет

// Заполняем CHOOSECOLOR
ZeroMemory(&cc, sizeof(cc));
cc.lStructSize = sizeof(cc);
cc.hwndOwner = hwnd;
cc.lpCustColors = (LPDWORD) acrCustClr;
cc.rgbResult = rgbCurrent;
cc.Flags = CC_FULLOPEN | CC_RGBINIT;

if (ChooseColor(&cc)==TRUE) {
    hbrush = CreateSolidBrush(cc.rgbResult);
    rgbCurrent = cc.rgbResult;
}

Диалог выбора шрифта

Чтобы показать диалого выборя Шрифта, который позволяет пользователю настроить определённые параметры шрифта, сначала необходимо инициализировать структуру CHOOSEFONT, а затем вызвать функцию ChooseFont.

Ниже приведён пример, в котором устанавливается флаг CF_SCREENFONTS, чтобы диалоговое окошко выбора шрифта показывало только экранные шрифты. Чтобы на диалоге были видны элементы управления, позволяющие изменять зачёркнутость, подчёркивание и цвет, необходимо установить флаг CF_EFFECTS.

Если пользователь нажмёт на диалоге кнопку OK, то функция ChooseFont вернёт TRUE, а переменная lpLogFont структуры CHOOSEFONT, будет указывать на струткуру описывающую параметры шрифта, выбранного пользователем. Переменная rgbColors содержит выбранный цвет текста. Далее эта информация используется для того, чтобы задать шрифт и цвет текста для контекста устройства, связанного с родительским окном.

Пример:

HWND hwnd;                // окно - владелец
HDC hdc;                  // контекст устройства окна владельца

CHOOSEFONT cf;            // сама струткура
static LOGFONT lf;        // структура, хранящая параметры шрифта
static DWORD rgbCurrent;   // текущий цвет текста
HFONT hfont, hfontPrev;
DWORD rgbPrev;

// Заполняем CHOOSEFONT
ZeroMemory(&cf, sizeof(cf));
cf.lStructSize = sizeof (cf);
cf.hwndOwner = hwnd;
cf.lpLogFont = &lf;
cf.rgbColors = rgbCurrent;
cf.Flags = CF_SCREENFONTS | CF_EFFECTS;

if (ChooseFont(&cf)==TRUE) {
    hfont = CreateFontIndirect(cf.lpLogFont);
    hfontPrev = SelectObject(hdc, hfont);
    rgbCurrent= cf.rgbColors;
    rgbPrev = SetTextColor(hdc, rgbCurrent);
 .
 .
 .
}

Диалог открытия файла

Чтобы показать пользователю диалоговое окошко выбора файла, сперва необходимо заполнить структуру OPENFILENAME, а затем вызвать функцию GetOpenFileName.

В данном примере, переменная lpstrFilter указывает на буфер, который содержит два фильтра имён файлов, которые пользователь может использовать, чтобы ограничить количество файлов, показанных в диалоге. Буфер оканчивается двумя нулями и содержит массив строк, при этом на каждый фильтр отводится пара строк. Переменная nFilterIndex содержит номер фильтра, который будет использован при создании диалога.

В переменной Flags устанавливаются два флага OFN_PATHMUSTEXIST и OFN_FILEMUSTEXIST. Эти два флага заставляют диалоговое окно проверить на существование пути и имени файла, перед тем как он вернёт управление.

В случае успешного выбора файла, функция GetOpenFileName должна вернёть TRUE. В этом случае, буфер, на который указывает переменная lpstrFile, будет содержать путь и имя файла. Далее эту информацию можно использовать для открытия файла.

Пример:

OPENFILENAME ofn;       // структура диалога открытия файла
char szFile[260];       // буфер для имени файла
HWND hwnd;              // окно - владелец
HANDLE hf;              // дескриптор файла

// Заполняем OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = "All\0*.*\0Text\0*.TXT\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

// Показываем диалог открытия файла.

if (GetOpenFileName(&ofn)==TRUE)
    hf = CreateFile(ofn.lpstrFile, GENERIC_READ,
        0, (LPSECURITY_ATTRIBUTES) NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
        (HANDLE) NULL);

Отображение диалога печати

Чтобы показать пользователю диалоговое окошко печати, позволяющее настроить параметры печати документа, сначала необходимо заполнить структуру PRINTDLG, а затем вызвать функцию PrintDlg.

Для того, чтобы получить контекст устройства выбранного принтера в переменной hDC, необходимо задать флаг PD_RETURNDC в переменной Flags структуры PRINTDLG.

Изначально, переменные hDevMode и hDevNames установлены в NULL. Если функция возвратит TRUE, то эти переменные будут содержать дескрипторы на структуры DEVMODE и DEVNAMES, содержащие информацию о пользовательском вводе, а так же информацию о принтере. Далее, эту информацию можно использовать для отправки данных на выбранный принтер.

Пример:

PRINTDLG pd;
HWND hwnd;

// Заполняем PRINTDLG
ZeroMemory(&pd, sizeof(pd));
pd.lStructSize = sizeof(pd);
pd.hwndOwner   = hwnd;
pd.hDevMode    = NULL; // Не забудьте освободить или сохранить hDevMode
pd.hDevNames   = NULL; // Не забудьте освободить или сохранить hDevNames
pd.Flags       = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC;
pd.nCopies     = 1;
pd.nFromPage   = 0xFFFF;
pd.nToPage     = 0xFFFF;
pd.nMinPage    = 1;
pd.nMaxPage    = 0xFFFF;

if (PrintDlg(&pd)==TRUE)
{
    // Теперь можно что-нибудь вывести на печать.

    // По завершению удаляем DC.
    DeleteDC(pd.hDC);
}

Использование диалогового окна свойств печати

Чтобы отобразить диалог свойств печати, необходимо сначала заполнить структуру PRINTDLGEX, а затем вызвать функцию PrintDlgEx.

Чтобы заставить функцию PrintDlgEx вернуть контекст устройства (в переменной hDC) выбранного принтера, необходимо установить флаг PD_RETURNDC в переменной Flags структуры PRINTDLG.

Изначально, переменные hDevMode и hDevNames установлены в NULL. Если функция возвратит S_OK, то эти переменные будут содержать дескрипторы на структуры DEVMODE и DEVNAMES, содержащие информацию о пользовательском вводе, а так же информацию о принтере. Далее, эту информацию можно использовать для отправки данных на выбранный принтер.

После завершения печати, необходимо освободить память выделенную под DEVMODE и DEVNAMES и удалить контекст устройства.

Пример:

HRESULT DisplayPrintPropertySheet(
    HWND hWnd  // окно, являющееся владельцем диалога свойств печати
)
{
HRESULT hResult;
LPPRINTDLGEX pPDX = NULL;
LPPRINTPAGERANGE pPageRanges = NULL;

// Выделяем память для структуры PRINTDLGEX.

pPDX = (LPPRINTDLGEX)GlobalAlloc(GPTR, sizeof(PRINTDLGEX));
if (!pPDX)
    return E_OUTOFMEMORY;

// Выделяем память для массива структур PRINTPAGERANGE.

pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR,
                   10 * sizeof(PRINTPAGERANGE));
if (!pPageRanges)
    return E_OUTOFMEMORY;

//  Инициализируем структуру PRINTDLGEX.

pPDX->lStructSize = sizeof(PRINTDLGEX);
pPDX->hwndOwner = hWnd;
pPDX->hDevMode = NULL;
pPDX->hDevNames = NULL;
pPDX->hDC = NULL;
pPDX->Flags = PD_RETURNDC | PD_COLLATE;
pPDX->Flags2 = 0;
pPDX->ExclusionFlags = 0;
pPDX->nPageRanges = 0;
pPDX->nMaxPageRanges = 10;
pPDX->lpPageRanges = pPageRanges;
pPDX->nMinPage = 1;
pPDX->nMaxPage = 1000;
pPDX->nCopies = 1;
pPDX->hInstance = 0;
pPDX->lpPrintTemplateName = NULL;
pPDX->lpCallback = NULL;
pPDX->nPropertyPages = 0;
pPDX->lphPropertyPages = NULL;
pPDX->nStartPage = START_PAGE_GENERAL;
pPDX->dwResultAction = 0;

//  Показываем диалоговое окно свойств печати.

hResult = PrintDlgEx(pPDX);

if ( (hResult == S_OK) &&
           pPDX->dwResultAction == PD_RESULT_PRINT) {

    // После того как пользователь нажмёт кнопку "Печать",
    // можно использовать DC и другую информацию, возвращённую в
    // структуре PRINTDLGEX для печати документа

}

if (pPDX->hDC != NULL)
    DeleteDC(pPDX->hDC);
if (pPDX->hDevMode != NULL)
    GlobalFree(pPDX->hDevMode);
if (pPDX->hDevNames != NULL)
    GlobalFree(pPDX->hDevNames);

return hResult;
}

Параметры страницы

Чтобы показать диалоговое окно "Параметры страницы" (Page Setup), которое позволяет пользователю задать параметры печатаемой страницы (такие как тип бумаги, источник бумаги, ориентация и отступы), сперва необходимо запонить структуру PAGESETUPDLG, а затем вызвать функцию PageSetupDlg.

Чтобы указать начальные отступы, необходимо указать флаг PSD_MARGINS в переменной Flags и указать сами отступы в переменной rtMargin. Флаг PSD_INTHOUSANDTHSOFINCHES используется для того, чтобы задать отступы в тысячных дюйма.

Изначально, переменные hDevMode и hDevNames установлены в NULL. Если функция возвратит TRUE, то эти переменные будут содержать дескрипторы на структуры DEVMODE и DEVNAMES, содержащие информацию о пользовательском вводе, а так же информацию о принтере. Далее, эту информацию можно использовать для отправки данных на выбранный принтер.

В примере разрешёно использование процедуры ловушки PagePaintHook, чтобы позволить настроить прорисовку содержимого страницы.

Пример:

PAGESETUPDLG psd;    // структура диалогового окна
HWND hwnd;           // окно - владелец

// Инициализируем PAGESETUPDLG
ZeroMemory(&psd, sizeof(psd));
psd.lStructSize = sizeof(psd);
psd.hwndOwner   = hwnd;
psd.hDevMode    = NULL; //Не забудьте освободить или сохранить hDevMode
psd.hDevNames   = NULL; //Не забудьте освободить или сохранить hDevNames
psd.Flags       = PSD_INTHOUSANDTHSOFINCHES | PSD_MARGINS | 
                  PSD_ENABLEPAGEPAINTHOOK;
psd.rtMargin.top = 1000;
psd.rtMargin.left = 1250;
psd.rtMargin.right = 1250;
psd.rtMargin.bottom = 1000;
psd.lpfnPagePaintHook = PaintHook;

if (PageSetupDlg(&psd)==TRUE) {
    // здесь проверяем размер бумаги и значения отступов
}

Следующий пример демонстрирует использование процедуры ловушки PagePaintHook, которая рисует прямоугольник отступа:

Пример:

BOOL CALLBACK PaintHook(HWND hwndDlg, UINT uMsg, WPARAM wParam,
    LPARAM lParam)
{
    LPRECT lprc;
    COLORREF crMargRect;
    HDC hdc, hdcOld;

    switch (uMsg) {

        // Рисуем прямоугольник отступа.
        case WM_PSD_MARGINRECT:
            hdc = (HDC) wParam;
            lprc = (LPRECT) lParam;

            crMargRect = GetSysColor(COLOR_HIGHLIGHT);

            hdcOld = SelectObject(hdc, CreatePen(PS_DASHDOT, .5,
                crMargRect));

            // Рисуем прямоугольник отступа.
            Rectangle(hdc, lprc->left, lprc->top, lprc->right,
                lprc->bottom);

            SelectObject(hdc, hdcOld);
            return TRUE;

        default:
            return FALSE;
    }
    return TRUE;
}

Диалоговые окошки поиска и замены текста

Чтобы использовать диалоги Поиска или Замены необходимо проделать следующие три операции:

  1. Получить идентификатор для зарегистрированного сообщения FINDMSGSTRING.
  2. Отобразить диалоговое окошко.
  3. Обработать сообщение FINDMSGSTRING когда диалог откроется.

При инициализации приложения можно зарегистрировать и получить идентификатор сообщения FINDMSGSTRING при помощи функции RegisterWindowMessage.

UINT uFindReplaceMsg;  // идентификатор сообщения для FINDMSGSTRING

uFindReplaceMsg = RegisterWindowMessage(FINDMSGSTRING);

Чтобы отобразить диалог поиска или замены текста, сперва необходимо заполнить структуру FINDREPLACE, а затем вызвать функцию FindText или ReplaceText. Обратите внимание, что структура FINDREPLACE и буфер для строки поиска должны быть объявлены глобально или статически, чтобы они не вышли за пределы видимости, до того, как диалоговое окошко будет закрыто. Чтобы указать, какое окно будет получать зарегистрированные сообщения, необходимо присвоить его дескриптор переменной hwndOwner.

Пример:

FINDREPLACE fr;       // структура
HWND hwnd;            // окно - владелец
CHAR szFindWhat[80];  // буфер для строки
HWND hdlg = NULL;     // десткриптор диалога поиска текста

// Заполняем FINDREPLACE
ZeroMemory(&fr, sizeof(fr));
fr.lStructSize = sizeof(fr);
fr.hwndOwner = hwnd;
fr.lpstrFindWhat = szFindWhat;
fr.wFindWhatLen = 80;
fr.Flags = 0;

hdlg = FindText(&fr);

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

Чтобы отслеживать сообщения, посылаемые диалоговым окном, оконная процедура должна проверять зарегистрированное сообщение FINDMSGSTRING и обрабатывать значения, переданные в структуре FINDREPLACE:

Пример:

LPFINDREPLACE lpfr;

if (message == uFindReplaceMsg){

    // Получаем указатель на структуру FINDREPLACE из lParam.

    lpfr = (LPFINDREPLACE)lParam;

    if (lpfr->Flags & FR_DIALOGTERM){ 
        hdlg = NULL; 
        return 0; 
        } 

    // Если установлен флаг FR_FINDNEXT,
    // то вызываем процедуру поиска запрошенной строки

    if (lpfr->Flags & FR_FINDNEXT) 
        SearchFile(lpfr->lpstrFindWhat,
                   (BOOL) (lpfr->Flags & FR_DOWN), 
                   (BOOL) (lpfr->Flags & FR_MATCHCASE)); 

    return 0; 
 
}