Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Технология разработки программных систем

..pdf
Скачиваний:
21
Добавлен:
05.02.2023
Размер:
1.31 Mб
Скачать

201

// объект dlg класса CDlg сейчас будет разрушен

}

17.3. Немодальная диалоговая панель

Процедура создания немодальной диалоговой панели незначительно отличается от процедуры создания модальной диалоговой панели. В первую очередь создается шаблон диалоговой панели, затем – класс, например CDlg, управляющий диалоговой панелью. Этот класс также как и для модальной панели наследуется от базового класса CDialog. Обработчики сообщений остаются без изменений, за исключением OnOK и OnCancel.

1. В теле этих функций нужно убрать вызовы одноименных методов базового класса CDialog::OnOK() и CDialog::OnCancel(). Дело в том, что они, в свою очередь, обращаются к методу EndDialog для завершения модального диалога, что недопустимо для немодальной панели. Таким образом, в простейшем случае обработчик OnOK будет иметь вид:

void CDlg::OnOK()

{

CWnd::DestroyWindow();

}

Уточняющее имя класса CWnd в данном случае не требуется. Здесь подчеркнуто то обстоятельство, что метод DestroyWindow размещается в классе CWnd, а не в классе CDialog.

2. Следующее принципиальное отличие относится к схеме использования немодального диалога. Нельзя конструировать объекты класса CDlg на стеке, как это было в случае модального диалога (см. пункт 4.2.3). Вы должны либо использовать динамическое создание объекта, либо, что предпочтительнее, объявить объект m_dlg класса CDlg элементом данных родительского класса, например класса CMainFrame. После этого, метод обработчик OnTest для командного сообщения ID_TEST мог бы иметь вид:

void CMainFrame::OnTest()

{

if( !m_dlg.GetSafeHwnd() ) { m_dlg.Create(m_dlg::IDD, this); m_dlg.ShowWindow(); m_dlg.UpdateWindow();

}

}

Метод Create класса CDialog создает, а метод ShowWindow класса CWnd (заметим, что свойство VISIBLE в шаблоне диалога не устанавливается) отображает окно немодального диалога и немедленно возвращает управление (сравните с методом DoModal). Следовательно, если объект

202

был бы создан на стеке, выход из функции OnTest разрушил бы этот объект, тогда как окно только появилось на экране. Такая ситуация, когда есть окно, но нет ассоциированного с ним объекта, в MFC не допустима.

Здесь же показан простой, но эффективный прием, который позволяет блокировать повторное создание немодального диалога, если он уже отображен на экране. Метод GetSafeHwnd класса CWnd возвращает дескриптор окна, ассоциированного с объектом m_dlg, либо NULL, если такого окна нет.

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

Первый путь состоит в передаче диалогу указателя на родительский объект, после чего появляется возможность вызова методов родительского объекта в методах немодального диалога. Однако здесь требуется приведение указателя, поскольку диалог получает указатель на объект класса CWnd, а не CMainFrame, как в нашем случае.

Обычно используется более универсальная технология, когда родительскому объекту посылается пользовательское сообщение при нажатии в диалоге кнопки OK или Cancel.

#define um_FROM_DIALOG WM_USER+144

void CDlg::OnOK()

{

UpdateData(TRUE); GetParent()->PostMessage(um_FROM_DIALOG, IDOK);

}

Родительское окно уведомляется о том, что пользователь закончил работу с диалогом, поскольку в своей таблице сообщений имеет макрокоманду (см. пункт 3.3.5)

ON_MESSAGE(um_FROM_DIALOG, FromDialog)

и обработчик сообщения um_FROM_DIALOG:

LONG CMainFrame::FromDialog(WPARAM wParam, LPARAM lParam)

{

//имеем обновленные данные из диалога

//m_dlg.m_szText

//закрываем диалог

m_dlg.DestroyWindow(); return 0;

}

203

Как легко заметить, в этом подходе родительский объект и создает, и разрушает окно немодального диалога.

204

18. Поддержка MFC новых элементов управления

MFC поддерживает все элементы управления, пришедшие из Win16,

