Краткое описание.
Класс CDailogXML является потомком MFC-шного класса CDialog и предназначен для создания диалоговых окон на основе XML-файлов, описывающих как само диалоговое окно, так и расположенные на нем элементы управления. Каждому диалоговому окну соответствует один XML-файл следующей структуры:
<?xml version="1.0" encoding="Windows-1251"?>
<Dialog
Style="0x80C800C0" ExStyle="0x00000000"
Left="0" Top="0" Width="260" Height="90"
Caption="Test Dialog"
FontFace="Tahoma" FontSize="8">
<Controls
Count="8">
<Control
Class="Static"
Style="0x50020202" ExStyle="0x00000000"
Left="4" Top="6" Width="44" Height="12"
Caption="Text edit:"
ID="-1"/>
<Control
Class="Edit"
Style="0x50030080" ExStyle="0x00000200"
Left="50" Top="6" Width="206" Height="12"
Caption=""
ID="1001"/>
...
</Controls>
</Dialog>
Использование класса CDialogXML в приложении аналогично CDialog — необходимо отнаследоваться от него, при необходимости перекрыв в потомке виртуальные методы OnInitDialog() и DoDataExchange(), после чего создать экземпляр объекта на стеке и вызвать метод DoModal():
class CTestDialog: public CDialogXML { ... };
...
void CMainFrame::OnDialogTest(void)
{
CTestDialog dlgTest(this);
if (dlgTest.DoModal() == IDOK) {
// используем введенные пользователем данные
...
}
}
Единственное отличие состоит в том, что вместо создания шаблона диалога в ресурсах приложения необходимо подготовить соответствующий XML-файл.
Средства реализации и библиотеки:
MS Visual C++ 6.0 SP6
MS Palform SDK November 2001
MFC 6.0
Pugnacious XML Parser (автор — Kristen Wegner)
SGI STL 3.3
Демонстрационный проект:
"Ключевые" файлы:
DialogXML.h — интерфейс класса CDialogXML
DialogXML.cpp — реализация класса CDialogXML
PugXML.h — парсер XML
PugXMLplus.h — функции-"обертки" для парсера XML (более строго проверяют результаты чтения численных атрибутов, чем "родные" методы)
StringConv.h — вспомогательные классы и функции, предназначенные для выполнения преобразований CHAR <-> WCHAR <-> TCHAR (с точки зрения реализации являются аналогами своих "собратьев" из ATL/MFC 7.x; могут быть заменены на соответствующие макросы/фукции из <AFXCONV.H> или <ATLCONV.H> любых версий MFC/ATL)
Детали реализации и советы по использованию.
Конструктор класса CDialogXML имеет вид:
CDialogXML(LPCTSTR pszDialogName, CWnd* pParentWnd = NULL);
Через параметр
pszDialogName необходимо передать "имя" диалогового окна, определяющее имя XML-файла, на основании которого необходимо построить данный диалог. Заметим, что это имя
не должно содержать расширения ".xml" и должно быть допустимым именем файла. Потомкам CDialogXML целесообразно передавать имя диалога в вызов конструктора базового класса:
CTestDialog::CTestDialog(CWnd* pParentWnd):
CDialogXML(_T("TestDialog"), pParentWnd)
{
...
}
Построение в памяти шаблона диалгового окна (то есть, инициализацию структур DLGTEMPLATE и DLGITEMTEMPLATE) выполняет публичный
статический метод
static const DLGTEMPLATE* CreateDlgTemplate(LPCTSTR pszFileXML);
Он возвращает адрес области памяти, в которой находится полностью подготовленный шаблон. Через параметр
pszFileXML необходимо передать
полное имя XML-файла, описывающиего диалоговое окно. Если данный файл содержит ошибки не может быть "разобран" парсером, метод возвращает значение NULL.
Поскольку данный метод является статическим, им можно воспользоваться, даже если вы не хотите наследовать свои диалоги от класса CDialogXML:
class CMyDialog: public CDialog
{
public:
CMyDialog(void);
...
};
...
CMyDialog::CMyDialog(void):
CDialog() // вызываем защищенный конструктор по умолчанию
{
...
}
...
void CMainFrame::foo(void)
{
CMyDialog dlgMy;
DLGTEMPLATE* pDlgTemplate = CDialogXML::CreateDlgTemplate(_T("C:\\MyDialog.xml"));
dlgMy.InitModalIndirect(pDlgTemplate);
if (dlgMy.DoModal() == IDOK) {
...
}
}
Путь, по которому экземпляры CDialogXML и его потомков ищут XML-файлы определяется виртуальным методом
virtual void GetXMLpath(CString& strDest);
"По умолчанию" этот путь конструируется следующим образом:
%AppData% \ AfxGetApp()->m_pszRegistryKey \ AfxGetAppName() \
Обратите внимание, что данная реализация GetXMLpath() предполагает, что в методе InitInstance() объекта-приложения был выполнен вызов
SetRegistryKey(_T("VendorName_Or_CompanyName"));
Метод GetXMLpath() может быть перекрыт, например, для реализации поддержки многоязычности в приложении:
class CLocDialogXML: public CDialogXML { ... };
...
void CLocDialogXML::GetXMLpath(CString& strDest)
{
CDialogXML::GetXMLpath(strDest);
strDest += _T(имя_текущего_языка_приложения);
strDest += _T('\\');
}
Реализация перекрытого метода CDialogXML::DoModal() очень проста:
int CDialogXML::DoModal(void)
{
CString strFileXML;
ASSERT(m_lpszTemplateName == NULL);
ASSERT(m_lpDialogTemplate == NULL);
GetXMLpath(strFileXML);
strFileXML += m_strDialogName + _T(".xml");
m_lpDialogTemplate = CreateDlgTemplate(strFileXML);
m_lpDialogInit = NULL;
return (m_lpDialogTemplate != NULL ? CDialog::DoModal() : -1);
}
Заключительные замечания.
Для запуска демонстрационного приложения необходимо создать папку %AppData%\Elijah Zarezky\XMLDialogs и скопировать в нее файл TestDialog.xml из папки XMLDialogs\AppData прилагающегося
архива.
Из STL в данном коде используется std::auto_ptr<>; кроме того, XML парсер использует std::ostream. Я использовал для компиляции несколько модифицированный вариант SGI STL 3.3, исходные тексты которого можно найти
здесь; если при использовании STLport или "родной" STL VC++ у вас возникнут проблемы — comments and suggestions are welcome.
[ posted via RSDN@Home 1.1.4 beta 3 r241, accompanied by silence ]