Шаг 139 - Стандартные диалоги Открытия и Сохранения файлов

Стандартный диалог открытия(сохранения) файла выглядит следующим образом:

139.gif (8172 b)

Не будет преувеличением сказать, что мы его используем несколько десятков раз на дню. Давайте создадим обычное приложение на базе диалогового окна. Добавим в него кнопку и элемент ListBox. Для элемента ListBox еще опишем переменную типа Control с именем m_List (MFC Class Wizard -> Member Variables -> Add Variable). Я задался целью помучить диалог открытия файла, который ничем принципиально не отличается от диалога сохранения файла, и даже класс для реализации этих диалогов существует один на двоих - CFileDialog.

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

void CTestDlg::OnButton1() 
{
	CFileDialog fd(true);
	if (fd.DoModal()==IDOK) 
	{ 
		m_List.ResetContent();
		m_List.AddString(fd.m_ofn.lpstrFile);
	} 
}

Конструктор класса перегружает все параметры, кроме первого, по умолчанию и поэтому, в принципе, их можно не указывать. Вызовы конструктора в строке

CFileDialog fd(true);

идентичен по смыслу вызову:

CFileDialog fd(true,NULL,NULL, OFN_HIDEREADONLY+
	OFN_OVERWRITEPROMPT,NULL,NULL);

Итак, конструктор класса:

CMyFileDialog(
BOOL bOpenFileDialog, // TRUE - диалог "Открыть", FALSE - диалог "Сохранить как"
LPCTSTR lpszDefExt = NULL, // расширение (по умолчанию)
LPCTSTR lpszFileName = NULL, // имя файла (по умолчанию)
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, //возможные флаги диалога
LPCTSTR lpszFilter = NULL, // фильтр(ы) для вывода определенной группы файлов
CWnd* pParentWnd = NULL // указатель на родителя, вызвавшего диалог
);
После объявления класса запускаем его на выполнение с помощью DoModal. Если была нажата кнопка OK, то элемент типа ListBox сначала очищается (на всякий случай) с помощью ResetContent, а потом в него добавляем выбранный файл вместе с путем с помощью AddString. Вполне естественно возникает вопрос насчет аргумента AddString - fd.m_ofn.lpstrFile. Что такое fd я думаю понятно, а m_ofn является встроенной переменной класса CFileDialog и имеет тип OPENFILENAME (структура из WinAPI). Член структуры (не подумайте чего плохого!) lpstrFile содержит имя выбранного файла с путем. Структура OPENFILENAME содержит в себе около 20 членов. Все параметры конструктора (кроме первого), которые мы задаем при инициализации fd, можно легко изменить с помощью этой встроенной переменной m_ofnOPENFILENAME можно найти в разделе MFC (WinAPI).

На следующем шаге я рассмотрю более продвинутый вариант работы с диалогом Открытия файлов.

Открываем несколько файлов

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

Вот новый код кнопки:

void CTestDlg::OnButton1() 
{
	CWnd* TheWindow=GetActiveWindow();
	CFileDialog fd(true,NULL,NULL,OFN_ALLOWMULTISELECT+
		OFN_HIDEREADONLY,NULL,TheWindow);
	fd.m_ofn.lpstrTitle="My own OpenFile Dialog";
	fd.m_ofn.lpstrFilter="Bitmap files(*.bmp)\0*.bmp\0All files(*.*)\0*.*\0\0";
	fd.m_ofn.nFilterIndex=2;
	fd.m_ofn.lpstrInitialDir="c:";

	if (fd.DoModal()==IDOK)
	{
		m_List.ResetContent();
		m_List.AddString(fd.m_ofn.lpstrFile);
		int i,st; 
		CString file,qq;

		st=fd.m_ofn.nFileOffset;
		qq=fd.m_ofn.lpstrFile[st-1];

		//Checking if one file or more...
		if (qq=="") 	
		{
			file="";
			for (i=st;true;i++)
			{
				qq=fd.m_ofn.lpstrFile[i];
				if (qq=="") 
				{
					m_List.AddString(file);		
					file="";
					qq=fd.m_ofn.lpstrFile[i+1];
					if (qq=="") break;
				}
				else	
					file=file+qq;
			}// for loop
		}//if more than 1 file
	} // if (fd.DoModal==IDOK)
}

А теперь пояснения к приведенному коду.

Получаем указатель на текущее окно с помощью функции GetActiveWindow и помещаем его в переменную TheWindow. Далее инициализируем переменную fd. Я не стал пока особо извращаться и установил второй, третий и пятый параметр как NULL. Первый параметр true говорит о том, что используем диалог открытия файла. Флаги устанавливаем по минимуму. Они указывают, что можно выбирать несколько файлов сразу и элемент "Только для чтения" диалога не будет показан. В последнем параметре вызова конструктора определил указатель на текущее активное окно. Даже если бы мы проставили в последнем параметре NULL, то все равно хозяином диалога стало бы текущее окно приложения. Но так работает MFC. Я же немного побаловался с соответствующими функциями WinAPI. Если не указывал этот параметр, то диалог становился бесхозным.

Далее демонстрирую то, что с помощью встроенной переменной m_ofn (тип OPENFILENAME) можно изменить многие параметры стандартного диалога. Изменяем заголовок диалога (параметр lpstrTitle); описываем 2 вида фильтров (параметр lpstrFilter); указываем, что при создании диалога вызывается второй фильтр(параметр nFilterIndex); указываем начальный путь диалога (параметр lpstrInitialDir). Полное описание структуры OPENFILENAME можно найти в разделе MFC(WinAPI). Далее по тексту еще не раз встретятся параметры из этой структуры (т.е. fd.m_ofn.<параметр>). За разъяснениями обращайтесь в указанный раздел.

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

Смотрим вариант, когда выбрано несколько файлов.

Проблема заключается в том, что путь и имена файлов разделены нулевым ("\0" или "") символом. Это сделано для того, чтобы корректно воспринимать пробелы в именах файлов. Таким образом, напрямую из параметра lpstrFile можно получить только путь к файлам. После пути к файлам стоит нулевой символ, что обычно воспринимается как конец строки. Но мы то знаем, что дальше должны быть перечислены файлы! После последнего имени файла стоит двойной нулевой символ.. Параметр nFileOffset показывает сдвиг имени файла от начала строки. Если предыдущий символ fd.m_ofn.lpstrFile[st-1] имеет нулевое значение, значит последует перечисление нескольких файлов, если же любой другой символ - выбран всего один файл и последующий код не нужен.

Начинаем по порядку просматривать все символы параметра lpstrFile начиная с первого символа перечисления имен файлов (параметр nFileOffset) и до тех пор, пока не найдем двойной нулевой символ. В переменной file сохраняем имя текущего файла. Как только находим нулевой символ, добавляем строку из переменной file в элемент ListBox и обнуляем переменную file. При этом проверяем, если следующий символ имеет значение NULL(нулевой), то прекращаем поиск и выходим из цикла.

Прислал Valeri Khromov.


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем - 9.03.2000