через такие классы, как CButton, CComboBox, CEdit, CListBox и CStatic. Мы довольно подробно разбирали особенности работы с этими элементами в работе [6]. В MFC учитывается, что все перечисленные элементы управления “общаются” с родительским окном посредством посылки сообщения WM_COMMAND с соответствующим нотификациянным кодом. Эти элементы по-прежнему имеют ряд жестких ограничений, унаследованных из Win16. Так, элемент управления EditBox, и соответственно класс CEdit, не позволяют разместить в данном элементе текст, превосходящий 32К для Windows 95/98/ME. Вы по-прежнему можете использовать эти элементы, однако в арсенале Win32 появились новые органы управления, а MFC осуществляет их полную поддержку. Заметим, что все элементы являются окнами и, следовательно, наследуются от CWnd класса.

Элемент

Краткое описание

управления

 

 

 

CRichEditCtrl

расширяет возможности Edit

CListCtrl

расширяет возможности ListBox

CTreeCtrl

окно для отображения иерархической структуры

CImageList

массив изображений icon или bitmap; обычно

 

используется совместно с CListCtrl и

 

CTreeCtrl

CHeaderCtrl

заголовок изменяемого размера; может иметь вид

 

кнопок; используется совместно с CListCtrl

CSpinButtonCtrl

наборный счетчик; используется совместно с

 

CEdit

CSliderCtrl

ползунок для выбора фиксированных значений

 

параметра

CToolTipCtrl

окно подсказки

CProgressCtrl

окно-индикатор для визуализации протекания

 

длительных процессов

CAnimateCtrl

“проигрывает” в своем окне файл AVI формата

Все новые элементы управления посылают родительскому окну уведомляющее сообщение WM_NOTIFY. В общем случае WPARAM данного сообщения является идентификатором элемента управления, а LPARAM – это указатель на структуру NMHDR, которая имеет следующие поля:

205

HWND hwndFrom;

дескриптор окна

UINT

idFrom;

идентификатор

UINT

code;

код уведомления

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

Работа с элементами управления CSliderCtrl – ползунок для выбора фиксированных значений параметра; CToolTipCtrl – окно подсказки по месту; CProgressCtrl – индикатор протекания длительных процессов и CAnimateCtrl – “проигрыватель” AVI файлов не вызывает особых затруднений, поэтому они выносится на самостоятельное изучение.

Ознакомление с остальными элементами управления будет проводиться на примерах решения практических задач.

18.1. Элемент CRichEditCtrl

Этот элемент управления – весьма мощное средство, появившееся в арсенале разработчика с приходом Win32. RichEdit позволяет проводить форматирование текста в широком диапазоне, отслеживает местоположение строк-ссылок, выделяя их цветом и меняя форму курсора и проч. В качестве недостатков следует отметить не всегда корректную вставку текста из буфера обмена под Windows 2000. Видимо, это связано с проблемами UNICOD’а для национальных шрифтов и будет исправлено в следующих версиях Windows.

18.1.1. Создание элемента RichEdit

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

DWORD dwStyle = ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_MULTILINE |

WS_CHILD | WS_VISIBLE |

WS_VSCROLL | WS_HSCROLL;

m_rich.Create(dwStyle, rc, pParentWnd, nID);

Остальные параметры не вызывают затруднений и соответственно равны: rc – прямоугольная область, в которой создается элемент управления; pParentWnd – указатель на родительский объект; nID – идентифика-

тор RichEdit.

206

Установка свойства “только для чтения” для RichEdit выполняется динамически вызовом метода SetReadOnly(fReadOnly). Если параметр fReadOnly принимает значение TRUE, RichEdit переводится в режим, когда редактирование текста становится невозможным.

18.1.2. Загрузка текста в RichEdit

Загрузка текста в элемент управления может выполняться несколькими способами. Здесь мы рассмотрим простейший путь, аналогичный тому, который применяется для EditBox.

В первую очередь следует установить максимально возможную длину текста, который может быть загружен в элемент управления. Эта величина равна максимально возможному значению числа типа long за вычетом длины одного параграфа:

m_rich.LimitText(0xFFFFFFFF/2-16);

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

//пусть len - длина текстового файла

CString tt;

LPTSTR temp = tt.GetBuffer(len);

//читаем len байт из файла f.Read(temp, len);

//помещаем текст в RichEdit

m_rich.SetWindowText(temp);

Недостаток данного подхода состоит в том, что метод SetWindowText класса CWnd имеет внутреннее ограничение: он не может обработать текст, если его длина превышает 64К. В этом случае можно воспользоваться методом StreamIn класса CRichEditCtrl, который осуществляет потоковую загрузку текста произвольной длины.

18.1.3. Форматирование текста

Форматирование текста в RichEdit – это новая возможность в сравнении с элементом EditBox. Непосредственно форматирование выполняется вызовом метода SetDefaultCharFormat класса CRichEditCtrl, од-

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

CHARFORMAT cf;

//устанавливаем длину структуры

//и необходимые маски

cf.cbSize = sizeof(cf);

207

cf.dwMask = CFM_CHARSET | CFM_SIZE | CFM_FACE;

//устанавливаем высоту шрифта cf.yHeight = 10*20;

//используем только русские шрифты cf.bCharSet = RUSSIAN_CHARSET;

//указываем предпочтительный шрифт lstrcpy(cf.szFaceName, "Courier New");

m_rich.SetDefaultCharFormat(cf);

Единицей измерения для элемента RichEdit является twips, поэтому все величины при форматировании задаются именно в этих единицах.

18.1.4. Форматирование параграфов

Форматирование параграфов текста выполняется с помощью метода SetParaFormat класса CRichEditCtrl и позволяет изменять ряд характеристик параграфа, например, величину полей, выравнивание текста и проч. Для упрощения работы с отдельными полями структуры форматирования, можно предложить следующий прием:

//получаем текущие параметры форматирования

PARAFORMAT pf = {0}; pf.cbSize = sizeof(pf); m_rich.GetParaFormat(pf);

//изменяем только нужные параметры pf.dwMask = PFM_TABSTOPS; pf.cTabCount = 10;

m_rich.SetParaFormat(pf);

18.1.5. Сохранение в файле текста

Сохранение в файле текста из элемента RichEdit может базироваться на нескольких подходах.

Вначале воспользуемся простейшим методом:

//определяем полную длину текста в RichEdit int len = m_rich.GetTextLength();

//выделяем буфер требуемой длины

CString tt;

LPTSTR temp = tt.GetBuffer(len+1); // получаем текст из RichEdit m_rich.GetWindowText(temp, len+1); // сохраняем len байт в файле f.Write(temp, len);

Недостаток данного метода заключается в том, что функция GetWindowText класса CWnd также не может обработать текст, если его длина превышает 64К.

Второй подход лишен указанного недостатка:

208

//выделяем весь текст в RichEdit m_rich.SetSel(0, -1);

//и получаем его

CString tt = m_rich.GetSelText();

//снимаем выделение m_rich.SetSel(0, 0);

//находим длину текста len = tt.GetLength();

//сохраняем len байт в файле f.Write(tt, len);

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

Если вас, как разработчика, это не устраивает, следует воспользоваться методом StreamOut класса CRichEditCtrl, который осуществляет потоковую выгрузку текста произвольной длины из данного элемента управления.

18.2. Элемент управления CListCtrl

По всей видимости, данный элемент управления является самым распространенным для Windows платформ. Вы имеете дело с ListCtrl, когда открываете “Панель управления” или “Мой компьютер”, “Windows проводник” или любимый вами ReGet и так далее, и так далее… Смело можно утверждать, что ни одно серьезное приложение не обходится без этого элемента управления. К недостаткам следует отнести то обстоятельство, что заполнение элемента большими объемами информации, например, 1000 и более строк, может выполняться весьма долго, даже на мощных компьютерах. В данном случае сортировка и поиск работают также медленно. Видимо это подтолкнуло Windows разработчиков на создание так называемого виртуального списка, который появился в последних версиях Windows (точнее – после установки IExplorer версии 5 и выше). Идея состоит в том, что вы только указываете количество строк в списке, например, 100000, а ListCtrl уведомляет родительское окно, например диалог, о том, какая порция строк требует перерисовки в каждый момент времени. Конечно, все это требует от вас дополнительных затрат времени на разработку, однако с использованием виртуального списка ваше приложение выглядит более профессионально, когда конечный пользователь работает с большими объемами информации.

18.2.1. Предварительные замечания

Элемент управления ListCtrl может находиться в одном из четырех режимов отображения:

1.Многоколоночный список. В этом режиме список имеет заголовок. Его первая колонка индексируется нулем и именуется item. Все остальные

209

колонки именуются subitem и индексируются 1,2,…. Каждая строка списка однозначно определяется уникальным значением long типа, которое обозначается param.

2.Набор крупных изображений размером 32 32 пикселя. Каждый элемент списка имеет подпись, а его расположение на поверхности родительского окна произвольно. В этом и последующих режимах вы можете отобразить только текст, содержащийся в item, но не в subitem.

3.Набор изображений размером 16 16 пикселей. Каждый элемент списка имеет подпись. Обычно пункты выровнены по верхней границе окна, но их расположение на поверхности родительского окна также может быть произвольно.

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

18.2.2. Создание элемента ListCtrl

Создание элемента управления ListCtrl осуществляется вызовом метода Create класса CListCtrl. Однако перед этим вы должны выбрать желаемый режим отображения и указать его в стиле окна. Также в стиле создаваемого окна задаются дополнительные параметры, которые зависят от выбранного режима:

Стиль

Описание

Режим

 

 

 

LVS_REPORT

Многоколоночный список

1

LVS_ICON

Набор крупных icon с подписью

2

LVS_SMALLICON

Набор мелких icon с подписью

3

LVS_LIST

Набор мелких icon с подписью

4

LVS_EDITLABELS

Позволяет редактировать item или

1,2,3,4

LVS_SINGLESEL

подпись

 

Позволяет выбирать только один

1,2,3,4

LVS_SHOWSELALWAYS

пункт списка

 

Выбранный пункт показан всегда

1,2,3,4

LVS_NOLABELWRAP

Блокирует перенос слов в подписи

2

LVS_ALIGNLEFT

Устанавливает выравнивание

2,3

LVS_ALIGNTOP

Устанавливает выравнивание

2,3

LVS_AUTOARRANGE

Устанавливает автовыравнивание

2,3

LVS_NOSORTHEADER

Заголовок не имеет кнопочного

1

LVS_NOCOLUMNHEADER

стиля

 

Список не имеет заголовка

1

LVS_SORTASCENDING

LVS_SORTDESCENDING

 

210

Устанавливает сортировку по воз-

1,2,3,4

растанию

 

Устанавливает сортировку по убы-

1,2,3,4

ванию

 

18.2.3. Инициализация списка

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

ка в режиме LVS_REPORT.

В общем случае вам потребуется элемент данных m_Images класса CImageList, который является контейнером для изображений размером 16 16 пикселей. Эти изображения размещаются в поле item, в левой его части. Заметим, что этот шаг инициализации не является обязательным, если вы не планируете использовать графические данные в списке.

1. В графическом редакторе нужно создать BMP файл с изображениями размером 16 16 пикселей. Для определенности будем считать, что этот ресурс имеет идентификатор IDB_IMAGELIST и содержит 8 изображений. После этого необходимо создать вспомогательный элемент-массив изображений m_Images класса CImageList и прикрепить его к списку:

// создаем массив изображений

COLORREF clr = ::GetSysColor(COLOR_WINDOW); m_Images.Create(IDB_IMAGELIST, 16, 8, clr); m_Images.SetBkColor(clr);

// прикрепляем массив к списку m_List.SetImageList(&m_Images, LVSIL_SMALL);

2. Затем нужно создать заголовок списка, указав требуемое количество колонок. Хотя впоследствии вы можете изменить это количество, добавляя новые колонки или удаляя существующие, но лучше это сделать на этапе инициализации. В нашем случае список будет содержать две колонки:

// добавляем первую колонку

CString str = _T(“Первая колонка”); int col = 0;

//рассчитываем ширину колонки в пикселях int pixW = m_List.GetStringWidth(str);

//указываем выравнивание для всей колонки int alig = LVCFMT_RIGHT;

//добавляем колонку

m_List.InsertColumn(col++, str, alig, pixW);

//задаем параметры второй колонки str = _T(“Вторая колонка”);

//и добавляем ее в список

m_List.InsertColumn(col++, str, alig, pixW);

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]